├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── Elf.lean ├── Elf ├── Header.lean ├── Main.lean ├── Program.lean ├── Section.lean └── Types.lean ├── LICENSE ├── R0sy.lean ├── R0sy ├── ByteDeserial.lean ├── Data │ ├── Bits.lean │ └── Hex.lean ├── Hash.lean ├── Hash │ └── Sha2.lean ├── Lean │ ├── ByteArray.lean │ ├── Nat.lean │ ├── Subarray.lean │ ├── UInt32.lean │ └── UInt64.lean └── Serial.lean ├── README.md ├── RiscV.lean ├── RiscV ├── Config.lean ├── Elf.lean ├── ISA.lean ├── Instr │ ├── ISA.lean │ ├── RV32I.lean │ ├── RV32M.lean │ └── Types.lean ├── Mach │ ├── Exception.lean │ ├── Int.lean │ ├── Mem.lean │ ├── Reg.lean │ └── XlenInt.lean └── Monad.lean ├── Soundness ├── Fri.lean ├── Merkle.lean └── ProofSystem.lean ├── Zkvm.lean ├── Zkvm ├── Algebra │ ├── BabyBear.lean │ ├── Classes.lean │ └── Ntt.lean ├── ArithVM │ ├── AST.lean │ ├── Circuit.lean │ └── Taps.lean ├── Constants.lean ├── MainEmu.lean ├── MainVerify.lean ├── MethodId.lean ├── Platform │ ├── Elf.lean │ └── Mem.lean ├── Seal │ ├── CheckCommitments.lean │ ├── Fri.lean │ ├── Header.lean │ └── TraceCommitments.lean ├── Verify.lean └── Verify │ ├── Error.lean │ ├── Merkle.lean │ └── ReadIop.lean ├── lake-manifest.json ├── lakefile.lean ├── lean-toolchain └── rust ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── circuit ├── Cargo.toml └── src │ ├── disk.rs │ └── main.rs ├── hello_world ├── Cargo.toml ├── guard │ ├── Cargo.toml │ ├── build.rs │ ├── guest │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── bin │ │ │ └── hello_world.rs │ └── src │ │ └── lib.rs └── src │ ├── disk.rs │ ├── lean.rs │ └── main.rs ├── hw ├── Cargo.toml ├── guard │ ├── Cargo.toml │ ├── build.rs │ ├── guest │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── bin │ │ │ └── hw.rs │ └── src │ │ └── lib.rs └── src │ ├── disk.rs │ ├── lean.rs │ └── main.rs ├── output ├── hello_world.bin ├── hello_world.id ├── hello_world.journal ├── hello_world.seal ├── hw.bin ├── hw.id ├── hw.journal ├── hw.seal └── riscv.circuit ├── rust-toolchain.toml └── rustfmt.toml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Build and run all the samples. 2 | name: CI 3 | 4 | # Controls when the workflow will run 5 | on: 6 | # Triggers the workflow on push or pull request events but only for the "main" branch 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | # and a nightly run to make sure samples still build on the latest Lean nightly 12 | # schedule: 13 | # - cron: '0 15 * * *' # 3PM CET/8AM PT 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "build" 18 | build: 19 | strategy: 20 | matrix: 21 | include: 22 | - name: Linux 23 | os: ubuntu-latest 24 | - name: Windows 25 | os: windows-latest 26 | - name: macOS 27 | os: macos-latest 28 | 29 | name: ${{ matrix.name }} 30 | # The type of runner that the job will run on 31 | runs-on: ${{ matrix.os }} 32 | 33 | # Steps represent a sequence of tasks that will be executed as part of the job 34 | steps: 35 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 36 | - uses: actions/checkout@v3 37 | 38 | - name: Setup elan toolchain on Linux or macOS 39 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' 40 | run: | 41 | curl -O --location https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh 42 | chmod u+x elan-init.sh 43 | ./elan-init.sh -y --default-toolchain leanprover/lean4:nightly 44 | echo "Adding location $HOME/.elan/bin to PATH..." 45 | echo "$HOME/.elan/bin" >> $GITHUB_PATH 46 | 47 | - name: Setup elan toolchain on Windows 48 | if: matrix.os == 'windows-latest' 49 | shell: pwsh 50 | run: | 51 | curl -O --location https://raw.githubusercontent.com/leanprover/elan/master/elan-init.ps1 52 | .\elan-init.ps1 -NoPrompt 1 -DefaultToolchain leanprover/lean4:nightly 53 | echo "Adding location $HOME\.elan\bin to PATH..." 54 | echo "$HOME\.elan\bin" >> $env:GITHUB_PATH 55 | 56 | - name: Test elan & lean are working 57 | run: | 58 | elan --version 59 | lean --version 60 | 61 | - name: Build everything 62 | run: | 63 | lake build 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /lake-packages 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/*.ilean": true, 4 | "lake-packages/**": true 5 | } 6 | } -------------------------------------------------------------------------------- /Elf.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Elf.Header 19 | import Elf.Section 20 | import Elf.Program 21 | import Elf.Types 22 | 23 | /-! 24 | # Executable and Linkable Format 25 | 26 | The Executable and Linkable Format (ELF). 27 | -/ 28 | 29 | 30 | structure Elf where 31 | e_header: Elf.Header.Header 32 | programs: Array (Elf.Program.Program e_header.e_ident.ei_class) 33 | sections: Array (Elf.Section.Section e_header.e_ident.ei_class) 34 | 35 | namespace Elf 36 | open R0sy.ByteDeserial 37 | open Elf.Header 38 | open Elf.Section 39 | open Elf.Program 40 | open Elf.Types 41 | 42 | def parse [Monad M] [MonadByteReader M]: M Elf 43 | := do let e_header <- Header.parse 44 | let ptrSize := e_header.e_ident.ei_class 45 | let endianness := e_header.e_ident.ei_data 46 | let programs 47 | <- do let num_programs := e_header.e_phnum.toNat 48 | let mut programs := Array.mkEmpty num_programs 49 | for i in [0:num_programs] do 50 | let start := e_header.e_phoff.toNat + i * e_header.e_phentsize.toNat 51 | let stop := start + e_header.e_phentsize.toNat 52 | let program <- MonadByteReader.onSubarray start stop (Program.parse ptrSize endianness) 53 | programs := programs.push program 54 | pure programs 55 | let sections 56 | <- do let num_sections := e_header.e_shnum.toNat 57 | let mut sections := Array.mkEmpty num_sections 58 | for i in [0:num_sections] do 59 | let start := e_header.e_shoff.toNat + i * e_header.e_shentsize.toNat 60 | let stop := start + e_header.e_shentsize.toNat 61 | let sec <- MonadByteReader.onSubarray start stop (Section.parse ptrSize endianness) 62 | sections := sections.push sec 63 | pure sections 64 | pure { 65 | e_header, 66 | programs, 67 | sections 68 | } 69 | 70 | def ofFile (filename: System.FilePath): IO (Except ByteReaderError Elf) 71 | := do let meta <- filename.metadata 72 | let byteSize := meta.byteSize 73 | let handle <- IO.FS.Handle.mk filename IO.FS.Mode.read 74 | let bin <- handle.read (byteSize.toNat.toUSize) 75 | pure <| ByteReader.run Elf.parse bin.data.toSubarray 76 | end Elf 77 | 78 | -------------------------------------------------------------------------------- /Elf/Header.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Elf.Types 19 | 20 | namespace Elf.Header 21 | 22 | open R0sy.ByteDeserial 23 | open Elf.Types 24 | 25 | structure EIdent where 26 | ei_class: PtrSize 27 | ei_data: Endianness 28 | ei_version: UInt8 29 | ei_osabi: UInt8 30 | ei_abiversion: UInt8 31 | 32 | namespace EIdent 33 | def parse [Monad M] [MonadByteReader M]: M EIdent 34 | := do if (<- MonadByteReader.readUInt8) != 0x7f then throw .InvalidData 35 | if (<- MonadByteReader.readUInt8) != 0x45 then throw .InvalidData 36 | if (<- MonadByteReader.readUInt8) != 0x4c then throw .InvalidData 37 | if (<- MonadByteReader.readUInt8) != 0x46 then throw .InvalidData 38 | let ei_class_raw <- MonadByteReader.readUInt8 39 | let ei_class <- 40 | match ei_class_raw with 41 | | 1 => pure .Ptr32 42 | | 2 => pure .Ptr64 43 | | _ => do panic! s!"ei_class_raw: {ei_class_raw}" 44 | throw .InvalidData 45 | let ei_data_raw <- MonadByteReader.readUInt8 46 | let ei_data <- 47 | match ei_data_raw with 48 | | 1 => pure .Little 49 | | 2 => pure .Big 50 | | _ => do panic! s!"ei_data_raw: {ei_data_raw}" 51 | throw .InvalidData 52 | let ei_version <- MonadByteReader.readUInt8 53 | let ei_osabi <- MonadByteReader.readUInt8 54 | let ei_abiversion <- MonadByteReader.readUInt8 55 | for _ in [0:7] do 56 | let _ <- MonadByteReader.readUInt8 57 | pure { 58 | ei_class, 59 | ei_data, 60 | ei_version, 61 | ei_osabi, 62 | ei_abiversion, 63 | } 64 | end EIdent 65 | 66 | structure Header where 67 | e_ident: EIdent 68 | e_type: UInt16 69 | e_machine: UInt16 70 | e_version: UInt32 71 | e_entry: Ptr e_ident.ei_class 72 | e_phoff: Ptr e_ident.ei_class 73 | e_shoff: Ptr e_ident.ei_class 74 | e_flags: UInt32 75 | e_ehsize: UInt16 76 | e_phentsize: UInt16 77 | e_phnum: UInt16 78 | e_shentsize: UInt16 79 | e_shnum: UInt16 80 | e_shstrndx: UInt16 81 | 82 | namespace Header 83 | def parse [Monad M] [MonadByteReader M]: M Header 84 | := do let e_ident <- EIdent.parse 85 | let ptrSize := e_ident.ei_class 86 | let endianness := e_ident.ei_data 87 | let e_type <- parseUInt16 endianness 88 | let e_machine <- parseUInt16 endianness 89 | let e_version <- parseUInt32 endianness 90 | let e_entry <- parsePtr ptrSize endianness 91 | let e_phoff <- parsePtr ptrSize endianness 92 | let e_shoff <- parsePtr ptrSize endianness 93 | let e_flags <- parseUInt32 endianness 94 | let e_ehsize <- parseUInt16 endianness 95 | let e_phentsize <- parseUInt16 endianness 96 | let e_phnum <- parseUInt16 endianness 97 | let e_shentsize <- parseUInt16 endianness 98 | let e_shnum <- parseUInt16 endianness 99 | let e_shstrndx <- parseUInt16 endianness 100 | pure { 101 | e_ident, 102 | e_type, 103 | e_machine, 104 | e_version, 105 | e_entry, 106 | e_phoff, 107 | e_shoff, 108 | e_flags, 109 | e_ehsize, 110 | e_phentsize, 111 | e_phnum, 112 | e_shentsize, 113 | e_shnum, 114 | e_shstrndx 115 | } 116 | end Header 117 | 118 | end Elf.Header 119 | -------------------------------------------------------------------------------- /Elf/Main.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Elf 19 | 20 | def dump_elf (elf: Elf): IO Unit 21 | := do IO.println s!"PC: {elf.e_header.e_entry.toNat}" 22 | IO.println s!"Program headers: {elf.programs.size}" 23 | for program in elf.programs do 24 | IO.println s!" vaddr: {program.header.p_vaddr.toNat} paddr: {program.header.p_paddr.toNat} filesz: {program.header.p_filesz.toNat} memsz: {program.header.p_memsz.toNat}" 25 | IO.println s!"Section headers: {elf.sections.size}" 26 | for sec in elf.sections do 27 | IO.println s!" addr: {sec.header.sh_addr.toNat} size: {sec.header.sh_size.toNat}" 28 | 29 | def main : IO Unit 30 | := do let filename := "rust/output/hw.bin" 31 | let result <- Elf.ofFile filename 32 | match result with 33 | | Except.ok elf => dump_elf elf 34 | | Except.error error => IO.println s!"ERROR: {error}" 35 | -------------------------------------------------------------------------------- /Elf/Program.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Elf.Types 19 | 20 | namespace Elf.Program 21 | 22 | open R0sy.ByteDeserial 23 | open Elf.Types 24 | 25 | inductive SegmentType where 26 | | PT_NULL 27 | | PT_LOAD 28 | | PT_DYNAMIC 29 | | PT_INTERP 30 | | PT_NOTE 31 | | PT_SHLIB 32 | | PT_PHDR 33 | | PT_TLS 34 | | PT_RESERVED_OS (p_type: UInt32) 35 | | PT_RESERVED_PROC (p_type: UInt32) 36 | deriving BEq 37 | 38 | 39 | def SegmentType.ofUInt32 [Monad M] [MonadByteReader M] (val: UInt32): M SegmentType 40 | := match val with 41 | | 0x00000000 => pure PT_NULL 42 | | 0x00000001 => pure PT_LOAD 43 | | 0x00000002 => pure PT_DYNAMIC 44 | | 0x00000003 => pure PT_INTERP 45 | | 0x00000004 => pure PT_NOTE 46 | | 0x00000005 => pure PT_SHLIB 47 | | 0x00000006 => pure PT_PHDR 48 | | 0x00000007 => pure PT_TLS 49 | | _ => 50 | if 0x60000000 <= val && val <= 0x6FFFFFFF then pure (.PT_RESERVED_OS val) 51 | else if 0x70000000 <= val && val <= 0x7FFFFFFF then pure (.PT_RESERVED_PROC val) 52 | else do panic! s!"p_type: {val}" 53 | throw .InvalidData 54 | 55 | structure PHeader (ptrSize: PtrSize) where 56 | p_type: SegmentType 57 | p_flags: UInt32 58 | p_offset: Ptr ptrSize 59 | p_vaddr: Ptr ptrSize 60 | p_paddr: Ptr ptrSize 61 | p_filesz: Ptr ptrSize 62 | p_memsz: Ptr ptrSize 63 | p_align: Ptr ptrSize 64 | 65 | namespace PHeader 66 | def parse [Monad M] [MonadByteReader M] (ptrSize: PtrSize) (endianness: Endianness): M (PHeader ptrSize) 67 | := do let mut p_flags := 0 68 | let p_type <- parseUInt32 endianness >>= SegmentType.ofUInt32 69 | if ptrSize == .Ptr64 then p_flags <- parseUInt32 endianness 70 | let p_offset <- parsePtr ptrSize endianness 71 | let p_vaddr <- parsePtr ptrSize endianness 72 | let p_paddr <- parsePtr ptrSize endianness 73 | let p_filesz <- parsePtr ptrSize endianness 74 | let p_memsz <- parsePtr ptrSize endianness 75 | if ptrSize == .Ptr32 then p_flags <- parseUInt32 endianness 76 | let p_align <- parsePtr ptrSize endianness 77 | pure { 78 | p_type, 79 | p_flags, 80 | p_offset, 81 | p_vaddr, 82 | p_paddr, 83 | p_filesz, 84 | p_memsz, 85 | p_align 86 | } 87 | end PHeader 88 | 89 | structure Program (ptrSize: PtrSize) where 90 | header: PHeader ptrSize 91 | file_data: Subarray UInt8 92 | 93 | namespace Program 94 | def parse [Monad M] [MonadByteReader M] (ptrSize: PtrSize) (endianness: Endianness): M (Program ptrSize) 95 | := do let header <- PHeader.parse ptrSize endianness 96 | let start := header.p_offset.toNat 97 | let stop := start + header.p_filesz.toNat 98 | let file_data <- MonadByteReader.getSubarray start stop 99 | pure { 100 | header, 101 | file_data 102 | } 103 | end Program 104 | 105 | end Elf.Program 106 | -------------------------------------------------------------------------------- /Elf/Section.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Elf.Types 19 | 20 | namespace Elf.Section 21 | 22 | open R0sy.ByteDeserial 23 | open Elf.Types 24 | 25 | structure SHeader (ptrSize: PtrSize) where 26 | sh_name: UInt32 27 | sh_type: UInt32 28 | sh_flags: Ptr ptrSize 29 | sh_addr: Ptr ptrSize 30 | sh_offset: Ptr ptrSize 31 | sh_size: Ptr ptrSize 32 | sh_link: UInt32 33 | sh_info: UInt32 34 | sh_addralign: Ptr ptrSize 35 | sh_entsize: Ptr ptrSize 36 | 37 | namespace SHeader 38 | def parse [Monad M] [MonadByteReader M] (ptrSize: PtrSize) (endianness: Endianness): M (SHeader ptrSize) 39 | := do let sh_name <- parseUInt32 endianness 40 | let sh_type <- parseUInt32 endianness 41 | let sh_flags <- parsePtr ptrSize endianness 42 | let sh_addr <- parsePtr ptrSize endianness 43 | let sh_offset <- parsePtr ptrSize endianness 44 | let sh_size <- parsePtr ptrSize endianness 45 | let sh_link <- parseUInt32 endianness 46 | let sh_info <- parseUInt32 endianness 47 | let sh_addralign <- parsePtr ptrSize endianness 48 | let sh_entsize <- parsePtr ptrSize endianness 49 | pure { 50 | sh_name, 51 | sh_type, 52 | sh_flags, 53 | sh_addr, 54 | sh_offset, 55 | sh_size, 56 | sh_link, 57 | sh_info, 58 | sh_addralign, 59 | sh_entsize 60 | } 61 | end SHeader 62 | 63 | structure Section (ptrSize: PtrSize) where 64 | header: SHeader ptrSize 65 | file_data: Subarray UInt8 66 | 67 | namespace Section 68 | def parse [Monad M] [MonadByteReader M] (ptrSize: PtrSize) (endianness: Endianness): M (Section ptrSize) 69 | := do let header <- SHeader.parse ptrSize endianness 70 | let start := header.sh_offset.toNat 71 | let stop := start + header.sh_size.toNat 72 | let file_data <- MonadByteReader.getSubarray start stop 73 | pure { 74 | header, 75 | file_data 76 | } 77 | end Section 78 | 79 | end Elf.Section 80 | -------------------------------------------------------------------------------- /Elf/Types.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace Elf.Types 20 | 21 | open R0sy.ByteDeserial 22 | 23 | inductive PtrSize where 24 | | Ptr32 25 | | Ptr64 26 | deriving BEq 27 | 28 | inductive Endianness where 29 | | Big 30 | | Little 31 | deriving BEq 32 | 33 | def Ptr: PtrSize -> Type 34 | | .Ptr32 => UInt32 35 | | .Ptr64 => UInt64 36 | 37 | def Ptr.toNat: {ptrSize: PtrSize} -> Ptr ptrSize -> Nat 38 | | .Ptr32, (val: UInt32) => val.toNat 39 | | .Ptr64, (val: UInt64) => val.toNat 40 | 41 | def parseUInt16 [Monad M] [MonadByteReader M]: Endianness -> M UInt16 42 | | .Big => MonadByteReader.readUInt16be 43 | | .Little => MonadByteReader.readUInt16le 44 | 45 | def parseUInt32 [Monad M] [MonadByteReader M]: Endianness -> M UInt32 46 | | .Big => MonadByteReader.readUInt32be 47 | | .Little => MonadByteReader.readUInt32le 48 | 49 | def parseUInt64 [Monad M] [MonadByteReader M]: Endianness -> M UInt64 50 | | .Big => MonadByteReader.readUInt64be 51 | | .Little => MonadByteReader.readUInt64le 52 | 53 | def parsePtr [Monad M] [MonadByteReader M]: (ptrSize: PtrSize) -> Endianness -> M (Ptr ptrSize) 54 | | .Ptr32 => parseUInt32 55 | | .Ptr64 => parseUInt64 56 | 57 | end Elf.Types 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /R0sy.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy.ByteDeserial 18 | import R0sy.Data.Bits 19 | import R0sy.Data.Hex 20 | import R0sy.Lean.ByteArray 21 | import R0sy.Lean.Nat 22 | import R0sy.Lean.Subarray 23 | import R0sy.Lean.UInt32 24 | import R0sy.Lean.UInt64 25 | import R0sy.Hash 26 | import R0sy.Hash.Sha2 27 | import R0sy.Serial 28 | 29 | /-! 30 | # R0sy 31 | 32 | Utility library. 33 | -/ 34 | -------------------------------------------------------------------------------- /R0sy/ByteDeserial.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy.Lean.Subarray 18 | 19 | namespace R0sy.ByteDeserial 20 | 21 | open Lean.Subarray 22 | 23 | /- Monad abstraction -/ 24 | 25 | inductive ByteReaderError where 26 | | InvalidData 27 | | OutOfData 28 | 29 | instance : ToString ByteReaderError where 30 | toString error 31 | := match error with 32 | | ByteReaderError.InvalidData => "InvalidData" 33 | | ByteReaderError.OutOfData => "OutOfData" 34 | 35 | class MonadByteReader (M: Type -> Type) 36 | extends MonadExcept ByteReaderError M 37 | where 38 | readBytes: Nat -> M (Subarray UInt8) 39 | onSubarray: (start stop: Nat) -> (f: M X) -> M X 40 | getSubarray: (start stop: Nat) -> M (Subarray UInt8) 41 | 42 | def MonadByteReader.readUInt8 [Monad M] [MonadByteReader M]: M UInt8 43 | := do let result <- MonadByteReader.readBytes 1 44 | let c0 := result[0]! 45 | pure c0 46 | 47 | def MonadByteReader.readUInt16le [Monad M] [MonadByteReader M]: M UInt16 48 | := do let result <- MonadByteReader.readBytes 2 49 | let c0 := result[0]!.toNat 50 | let c1 := result[1]!.toNat 51 | let d := c0 ||| (c1 <<< 8) 52 | pure (UInt16.ofNat d) 53 | 54 | def MonadByteReader.readUInt16be [Monad M] [MonadByteReader M]: M UInt16 55 | := do let result <- MonadByteReader.readBytes 2 56 | let c0 := result[0]!.toNat 57 | let c1 := result[1]!.toNat 58 | let d := c1 ||| (c0 <<< 8) 59 | pure (UInt16.ofNat d) 60 | 61 | def MonadByteReader.readUInt32le [Monad M] [MonadByteReader M]: M UInt32 62 | := do let result <- MonadByteReader.readBytes 4 63 | let c0 := result[0]!.toNat 64 | let c1 := result[1]!.toNat 65 | let c2 := result[2]!.toNat 66 | let c3 := result[3]!.toNat 67 | let d := c0 ||| (c1 <<< 8) ||| (c2 <<< 16) ||| (c3 <<< 24) 68 | pure (UInt32.ofNat d) 69 | 70 | def MonadByteReader.readUInt32be [Monad M] [MonadByteReader M]: M UInt32 71 | := do let result <- MonadByteReader.readBytes 4 72 | let c0 := result[0]!.toNat 73 | let c1 := result[1]!.toNat 74 | let c2 := result[2]!.toNat 75 | let c3 := result[3]!.toNat 76 | let d := c3 ||| (c2 <<< 8) ||| (c1 <<< 16) ||| (c0 <<< 24) 77 | pure (UInt32.ofNat d) 78 | 79 | def MonadByteReader.readUInt64le [Monad M] [MonadByteReader M]: M UInt64 80 | := do let result <- MonadByteReader.readBytes 8 81 | let c0 := result[0]!.toNat 82 | let c1 := result[1]!.toNat 83 | let c2 := result[2]!.toNat 84 | let c3 := result[3]!.toNat 85 | let c4 := result[4]!.toNat 86 | let c5 := result[5]!.toNat 87 | let c6 := result[6]!.toNat 88 | let c7 := result[7]!.toNat 89 | let d := c0 ||| (c1 <<< 8) ||| (c2 <<< 16) ||| (c3 <<< 24) ||| (c4 <<< 32) ||| (c5 <<< 40) ||| (c6 <<< 44) ||| (c7 <<< 52) 90 | pure (UInt64.ofNat d) 91 | 92 | def MonadByteReader.readUInt64be [Monad M] [MonadByteReader M]: M UInt64 93 | := do let result <- MonadByteReader.readBytes 8 94 | let c0 := result[0]!.toNat 95 | let c1 := result[1]!.toNat 96 | let c2 := result[2]!.toNat 97 | let c3 := result[3]!.toNat 98 | let c4 := result[4]!.toNat 99 | let c5 := result[5]!.toNat 100 | let c6 := result[6]!.toNat 101 | let c7 := result[7]!.toNat 102 | let d := c7 ||| (c6 <<< 8) ||| (c5 <<< 16) ||| (c4 <<< 24) ||| (c3 <<< 32) ||| (c2 <<< 40) ||| (c1 <<< 44) ||| (c0 <<< 52) 103 | pure (UInt64.ofNat d) 104 | 105 | def MonadByteReader.readMany [Monad M] [MonadByteReader M] (num: Nat) (readX: M X) (out: Array X := Array.mkEmpty num): M (Array X) 106 | := match num with 107 | | 0 => pure out 108 | | num + 1 109 | => do let x <- readX 110 | MonadByteReader.readMany num readX (out.push x) 111 | 112 | def MonadByteReader.readArray [Monad M] [MonadByteReader M] (readX: M X): M (Array X) 113 | := do let size <- MonadByteReader.readUInt32le 114 | MonadByteReader.readMany size.toNat readX 115 | 116 | def MonadByteReader.readUInt16le_array [Monad M] [MonadByteReader M]: M (Array UInt16) 117 | := MonadByteReader.readArray MonadByteReader.readUInt16le 118 | 119 | def MonadByteReader.readUInt16be_array [Monad M] [MonadByteReader M]: M (Array UInt16) 120 | := MonadByteReader.readArray MonadByteReader.readUInt16be 121 | 122 | def MonadByteReader.readUInt32le_array [Monad M] [MonadByteReader M]: M (Array UInt32) 123 | := MonadByteReader.readArray MonadByteReader.readUInt32le 124 | 125 | def MonadByteReader.readUInt32be_array [Monad M] [MonadByteReader M]: M (Array UInt32) 126 | := MonadByteReader.readArray MonadByteReader.readUInt32be 127 | 128 | def MonadByteReader.readUInt64le_array [Monad M] [MonadByteReader M]: M (Array UInt64) 129 | := MonadByteReader.readArray MonadByteReader.readUInt64le 130 | 131 | def MonadByteReader.readUInt64be_array [Monad M] [MonadByteReader M]: M (Array UInt64) 132 | := MonadByteReader.readArray MonadByteReader.readUInt64be 133 | 134 | 135 | /- Basic instance -/ 136 | 137 | structure ByteReader where 138 | data: Subarray UInt8 139 | 140 | def ByteReader.readBytes [Monad M] [MonadStateOf ByteReader M] [MonadExcept ByteReaderError M] (bytes: Nat): M (Subarray UInt8) 141 | := do let self <- get 142 | if self.data.size < bytes then throw ByteReaderError.OutOfData 143 | let (result, data) := Subarray.take self.data bytes 144 | set { self with data } 145 | pure result 146 | 147 | instance [Monad M] [MonadStateOf ByteReader M] [MonadExcept ByteReaderError M] : MonadByteReader M where 148 | readBytes := ByteReader.readBytes 149 | onSubarray {X} (start stop: Nat) (f: M X) 150 | := do let self <- get 151 | let sub: ByteReader := { data := self.data.as.toSubarray start stop } 152 | set sub 153 | let x <- f 154 | set self 155 | pure x 156 | getSubarray (start stop: Nat) 157 | := do let self <- get 158 | pure (self.data.as.toSubarray start stop) 159 | 160 | def ByteReader.run 161 | (f: {M: Type -> Type} -> [Monad M] -> [MonadByteReader M] -> M X) 162 | (buffer: Subarray UInt8) 163 | : Except ByteReaderError X 164 | := 165 | let M: Type -> Type := StateT ByteReader (ExceptT ByteReaderError Id) 166 | ExceptT.run (StateT.run' (@f M _ _) { data := buffer }) 167 | 168 | end R0sy.ByteDeserial 169 | -------------------------------------------------------------------------------- /R0sy/Data/Bits.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Data.Bits 18 | 19 | def Bits.least_upper_bound (hi lo: UInt32): Nat 20 | := 1 <<< (1 + hi.toNat - lo.toNat) 21 | 22 | def Bits.mask (hi lo: UInt32): UInt32 23 | := (Bits.least_upper_bound hi lo - 1).toUInt32 <<< lo 24 | 25 | structure Bits (hi lo: UInt32) where 26 | val: UInt32 -- Fin (Bits.least_upper_bound hi lo) 27 | deriving BEq 28 | 29 | def Bits.ofUInt32 (x: UInt32): Bits hi lo 30 | := { 31 | val := (x >>> lo) &&& (Bits.least_upper_bound hi lo - 1).toUInt32 32 | } 33 | 34 | def Bits.toUInt32 (x: Bits hi lo): UInt32 35 | := x.val <<< lo 36 | 37 | end R0sy.Data.Bits 38 | -------------------------------------------------------------------------------- /R0sy/Data/Hex.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Data.Hex 18 | 19 | def charTable: Array Char 20 | := #[ 21 | '0', '1', '2', '3', 22 | '4', '5', '6', '7', 23 | '8', '9', 'a', 'b', 24 | 'c', 'd', 'e', 'f' 25 | ] 26 | 27 | def UInt8.toHex (val: UInt8): String 28 | := Id.run do 29 | let lo := (val &&& 0x0f).toNat 30 | let hi := (val >>> 4).toNat 31 | let mut out: String := "" 32 | out := out.push charTable[hi]! 33 | out := out.push charTable[lo]! 34 | out 35 | 36 | def UInt16.toHex (val: UInt16): String 37 | := Id.run do 38 | let lo := UInt8.toHex (val &&& 0xff).toNat.toUInt8 39 | let hi := UInt8.toHex (val >>> 8 &&& 0xff).toNat.toUInt8 40 | hi ++ lo 41 | 42 | def UInt32.toHex (val: UInt32): String 43 | := Id.run do 44 | let lo := UInt16.toHex (val &&& 0xffff).toNat.toUInt16 45 | let hi := UInt16.toHex (val >>> 16 &&& 0xffff).toNat.toUInt16 46 | hi ++ lo 47 | 48 | end R0sy.Data.Hex 49 | -------------------------------------------------------------------------------- /R0sy/Hash.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy.Serial 18 | import R0sy.Lean.UInt32 19 | 20 | 21 | namespace R0sy.Hash 22 | 23 | open R0sy.Lean.UInt32 24 | open Serial 25 | 26 | 27 | class Hash (D: Type) 28 | extends 29 | BEq D, 30 | Inhabited D, 31 | SerialUInt32 D 32 | where 33 | hash: ByteArray -> D 34 | hash_words: Subarray UInt32 -> D 35 | hash_pair: D -> D -> D 36 | hash_array_array: Array (Array UInt32) -> D 37 | 38 | def Hash.hash_pod [Hash D] [SerialUInt32 X] (xs : Array X) : D := hash_array_array (Array.map SerialUInt32.toUInt32Words xs) 39 | 40 | class MonadRng (M: Type -> Type) where 41 | nextUInt32: M UInt32 42 | nextUInt64: M UInt64 43 | 44 | class MonadMixRng (D: Type) (M: Type -> Type) where 45 | mix (val: D): M Unit 46 | 47 | end R0sy.Hash 48 | -------------------------------------------------------------------------------- /R0sy/Lean/ByteArray.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy.Lean.UInt32 18 | 19 | namespace R0sy.Lean.ByteArray 20 | 21 | open R0sy.Lean.UInt32 22 | 23 | /- Endian helpers -/ 24 | 25 | partial def ByteArray.to_be32 (x: ByteArray) (i: Nat := 0) (out: Array UInt32 := #[]): Array UInt32 := 26 | if i + 4 <= x.size 27 | then ByteArray.to_be32 x (i + 4) (out.push (UInt32.of_be32 x[i]! x[i+1]! x[i+2]! x[i+3]!)) 28 | else out 29 | 30 | partial def ByteArray.to_le32 (x: ByteArray) (i: Nat := 0) (out: Array UInt32 := #[]): Array UInt32 := 31 | if i + 4 <= x.size 32 | then ByteArray.to_le32 x (i + 4) (out.push (UInt32.of_be32 x[i+3]! x[i+2]! x[i+1]! x[i]!)) 33 | else out 34 | 35 | partial def ByteArray.of_le32 (x: Subarray UInt32) (i: Nat := 0) (out: ByteArray := ByteArray.mkEmpty (x.size * 4)): ByteArray 36 | := if h: i < x.size 37 | then ByteArray.of_le32 x (i + 1) (out ++ UInt32.to_le x[i]) 38 | else out 39 | 40 | #eval (ByteArray.of_le32 #[0xff000001, 0xcc000002].toSubarray).data == #[1, 0, 0, 0xff, 2, 0, 0, 0xcc] 41 | #eval ByteArray.to_le32 (ByteArray.of_le32 #[0xff000001, 0xcc000002].toSubarray) == #[0xff000001, 0xcc000002] 42 | 43 | partial def ByteArray.of_be32 (x: Subarray UInt32) (i: Nat := 0) (out: ByteArray := ByteArray.mkEmpty (x.size * 4)): ByteArray 44 | := if h: i < x.size 45 | then ByteArray.of_be32 x (i + 1) (out ++ UInt32.to_be x[i]) 46 | else out 47 | 48 | end R0sy.Lean.ByteArray 49 | -------------------------------------------------------------------------------- /R0sy/Lean/Nat.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Lean.Nat 18 | 19 | 20 | /- Serialize/deserialize helpers -/ 21 | 22 | def Nat.toUInt32Words (words: Nat) (val: Nat) (out: Array UInt32 := #[]): Array UInt32 23 | := match words with 24 | | 0 => out 25 | | words + 1 => Nat.toUInt32Words words (val >>> 32) (out.push (UInt32.ofNat val)) 26 | 27 | def Nat.fromUInt32Words (x: Subarray UInt32) (i: Nat := 0) (out: Nat := 0): Nat 28 | := if i < x.size 29 | then Nat.fromUInt32Words x (i + 1) ((out <<< 32) ||| UInt32.toNat x[x.size - i - 1]!) 30 | else out 31 | termination_by _ => x.size - i 32 | 33 | 34 | /- Log2 -/ 35 | 36 | partial def Nat.log2_ceil (value: Nat) (result: Nat := 0): Nat 37 | := if (1 <<< result) < value then log2_ceil value (result + 1) else result 38 | 39 | partial def Nat.log2_floor (value: Nat) (result: Nat := 0): Nat 40 | := if (1 <<< (result + 1)) > value then result else log2_floor value (result + 1) 41 | 42 | end R0sy.Lean.Nat 43 | -------------------------------------------------------------------------------- /R0sy/Lean/Subarray.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Lean.Subarray 18 | 19 | def Subarray.take (x: Subarray X) (n: Nat): Subarray X × Subarray X := 20 | let xx := x.as 21 | let l := xx.toSubarray x.start (x.start + n) 22 | let r := xx.toSubarray (x.start + n) (x.stop) 23 | (l, r) 24 | 25 | end R0sy.Lean.Subarray 26 | -------------------------------------------------------------------------------- /R0sy/Lean/UInt32.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Lean.UInt32 18 | 19 | def UInt32.test_bit (bit: Nat) (x: UInt32): Bool 20 | := (1 <<< bit).toUInt32 &&& x != 0 21 | 22 | 23 | /- Endian helpers -/ 24 | 25 | def UInt32.swap_endian (x: UInt32): UInt32 := 26 | let a0 := x &&& 0xff 27 | let a1 := (x >>> (8*1)) &&& 0xff 28 | let a2 := (x >>> (8*2)) &&& 0xff 29 | let a3 := (x >>> (8*3)) &&& 0xff 30 | let c0 := a0 <<< (8*3) 31 | let c1 := a1 <<< (8*2) 32 | let c2 := a2 <<< (8*1) 33 | let c3 := a3 34 | c3 ||| c2 ||| c1 ||| c0 35 | 36 | def UInt32.ror (x: UInt32) (n: Nat): UInt32 := 37 | let l := x >>> UInt32.ofNat n 38 | let r := x <<< UInt32.ofNat (32 - n) 39 | l ||| r 40 | 41 | def UInt32.of_be32 (b3 b2 b1 b0: UInt8): UInt32 := 42 | let c0 := UInt32.ofNat (b0.val.val) 43 | let c1 := UInt32.ofNat (b1.val.val) <<< (8*1) 44 | let c2 := UInt32.ofNat (b2.val.val) <<< (8*2) 45 | let c3 := UInt32.ofNat (b3.val.val) <<< (8*3) 46 | c3 ||| c2 ||| c1 ||| c0 47 | 48 | def UInt32.to_le (x: UInt32): ByteArray := 49 | let a0 := UInt8.ofNat <| UInt32.toNat <| x &&& 0xff 50 | let a1 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*1)) &&& 0xff 51 | let a2 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*2)) &&& 0xff 52 | let a3 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*3)) &&& 0xff 53 | { data := #[ a0, a1, a2, a3 ] } 54 | 55 | def UInt32.to_be (x: UInt32): ByteArray := 56 | let a0 := UInt8.ofNat <| UInt32.toNat <| x &&& 0xff 57 | let a1 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*1)) &&& 0xff 58 | let a2 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*2)) &&& 0xff 59 | let a3 := UInt8.ofNat <| UInt32.toNat <| (x >>> (8*3)) &&& 0xff 60 | { data := #[ a3, a2, a1, a0 ] } 61 | 62 | end R0sy.Lean.UInt32 63 | -------------------------------------------------------------------------------- /R0sy/Lean/UInt64.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Lean.UInt64 18 | 19 | def UInt64.lo (x: UInt64): UInt32 20 | := x.toNat.toUInt32 21 | 22 | def UInt64.hi (x: UInt64): UInt32 23 | := (x >>> 32).toNat.toUInt32 24 | 25 | end R0sy.Lean.UInt64 26 | -------------------------------------------------------------------------------- /R0sy/Serial.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace R0sy.Serial 18 | 19 | class SerialUInt32 (X: Type) where 20 | words: Nat 21 | toUInt32Words: X -> Array UInt32 22 | fromUInt32Words: Subarray UInt32 -> X 23 | 24 | instance : SerialUInt32 UInt32 where 25 | words := 1 26 | toUInt32Words x := #[x] 27 | fromUInt32Words x := x[0]! 28 | 29 | end R0sy.Serial 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # risc0-lean4 2 | 3 | > :warning: **This repository contains research artifacts. It is a work in progress and should not be used for any purpose.** 4 | 5 | *[risc0-lean4](https://github.com/risc0/risc0-lean4)* is a formal model of the [RISC Zero zkVM](https://www.github.com/risc0/risc0), written in the [Lean Theorem Prover](https://leanprover.github.io/) (version **4**). Its long-term goal is to support formal security and soundness proofs for the RISC Zero ecosystem. 6 | 7 | 8 | ## Features 9 | 10 | We have executable models for: 11 | 12 | * [x] ELF file handling 13 | * [x] RV32IM emulation 14 | * [x] SHA2-256 15 | * [x] Merkle tree parsing and inclusion proof verification 16 | * [x] The Baby Bear field (and its degree 4 extension) 17 | * [X] Number Theoretic transform (NTT) 18 | * [x] FRI verification 19 | * [x] Arithmetic circuit parsing and evaluation 20 | * [x] zkVM receipt parsing and verification 21 | 22 | We are currently developing the meta-theory of these systems. 23 | 24 | 25 | ## Repo Organization 26 | - Main verification logic 27 | - Lean version: [Verify.lean](https://github.com/risc0/risc0-lean4/blob/main/Zkvm/Verify.lean) 28 | - Rust version: [verify/mod.rs](https://github.com/risc0/risc0/tree/main/risc0/zkp/src/verify/mod.rs) 29 | - Merkle verification 30 | - Lean version: [Merkle.lean](https://github.com/risc0/risc0-lean4/blob/main/Zkvm/Verify/Merkle.lean) 31 | - Rust version: [verify/merkle.rs](https://github.com/risc0/risc0/blob/main/risc0/zkp/src/verify/merkle.rs) 32 | - FRI verification 33 | - Lean version: [Fri.lean](https://github.com/risc0/risc0-lean4/blob/main/Zkvm/Seal/Fri.lean) 34 | - Rust version: [verify/fri.rs](https://github.com/risc0/risc0/blob/main/risc0/zkp/src/verify/fri.rs) 35 | 36 | 37 | ## Compatibility 38 | 39 | This repository trails behind the [zkVM repository](https://www.github.com/risc0/risc0). Precise version information can be found in [Cargo.lock](https://github.com/risc0/risc0-lean4/blob/main/rust/Cargo.lock). 40 | 41 | 42 | ## Building 43 | 44 | ### Getting Lean4 45 | 46 | See the [Quickstart guide](https://leanprover.github.io/lean4/doc/quickstart.html). 47 | 48 | ### Performing the build 49 | 50 | First, check to see if `lake` is in your terminal's search path. If not, you either haven't installed Lean4 yet, or you haven't `source`'d the `elan` environment variables. 51 | 52 | ```console 53 | risc0-lean4$ source ~/.elan/env 54 | ``` 55 | 56 | Now you can perform the build: 57 | 58 | ```console 59 | risc0-lean4$ lake build 60 | ``` 61 | 62 | ## Running the tools 63 | 64 | This repository includes various executables. These executables are currently used for testing, but will eventually be expanded to provide greater utility. 65 | 66 | ### Receipt verifier 67 | 68 | Verify a receipt generated by the zkVM: 69 | 70 | ```console 71 | risc0-lean4$ ./build/bin/zkvm-verify-lean4 72 | ``` 73 | 74 | ### zkVM emulator 75 | 76 | Run a zkVM guest (without proof): 77 | 78 | ```console 79 | risc0-lean4$ ./build/bin/zkvm-emu-lean4 80 | ``` 81 | 82 | ### Elf dumper 83 | 84 | Read the headers of an ELF binary: 85 | 86 | ```console 87 | risc0-lean4$ ./build/bin/elf-dump-lean4 88 | ``` 89 | 90 | ## Building the docs 91 | 92 | ```console 93 | $ lake -Kdoc=on update 94 | $ lake -Kdoc=on build Zkvm:docs 95 | ``` 96 | 97 | The docs will be viewable at `build/doc/index.html`. 98 | -------------------------------------------------------------------------------- /RiscV.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Config 18 | import RiscV.Elf 19 | import RiscV.Instr.ISA 20 | import RiscV.Instr.RV32I 21 | import RiscV.Instr.RV32M 22 | import RiscV.Instr.Types 23 | import RiscV.ISA 24 | import RiscV.Mach.Exception 25 | import RiscV.Mach.Int 26 | import RiscV.Mach.Mem 27 | import RiscV.Mach.Reg 28 | import RiscV.Mach.XlenInt 29 | import RiscV.Monad 30 | 31 | /-! 32 | # RISC-V 33 | 34 | The RISC-V instruction set architecture. 35 | -/ 36 | -------------------------------------------------------------------------------- /RiscV/Config.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace RiscV.Config 18 | 19 | inductive Xlen: Type where 20 | | Xlen32 21 | | Xlen64 22 | 23 | def Xlen.bits (self: Xlen): Nat 24 | := match self with 25 | | .Xlen32 => 32 26 | | .Xlen64 => 64 27 | 28 | inductive Endian: Type where 29 | | Big 30 | | Little 31 | 32 | end RiscV.Config 33 | -------------------------------------------------------------------------------- /RiscV/Elf.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import Elf 18 | import RiscV.Config 19 | import RiscV.Mach.Mem 20 | import RiscV.Mach.Reg 21 | import RiscV.Monad 22 | 23 | namespace RiscV.Elf 24 | 25 | open R0sy.Lean.ByteArray 26 | open Elf 27 | open RiscV.Config 28 | open RiscV.Mach.Mem 29 | open RiscV.Mach.Reg 30 | open RiscV.Monad 31 | 32 | def xlenOfElf (elf: Elf): Xlen 33 | := match elf.e_header.e_ident.ei_class with 34 | | .Ptr32 => .Xlen32 35 | | .Ptr64 => .Xlen64 36 | 37 | def endianOfElf (elf: Elf): Endian 38 | := match elf.e_header.e_ident.ei_data with 39 | | .Big => .Big 40 | | .Little => .Little 41 | 42 | def loadElf (elf: Elf): MachineState 43 | := Id.run do 44 | let mut blocks: Array Block := Array.mkEmpty 0 45 | for segment in elf.programs do 46 | if segment.header.p_type == .PT_LOAD 47 | then do let zerosRequired := segment.header.p_memsz.toNat - segment.header.p_filesz.toNat 48 | let zeros: Array UInt8 := Array.mkArray zerosRequired 0 49 | let data := segment.file_data.toArray ++ zeros 50 | blocks := blocks.push { 51 | base := segment.header.p_vaddr.toNat, 52 | data 53 | } 54 | pure { 55 | reg_file := RegFile.newWithPc elf.e_header.e_entry.toNat.toUInt32 56 | mem := { endian := endianOfElf elf, blocks } 57 | } 58 | 59 | def loadElfInfo (mach: MachineState) (elf: Elf): Except Mach.Exception.RiscVException MachineState 60 | := Id.run do 61 | mach.run' 62 | <| do let entry := elf.e_header.e_entry.toNat.toUInt32 63 | RegFile.set_word .PC entry 64 | for segment in elf.programs do 65 | if segment.header.p_type == .PT_LOAD 66 | then do let base := segment.header.p_vaddr.toNat 67 | for i in [0:segment.file_data.size] do 68 | Mem.set_byte (base + i) segment.file_data[i]! 69 | MonadMachine.getMachine 70 | 71 | end RiscV.Elf 72 | -------------------------------------------------------------------------------- /RiscV/ISA.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Instr.ISA 18 | import RiscV.Instr.RV32I 19 | import RiscV.Instr.RV32M 20 | import RiscV.Instr.Types 21 | import RiscV.Mach.Mem 22 | import RiscV.Mach.Reg 23 | import RiscV.Monad 24 | 25 | namespace RiscV.ISA 26 | 27 | open RiscV.Instr 28 | open RiscV.Instr.ISA 29 | open RiscV.Monad 30 | 31 | 32 | namespace RV32IM 33 | inductive Instr where 34 | | I (instr: RV32I.Instr) 35 | | M (instr: RV32M.Instr) 36 | 37 | @[always_inline, inline] 38 | def ISA: ISA where 39 | Mnemonic := Instr 40 | all := RV32I.ISA.all.map .I ++ RV32M.ISA.all.map .M 41 | toString 42 | | .I instr => RV32I.ISA.toString instr 43 | | .M instr => RV32M.ISA.toString instr 44 | encode_mnemonic 45 | | .I instr => RV32I.ISA.encode_mnemonic instr 46 | | .M instr => RV32M.ISA.encode_mnemonic instr 47 | run 48 | | .I instr => RV32I.ISA.run instr 49 | | .M instr => RV32M.ISA.run instr 50 | end RV32IM 51 | 52 | end RiscV.ISA 53 | -------------------------------------------------------------------------------- /RiscV/Instr/ISA.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Instr.Types 18 | import RiscV.Mach.Mem 19 | import RiscV.Mach.Reg 20 | import RiscV.Monad 21 | 22 | namespace RiscV.Instr.ISA 23 | 24 | open RiscV.Instr.Types 25 | open RiscV.Mach.Mem 26 | open RiscV.Mach.Reg 27 | open RiscV.Monad 28 | 29 | structure ISA where 30 | Mnemonic: Type 31 | all: Array Mnemonic 32 | toString: Mnemonic -> String 33 | encode_mnemonic (m: Mnemonic): EncMnemonic 34 | run [MonadMachine M] (m: Mnemonic) (args: EncMnemonic.Args (encode_mnemonic m)): M Unit 35 | 36 | namespace ISA 37 | def serialize_mnemonic (isa: ISA) (m: isa.Mnemonic): UInt32 38 | := (isa.encode_mnemonic m).serialize_mnemonic 39 | 40 | def code_matches (isa: ISA) (m: isa.Mnemonic) (x: UInt32): Bool 41 | := let mask := (isa.encode_mnemonic m).mask_mnemonic 42 | x &&& mask == isa.serialize_mnemonic m 43 | 44 | def deserialize_mnemonic (isa: ISA) (x: UInt32): Option isa.Mnemonic 45 | := Id.run do 46 | for mnemonic in isa.all do 47 | if isa.code_matches mnemonic x then return (some mnemonic) 48 | pure none 49 | 50 | def EncArgs (isa: ISA) (m: isa.Mnemonic): Type 51 | := (isa.encode_mnemonic m).EncArgs 52 | 53 | @[always_inline, inline] 54 | def deserialize_args (isa: ISA) (m: isa.Mnemonic) (x: UInt32): isa.EncArgs m 55 | := (isa.encode_mnemonic m).deserialize_args x 56 | 57 | def Args (isa: ISA) (m: isa.Mnemonic): Type 58 | := (isa.encode_mnemonic m).Args 59 | 60 | instance : ToString (Args isa m) where 61 | toString x := EncMnemonic.Args.ToString.toString x 62 | 63 | @[always_inline, inline] 64 | def decode_args (isa: ISA) (m: isa.Mnemonic) (x: isa.EncArgs m): isa.Args m 65 | := EncMnemonic.decode_args x 66 | 67 | def decode_to_string (isa: ISA) (instr: UInt32): Option String 68 | := Id.run do 69 | match isa.deserialize_mnemonic instr with 70 | | none => pure none 71 | | some mnemonic 72 | => do let enc_args := isa.deserialize_args mnemonic instr 73 | let args := isa.decode_args mnemonic enc_args 74 | pure (some s!"{isa.toString mnemonic} {args}") 75 | 76 | @[always_inline, inline] 77 | def step [MonadMachine M] (isa: ISA): M Unit 78 | := do let pc <- RegFile.get_word .PC 79 | let instr <- Mem.get_word pc.toNat 80 | match isa.deserialize_mnemonic instr with 81 | | none => throw (.InvalidInstruction pc.toNat instr.toNat) 82 | | some mnemonic 83 | => do RegFile.set_word .PC (pc + 4) 84 | let enc_args := isa.deserialize_args mnemonic instr 85 | let args := isa.decode_args mnemonic enc_args 86 | isa.run mnemonic args 87 | end ISA 88 | 89 | end RiscV.Instr.ISA 90 | -------------------------------------------------------------------------------- /RiscV/Instr/RV32M.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import RiscV.Instr.ISA 19 | import RiscV.Instr.Types 20 | import RiscV.Mach.Int 21 | import RiscV.Mach.Reg 22 | import RiscV.Monad 23 | 24 | namespace RiscV.Instr.RV32M 25 | 26 | open R0sy.Lean.UInt64 27 | open RiscV.Instr.ISA 28 | open RiscV.Instr.Types 29 | open RiscV.Mach.Int 30 | open RiscV.Mach.Reg 31 | open RiscV.Monad 32 | 33 | /- 34 | Volume I: RISC-V Unprivileged ISA V20191213 35 | RV32M Standard Extension 36 | 37 | funct7 rs2 rs1 funct3 rd opcode R-type 38 | 39 | 0000001 rs2 rs1 000 rd 0110011 MUL 40 | 0000001 rs2 rs1 001 rd 0110011 MULH 41 | 0000001 rs2 rs1 010 rd 0110011 MULHSU 42 | 0000001 rs2 rs1 011 rd 0110011 MULHU 43 | 0000001 rs2 rs1 100 rd 0110011 DIV 44 | 0000001 rs2 rs1 101 rd 0110011 DIVU 45 | 0000001 rs2 rs1 110 rd 0110011 REM 46 | 0000001 rs2 rs1 111 rd 0110011 REMU 47 | -/ 48 | 49 | inductive Instr where 50 | | MUL | MULH | MULHSU | MULHU | DIV | DIVU | REM | REMU 51 | 52 | def ISA: ISA where 53 | Mnemonic := Instr 54 | all := #[ 55 | .MUL, .MULH, .MULHSU, .MULHU, .DIV, .DIVU, .REM, .REMU 56 | ] 57 | toString 58 | | .MUL => "MUL" | .MULH => "MULH" | .MULHSU => "MULHSU" | .MULHU => "MULHU" | .DIV => "DIV" | .DIVU => "DIVU" | .REM => "REM" | .REMU => "REMU" 59 | encode_mnemonic (m: Instr) 60 | := match m with 61 | | .MUL => .R <| R.EncMnemonic.new 0b0000001 0b000 0b0110011 62 | | .MULH => .R <| R.EncMnemonic.new 0b0000001 0b001 0b0110011 63 | | .MULHSU => .R <| R.EncMnemonic.new 0b0000001 0b010 0b0110011 64 | | .MULHU => .R <| R.EncMnemonic.new 0b0000001 0b011 0b0110011 65 | | .DIV => .R <| R.EncMnemonic.new 0b0000001 0b100 0b0110011 66 | | .DIVU => .R <| R.EncMnemonic.new 0b0000001 0b101 0b0110011 67 | | .REM => .R <| R.EncMnemonic.new 0b0000001 0b110 0b0110011 68 | | .REMU => .R <| R.EncMnemonic.new 0b0000001 0b111 0b0110011 69 | run 70 | | .MUL, args 71 | => do let x <- RegFile.get_word args.rs1 72 | let y <- RegFile.get_word args.rs2 73 | RegFile.set_word args.rd (x * y) 74 | | .MULH, args 75 | => do let x <- RegFile.get_word args.rs1 76 | let y <- RegFile.get_word args.rs2 77 | let z := UInt32.extend_signed x * UInt32.extend_signed y 78 | RegFile.set_word args.rd (UInt64.hi z) 79 | | .MULHSU, args 80 | => do let x <- RegFile.get_word args.rs1 81 | let y <- RegFile.get_word args.rs2 82 | let z := UInt32.extend_signed x * UInt32.extend_unsigned y 83 | RegFile.set_word args.rd (UInt64.hi z) 84 | | .MULHU, args 85 | => do let x <- RegFile.get_word args.rs1 86 | let y <- RegFile.get_word args.rs2 87 | let z := UInt32.extend_unsigned x * UInt32.extend_unsigned y 88 | RegFile.set_word args.rd (UInt64.hi z) 89 | | .DIV, args 90 | => do let x <- RegFile.get_word args.rs1 91 | let y <- RegFile.get_word args.rs2 92 | let q := 93 | if x == UInt32.min_signed && y == UInt32.neg_one then x 94 | else if y == 0 then UInt32.neg_one 95 | else 96 | let sgn_x := if UInt32.is_neg x then UInt32.neg_one else 1 97 | let sgn_y := if UInt32.is_neg y then UInt32.neg_one else 1 98 | (sgn_x * sgn_y) * ((x * sgn_x) / (y * sgn_y)) 99 | RegFile.set_word args.rd q 100 | | .DIVU, args 101 | => do let x <- RegFile.get_word args.rs1 102 | let y <- RegFile.get_word args.rs2 103 | let q := 104 | if y == 0 then UInt32.max_unsigned 105 | else x / y 106 | RegFile.set_word args.rd q 107 | | .REM, args 108 | => do let x <- RegFile.get_word args.rs1 109 | let y <- RegFile.get_word args.rs2 110 | let r := 111 | if x == UInt32.min_signed && y == UInt32.neg_one then 0 112 | else if y == 0 then x 113 | else 114 | let sgn_x := if UInt32.is_neg x then UInt32.neg_one else 1 115 | let sgn_y := if UInt32.is_neg y then UInt32.neg_one else 1 116 | (sgn_x * sgn_y) * ((x * sgn_x) % (y * sgn_y)) 117 | RegFile.set_word args.rd r 118 | | .REMU, args 119 | => do let x <- RegFile.get_word args.rs1 120 | let y <- RegFile.get_word args.rs2 121 | let r := 122 | if y == 0 then x 123 | else x % y 124 | RegFile.set_word args.rd r 125 | 126 | end RiscV.Instr.RV32M 127 | -------------------------------------------------------------------------------- /RiscV/Mach/Exception.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace RiscV.Mach.Exception 18 | 19 | inductive RiscVException where 20 | | PtrOutOfBounds (addr: Nat) 21 | | InstructionAddressMisaligned (addr: Nat) 22 | | InvalidInstruction (addr instr: Nat) 23 | | ECall (ecall: Nat) 24 | 25 | instance : ToString RiscVException where 26 | toString 27 | | .PtrOutOfBounds addr => s!"PtrOutOfBounds addr:{addr}" 28 | | .InstructionAddressMisaligned addr => s!"InstructionAddressMisaligned addr:{addr}" 29 | | .InvalidInstruction addr instr => s!"InvalidInstruction addr:{addr} instr:{instr}" 30 | | .ECall addr => s!"ECall addr:{addr}" 31 | 32 | end RiscV.Mach.Exception 33 | -------------------------------------------------------------------------------- /RiscV/Mach/Int.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace RiscV.Mach.Int 20 | 21 | open R0sy.Data.Bits 22 | open R0sy.Lean.UInt32 23 | 24 | def UInt32.is_neg (x: UInt32): Bool 25 | := UInt32.test_bit 31 x 26 | 27 | def UInt32.extend_unsigned (x: UInt32): UInt64 28 | := x.toNat.toUInt64 29 | 30 | def UInt32.extend_signed (x: UInt32): UInt64 31 | := UInt32.extend_unsigned x ||| if UInt32.is_neg x then 0xffffffff00000000 else 0 32 | 33 | def UInt32.neg_one: UInt32 := 0xffffffff 34 | 35 | def UInt32.max_signed: UInt32 := 0x7fffffff 36 | 37 | def UInt32.min_signed: UInt32 := 0x80000000 38 | 39 | def UInt32.max_unsigned: UInt32 := 0xffffffff 40 | 41 | def UInt32.min_unsigned: UInt32 := 0x00000000 42 | 43 | def UInt32.toInt (x: UInt32): Int 44 | := if UInt32.is_neg x then Int.negOfNat (x * neg_one).toNat else x.toNat 45 | 46 | def UInt32.lt_signed (x y: UInt32): Bool 47 | := UInt32.toInt x < UInt32.toInt y 48 | 49 | def UInt32.ge_signed (x y: UInt32): Bool 50 | := UInt32.toInt x >= UInt32.toInt y 51 | 52 | def UInt32.ofUInt8_signed (x: UInt8): UInt32 53 | := Id.run do 54 | let lo: Bits 7 0 := { val := x.toNat.toUInt32 } 55 | let hi: Bits 31 8 := Bits.ofUInt32 <| if UInt32.test_bit 7 lo.val then 0xffffffff else 0 56 | pure (hi.toUInt32 ||| lo.toUInt32) 57 | 58 | def UInt32.ofUInt16_signed (x: UInt16): UInt32 59 | := Id.run do 60 | let lo: Bits 15 0 := { val := x.toNat.toUInt32 } 61 | let hi: Bits 31 16 := Bits.ofUInt32 <| if UInt32.test_bit 15 lo.val then 0xffffffff else 0 62 | pure (hi.toUInt32 ||| lo.toUInt32) 63 | 64 | def UInt32.shr_signed (x y: UInt32): UInt32 65 | := Id.run do 66 | let lo := x >>> y 67 | let hi := if UInt32.is_neg x then ((1 <<< y) - 1) <<< (32 - y) else 0 68 | pure (hi ||| lo) 69 | 70 | end RiscV.Mach.Int 71 | -------------------------------------------------------------------------------- /RiscV/Mach/Mem.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Config 18 | import RiscV.Mach.Exception 19 | import RiscV.Mach.XlenInt 20 | 21 | namespace RiscV.Mach.Mem 22 | 23 | open RiscV.Config 24 | open RiscV.Mach.Exception 25 | open RiscV.Mach.XlenInt 26 | 27 | structure Block where 28 | base: Nat 29 | data: Array UInt8 30 | deriving Inhabited 31 | 32 | namespace Block 33 | def size_in_bytes (self: Block): Nat := self.data.size 34 | 35 | def limit (self: Block): Nat 36 | := self.base + self.size_in_bytes 37 | 38 | def contains (self: Block) (addr: Nat): Bool 39 | := self.base <= addr && addr < self.limit 40 | 41 | def get_byte (self: Block) (addr: Nat): UInt8 42 | := Array.getD self.data (addr - self.base) 0 43 | 44 | @[always_inline, inline] 45 | def set_byte (self: Block) (addr: Nat) (val: UInt8): Block 46 | := { 47 | self with 48 | data := Array.setD self.data (addr - self.base) val 49 | } 50 | end Block 51 | 52 | 53 | structure Mem where 54 | endian: Endian 55 | blocks: Array Block 56 | 57 | namespace Mem 58 | def getEndian [Monad M] [MonadStateOf Mem M]: M Endian 59 | := do let self <- get 60 | pure self.endian 61 | 62 | def locate_block [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat): M Nat 63 | := do let self <- get 64 | for i in [0:self.blocks.size] do 65 | if self.blocks[i]!.contains addr then return i 66 | throw (RiscVException.PtrOutOfBounds addr) 67 | 68 | def get_byte [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat): M UInt8 69 | := do let self <- get 70 | let idx <- Mem.locate_block addr 71 | pure <| self.blocks[idx]!.get_byte addr 72 | 73 | @[always_inline, inline] 74 | def set_byte [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat) (val: UInt8): M Unit 75 | := do let mut self <- get 76 | let idx <- Mem.locate_block addr 77 | let block := self.blocks[idx]! 78 | set { 79 | self with 80 | blocks := Array.setD self.blocks idx Inhabited.default 81 | } 82 | self <- get 83 | set { 84 | self with 85 | blocks := Array.setD self.blocks idx (block.set_byte addr val) 86 | } 87 | 88 | def get_half [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat): M UInt16 89 | := do let x0 := (<- Mem.get_byte addr).toUInt16 90 | let x1 := (<- Mem.get_byte (addr + 1)).toUInt16 91 | pure <| match (<- getEndian) with 92 | | .Big => (x0 <<< 8) ||| x1 93 | | .Little => (x1 <<< 8) ||| x0 94 | 95 | def set_half [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat) (val: UInt16): M Unit 96 | := do let lo := val.toNat.toUInt8 97 | let hi := (val >>> 8).toNat.toUInt8 98 | let (x0, x1) 99 | := match (<- getEndian) with 100 | | .Big => (hi, lo) 101 | | .Little => (lo, hi) 102 | Mem.set_byte addr x0 103 | Mem.set_byte (addr + 1) x1 104 | 105 | def get_word [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat): M UInt32 106 | := do let x0 := (<- Mem.get_half addr).toUInt32 107 | let x1 := (<- Mem.get_half (addr + 2)).toUInt32 108 | pure <| match (<- getEndian) with 109 | | .Big => (x0 <<< 16) ||| x1 110 | | .Little => (x1 <<< 16) ||| x0 111 | 112 | def set_word [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat) (val: UInt32): M Unit 113 | := do let lo := val.toNat.toUInt16 114 | let hi := (val >>> 16).toNat.toUInt16 115 | let (x0, x1) 116 | := match (<- getEndian) with 117 | | .Big => (hi, lo) 118 | | .Little => (lo, hi) 119 | Mem.set_half addr x0 120 | Mem.set_half (addr + 2) x1 121 | 122 | def get_wide [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat): M UInt64 123 | := do let x0 := (<- Mem.get_word addr).toUInt64 124 | let x1 := (<- Mem.get_word (addr + 4)).toUInt64 125 | pure <| match (<- getEndian) with 126 | | .Big => (x0 <<< 32) ||| x1 127 | | .Little => (x1 <<< 32) ||| x0 128 | 129 | def set_wide [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf Mem M] (addr: Nat) (val: UInt64): M Unit 130 | := do let lo := val.toNat.toUInt32 131 | let hi := (val >>> 32).toNat.toUInt32 132 | let (x0, x1) 133 | := match (<- getEndian) with 134 | | .Big => (hi, lo) 135 | | .Little => (lo, hi) 136 | Mem.set_word addr x0 137 | Mem.set_word (addr + 4) x1 138 | end Mem 139 | 140 | end RiscV.Mach.Mem 141 | -------------------------------------------------------------------------------- /RiscV/Mach/Reg.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace RiscV.Mach.Reg 20 | 21 | open R0sy.Data.Hex 22 | 23 | def X_REGISTER_COUNT: Nat := 32 24 | 25 | inductive Reg where 26 | | X (reg: Fin X_REGISTER_COUNT) 27 | | PC 28 | 29 | instance : ToString Reg where 30 | toString 31 | | .X reg => s!"x{reg.val}" 32 | | .PC => "PC" 33 | 34 | def Reg.ofNat (val: Nat): Reg 35 | := if isLt: val < X_REGISTER_COUNT 36 | then Reg.X { val, isLt } 37 | else Reg.PC 38 | 39 | def Reg.ofUInt32 (x: UInt32): Reg 40 | := Reg.ofNat x.toNat 41 | 42 | def Reg.index (self: Reg): Nat 43 | := match self with 44 | | X x => x.val 45 | | PC => X_REGISTER_COUNT 46 | 47 | 48 | structure RegFile where 49 | data: Array UInt32 50 | 51 | instance : ToString RegFile where 52 | toString self 53 | := Id.run do 54 | let mut out := "" 55 | for i in [0:self.data.size] do 56 | let val := self.data[i]! 57 | if val != 0 then out := s!"{out}{Reg.ofNat i}:{UInt32.toHex val} " 58 | pure out 59 | 60 | def RegFile.new: RegFile 61 | := { 62 | data := Array.mkArray (X_REGISTER_COUNT + 1) 0 63 | } 64 | 65 | def RegFile.newWithPc (pc: UInt32): RegFile 66 | := { 67 | data := Array.setD RegFile.new.data Reg.PC.index pc 68 | } 69 | 70 | def RegFile.get_word [Monad M] [MonadStateOf RegFile M] (reg: Reg): M UInt32 71 | := do if reg.index == 0 then return 0 72 | let self <- get 73 | pure <| self.data[reg.index]! 74 | 75 | def RegFile.set_word [Monad M] [MonadStateOf RegFile M] (reg: Reg) (val: UInt32): M Unit 76 | := do if reg.index == 0 then return () 77 | let self <- get 78 | set { 79 | self with 80 | data := Array.setD self.data reg.index val 81 | } 82 | 83 | 84 | end RiscV.Mach.Reg 85 | -------------------------------------------------------------------------------- /RiscV/Mach/XlenInt.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Config 18 | 19 | namespace RiscV.Mach.XlenInt 20 | 21 | open RiscV.Config 22 | 23 | def XlenInt (xlen: Xlen): Type 24 | := match xlen with 25 | | .Xlen32 => UInt32 26 | | .Xlen64 => UInt64 27 | 28 | namespace XlenInt 29 | def min_unsigned: (xlen: Xlen) -> XlenInt xlen 30 | | .Xlen32 => (0x00000000: UInt32) 31 | | .Xlen64 => (0x0000000000000000: UInt64) 32 | 33 | def max_unsigned: (xlen: Xlen) -> XlenInt xlen 34 | | .Xlen32 => (0xffffffff: UInt32) 35 | | .Xlen64 => (0xffffffffffffffff: UInt64) 36 | 37 | def min_signed: (xlen: Xlen) -> XlenInt xlen 38 | | .Xlen32 => (0x80000000: UInt32) 39 | | .Xlen64 => (0x8000000000000000: UInt64) 40 | 41 | def max_signed: (xlen: Xlen) -> XlenInt xlen 42 | | .Xlen32 => (0x7fffffff: UInt32) 43 | | .Xlen64 => (0x7fffffffffffffff: UInt64) 44 | 45 | 46 | def ofNat: (xlen: Xlen) -> Nat -> XlenInt xlen 47 | | .Xlen32, val => val.toUInt32 48 | | .Xlen64, val => val.toUInt64 49 | 50 | def toNat: {xlen: Xlen} -> XlenInt xlen -> Nat 51 | | .Xlen32, (val: UInt32) => val.toNat 52 | | .Xlen64, (val: UInt64) => val.toNat 53 | 54 | instance : Inhabited (XlenInt xlen) where default := ofNat xlen 0 55 | 56 | def zero {xlen: Xlen}: XlenInt xlen 57 | := ofNat xlen 0 58 | 59 | def one {xlen: Xlen}: XlenInt xlen 60 | := ofNat xlen 1 61 | 62 | 63 | def isNeg: {xlen: Xlen} -> XlenInt xlen -> Bool 64 | | .Xlen32, (val: UInt32) => val >>> 31 == 1 65 | | .Xlen64, (val: UInt64) => val >>> 63 == 1 66 | 67 | def neg: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen 68 | | .Xlen32, (val: UInt32) => (~~~ val) + 1 69 | | .Xlen64, (val: UInt64) => (~~~ val) + 1 70 | 71 | 72 | def ofInt (xlen: Xlen) (val: Int): XlenInt xlen 73 | := if val < 0 74 | then (ofNat xlen (- val).toNat).neg 75 | else ofNat xlen val.toNat 76 | 77 | def toInt {xlen: Xlen} (val: XlenInt xlen): Int 78 | := if val.isNeg 79 | then Int.negOfNat val.neg.toNat 80 | else Int.ofNat val.toNat 81 | 82 | 83 | def lt_unsigned {xlen: Xlen} (lhs rhs: XlenInt xlen): Bool 84 | := lhs.toNat < rhs.toNat 85 | 86 | def ge_unsigned {xlen: Xlen} (lhs rhs: XlenInt xlen): Bool 87 | := lhs.toNat >= rhs.toNat 88 | 89 | 90 | def lt_signed {xlen: Xlen} (lhs rhs: XlenInt xlen): Bool 91 | := lhs.toInt < rhs.toInt 92 | 93 | def ge_signed {xlen: Xlen} (lhs rhs: XlenInt xlen): Bool 94 | := lhs.toInt >= rhs.toInt 95 | 96 | 97 | def add: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen -> XlenInt xlen 98 | | .Xlen32, (lhs: UInt32), (rhs: UInt32) => lhs + rhs 99 | | .Xlen64, (lhs: UInt64), (rhs: UInt64) => lhs + rhs 100 | 101 | def sub: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen -> XlenInt xlen 102 | | .Xlen32, (lhs: UInt32), (rhs: UInt32) => lhs - rhs 103 | | .Xlen64, (lhs: UInt64), (rhs: UInt64) => lhs - rhs 104 | 105 | 106 | def and: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen -> XlenInt xlen 107 | | .Xlen32, (lhs: UInt32), (rhs: UInt32) => lhs &&& rhs 108 | | .Xlen64, (lhs: UInt64), (rhs: UInt64) => lhs &&& rhs 109 | 110 | def or: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen -> XlenInt xlen 111 | | .Xlen32, (lhs: UInt32), (rhs: UInt32) => lhs ||| rhs 112 | | .Xlen64, (lhs: UInt64), (rhs: UInt64) => lhs ||| rhs 113 | 114 | def xor: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen -> XlenInt xlen 115 | | .Xlen32, (lhs: UInt32), (rhs: UInt32) => lhs ^^^ rhs 116 | | .Xlen64, (lhs: UInt64), (rhs: UInt64) => lhs ^^^ rhs 117 | 118 | def not: {xlen: Xlen} -> XlenInt xlen -> XlenInt xlen 119 | | .Xlen32, (val: UInt32) => (~~~ val: UInt32) 120 | | .Xlen64, (val: UInt64) => (~~~ val: UInt64) 121 | 122 | def shl: {xlen: Xlen} -> XlenInt xlen -> Nat -> XlenInt xlen 123 | | .Xlen32, (val: UInt32), sh => val <<< sh.toUInt32 124 | | .Xlen64, (val: UInt64), sh => val <<< sh.toUInt64 125 | 126 | def shr_unsigned: {xlen: Xlen} -> XlenInt xlen -> Nat -> XlenInt xlen 127 | | .Xlen32, (val: UInt32), sh => val >>> sh.toUInt32 128 | | .Xlen64, (val: UInt64), sh => val >>> sh.toUInt64 129 | 130 | def shr_signed {xlen: Xlen} (val: XlenInt xlen) (sh: Nat): XlenInt xlen 131 | := (shr_unsigned val sh).or (if val.isNeg then ofNat xlen <| ((1 <<< sh) - 1) <<< (xlen.bits - sh) else zero) 132 | end XlenInt 133 | 134 | end RiscV.Mach.XlenInt 135 | -------------------------------------------------------------------------------- /RiscV/Monad.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV.Mach.Exception 18 | import RiscV.Mach.Mem 19 | import RiscV.Mach.Reg 20 | 21 | namespace RiscV.Monad 22 | 23 | open RiscV.Mach.Exception 24 | open RiscV.Mach.Mem 25 | open RiscV.Mach.Reg 26 | 27 | 28 | structure MachineState where 29 | mem: Mem 30 | reg_file: RegFile 31 | 32 | namespace MachineState 33 | @[always_inline] 34 | instance [Monad M] [MonadStateOf MachineState M] : MonadStateOf Mem M where 35 | get 36 | := do let self <- get 37 | pure self.mem 38 | set mem 39 | := do let self <- get 40 | set { self with mem } 41 | modifyGet f 42 | := do let self <- get 43 | let (out, mem) := f self.mem 44 | set { self with mem } 45 | pure out 46 | 47 | @[always_inline] 48 | instance [Monad M] [MonadStateOf MachineState M] : MonadStateOf RegFile M where 49 | get 50 | := do let self <- get 51 | pure self.reg_file 52 | set reg_file 53 | := do let self <- get 54 | set { self with reg_file } 55 | modifyGet f 56 | := do let self <- get 57 | let (out, reg_file) := f self.reg_file 58 | set { self with reg_file } 59 | pure out 60 | end MachineState 61 | 62 | 63 | class MonadMachine (M: Type -> Type) 64 | extends 65 | Monad M, 66 | MonadExceptOf RiscVException M, 67 | MonadStateOf MachineState M 68 | where 69 | 70 | namespace MonadMachine 71 | @[always_inline, inline] 72 | def getMachine [MonadMachine M]: M MachineState 73 | := MonadStateOf.get 74 | 75 | @[always_inline, inline] 76 | def getReg [MonadMachine M] (reg: Reg): M UInt32 77 | := RegFile.get_word reg 78 | 79 | @[always_inline, inline] 80 | def fetchWord [MonadMachine M] (addr: UInt32): M UInt32 81 | := Mem.get_word addr.toNat 82 | 83 | @[always_inline] 84 | instance CanonicalInstance [Monad M] [MonadExceptOf RiscVException M] [MonadStateOf MachineState M] : MonadMachine M where 85 | 86 | @[always_inline] 87 | instance LiftInstance [Monad M] : MonadLift M (ExceptT RiscVException (StateT Machine M)) where 88 | monadLift f := ExceptT.lift (StateT.lift f) 89 | end MonadMachine 90 | 91 | 92 | namespace MachineState 93 | @[always_inline, inline] 94 | def run [Monad M] (machine: MachineState) (f: {M': Type -> Type} -> [MonadMachine M'] -> [MonadLift M M'] -> M' X): M (Except RiscVException X × MachineState) 95 | := StateT.run (ExceptT.run f) machine 96 | 97 | @[always_inline, inline] 98 | def run' [Monad M] (machine: MachineState) (f: {M': Type -> Type} -> [MonadMachine M'] -> [MonadLift M M'] -> M' X): M (Except RiscVException X) 99 | := StateT.run' (ExceptT.run f) machine 100 | end MachineState 101 | 102 | end RiscV.Monad 103 | -------------------------------------------------------------------------------- /Soundness/Merkle.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import Mathlib.Data.Option.Basic 18 | import Init.Data.Nat.Log2 19 | import Zkvm.Verify.Merkle 20 | 21 | /-! 22 | # Merkle Trees in Lean 23 | 24 | Implements merkle trees 25 | 26 | ## Indexing 27 | 28 | 01 -- Level 0 29 | | \ 30 | 02 03 -- Level 1 31 | | \ | \ 32 | 04 05 06 07 -- Level 2 33 | 34 | ## Merkle Tree Collision Extraction 35 | 36 | The main def here is MerkleTreeCollisionExtractor, which, 37 | given two merkle branches to the same index proving different values, 38 | returns a collision of the hash function. 39 | 40 | There is also code for a proof of this fact. This proof is in the process of being ported from Lean 3, but a few more tactics will be needed first. 41 | 42 | -/ 43 | 44 | -- variables {F : Type} {A : Type} [decidable_eq A] [inhabited F] [inhabited A] 45 | 46 | /-- A collection of functions representing a hash function which can act on a field type and also on pairs of its own hashes, 47 | as well as return field elements. -/ 48 | structure hashing_scheme (F A : Type) := 49 | (of_hash_pair : A -> A -> A) 50 | (of_field_element : F -> A) 51 | -- (hash_to_field : A -> F) -- might readd later 52 | 53 | variable (h : hashing_scheme F A) 54 | 55 | def LayersFromRowSize (row_size : Nat) := Nat.log2 row_size 56 | 57 | def TopLayerFromQueries (queries : Nat) := Nat.log2 (queries) + 1 58 | 59 | -- Pairs elements off to make a row in the Merkle tree one layer closer to the root 60 | def CollapseLayer : List A -> List A 61 | | [] => [] 62 | | (a :: []) => a :: [] 63 | | (a :: b :: tail') => (h.of_hash_pair a b) :: (CollapseLayer tail') 64 | 65 | /-- Takes a height and a List of hashes of length 2^height and returns the hash tree -/ 66 | def hash_tree (height : Nat) (hashes: List A) : List A := 67 | match height with 68 | | 0 => hashes ++ hashes 69 | | (n+1) => hash_tree n (CollapseLayer h hashes) ++ hashes 70 | 71 | 72 | /-- A structure representing a merkle tree of elements of leaf type F and hash type A 73 | leaves is expected to be a List of power-of-2-length, and hashes a List of twice the length 74 | The indexing of hashes is as follows: 75 | the first element is a default, 76 | the index one element is the root, 77 | the index 2-3 elements are the next row of the tree 78 | and so forth, up to the n to (2*n-1)th elements, which are the indices of the hashes of the leaves-/ 79 | structure merkle_tree (F A : Type) := 80 | (hashes : List A) 81 | (leaves : List F) 82 | 83 | def merkle_tree.root [Inhabited A] (tree : merkle_tree F A) : A := tree.hashes.get! 1 84 | 85 | /-- Contains the List of hashes and the leaf value field element. 86 | The head element of the branch hashes is closest to the leaves -/ 87 | structure MerkleBranch (F A : Type) := 88 | (branch_hashes : List A) 89 | (leaf : F) 90 | 91 | def merkle_tree.of_leaf_list (leaves : List F) : merkle_tree F A := 92 | { hashes := hash_tree h (Nat.log2 leaves.length) (leaves.map h.of_field_element), 93 | leaves := leaves } 94 | 95 | def merkle_tree.layers (tree : merkle_tree F A) : Nat := (Nat.log2 tree.leaves.length) 96 | 97 | /-- Given an index as described in the module docstring and a level, returns the descendant of that index in that level. 98 | e.g. get_ancestor_at_level 1 7 should return 3. -/ 99 | def get_ancestor_at_level (level : Nat) (val : Nat): Nat := 100 | val / 2 ^ (Nat.log2 val - level) 101 | 102 | 103 | def get_sibling (idx : Nat) : Nat := 104 | if idx % 2 = 1 then idx - 1 else idx + 1 105 | 106 | 107 | def merkle_tree.get_branch [Inhabited F] [Inhabited A] (tree : merkle_tree F A) (idx : Nat) : MerkleBranch F A := 108 | { branch_hashes := 109 | (List.range tree.layers).map 110 | (λ i => tree.hashes.get! (get_sibling (get_ancestor_at_level (tree.layers - i) idx))), 111 | leaf := tree.leaves.get! idx } 112 | 113 | /-- Given `top_layer` a row of a Merkle tree at layer top, an index into the tree, 114 | a value at that index, and a Merkle branch putatively proving that value to that top row, 115 | this checks if the verification of the branch will return correct 116 | -/ 117 | def merkle_tree.verify_aux [Inhabited A] [DecidableEq A] (hash : A -> A -> A) 118 | (top_layer_idx : Nat) (top_layer : List A) (tree_idx: Nat) (value: A) (branch: List A): 119 | Bool := 120 | match branch with 121 | | [] => 122 | Nat.ble (2 ^ top_layer_idx) tree_idx 123 | && 124 | Nat.blt tree_idx (2 ^ (Nat.succ top_layer_idx)) 125 | && 126 | List.get! top_layer (tree_idx - 2 ^ top_layer_idx) == some value 127 | | (h :: tail) => 128 | if tree_idx % 2 = 1 129 | then merkle_tree.verify_aux hash top_layer_idx top_layer (tree_idx / 2) (hash h value) tail 130 | else merkle_tree.verify_aux hash top_layer_idx top_layer (tree_idx / 2) (hash value h) tail 131 | 132 | 133 | -- * Given `top` a row of a Merkle tree at layer top_layer, a row_size, an index into the row of leaves, a leaf value at that index, and a Merkle branch putatively proving that value to that top row, this checks if the verification of the branch will return correct *) 134 | def MerkleTreeVerifyToTop [Inhabited A] [DecidableEq A] (top_layer : Nat) (top : List A) (row_size : Nat) (idx : Nat) (value : A) 135 | (branch : List A) : Bool := 136 | merkle_tree.verify_aux h.of_hash_pair top_layer top (idx + row_size) value branch 137 | 138 | -- Given a root of a Merkle tree, a row_size, an index into the row of leaves, a leaf value at that index, and a Merkle branch putatively proving that value to that top row, this checks if the verification of the branch will return correct *) 139 | def MerkleTreeVerifyToRoot [Inhabited A] [DecidableEq A] (root : A) (row_size : Nat) (idx : Nat) (value : A) 140 | (branch : List A) : Bool := 141 | MerkleTreeVerifyToTop h 0 (root :: []) row_size idx value branch 142 | 143 | def merkle_branch.verify [Inhabited A] [DecidableEq A] (branch : MerkleBranch F A) (root_hash : A) (index : Nat) : Bool := 144 | MerkleTreeVerifyToRoot h root_hash (2^branch.branch_hashes.length) index 145 | (h.of_field_element branch.leaf) (branch.branch_hashes) 146 | 147 | 148 | 149 | 150 | structure MerkleTreeVerifier : Type := 151 | (top_layer_idx : Nat) 152 | (top_layer : List A) 153 | 154 | 155 | 156 | 157 | -- (* Given two merkle branches to two values at the same index, returns the first pair of pairs of hashes that result in the same value. *) 158 | def MerkleTreeCollisionExtractor [Inhabited A] [DecidableEq A] (hash : A -> A -> A) 159 | (idx : Nat) (value1 value2 : A) (branch1 branch2 : List A): 160 | (Option ((A × A) × (A × A))) := 161 | match branch1, branch2 with 162 | | [], _ => none 163 | | _, [] => none 164 | | (h1 :: tail1), (h2 :: tail2) => 165 | if idx % 2 = 1 166 | then if (hash h1 value1) == (hash h2 value2) 167 | then some ((h1, value1), (h2, value2)) 168 | else MerkleTreeCollisionExtractor hash (idx / 2) (hash h1 value1) (hash h2 value2) tail1 tail2 169 | else if (hash value1 h1) == (hash value2 h2) 170 | then some ((value1, h1), (value2, h2)) 171 | else MerkleTreeCollisionExtractor hash (idx / 2) (hash value1 h1) (hash value2 h2) tail1 tail2 172 | 173 | 174 | -- (* A helper lemma telling us the length of a Merkle Branch *) 175 | def BranchLengthOfMerkleTreeVerifyAux [Inhabited A] [DecidableEq A] 176 | (hash: A -> A -> A) 177 | {top_layer_idx : Nat} {top_layer : List A} {tree_idx : Nat} 178 | {value : A} {branch : List A} 179 | (h_verifies : merkle_tree.verify_aux hash top_layer_idx top_layer tree_idx value branch) : 180 | List.length branch = (Nat.log2 tree_idx) - top_layer_idx := by 181 | revert h_verifies 182 | revert tree_idx 183 | revert value 184 | induction branch 185 | { intros value tree_idx h_verifies 186 | unfold merkle_tree.verify_aux at h_verifies 187 | sorry 188 | -- cases h_verifies with ⟨hva, hvb, hve⟩ 189 | -- clear hve value top_layer 190 | -- simp 191 | -- symmetry 192 | -- rw tsub_eq_zero_iff_le 193 | -- have : (Nat.log2 tree_idx = top_layer_idx) 194 | -- { 195 | -- sorry, 196 | -- }, 197 | -- rw this, 198 | -- apply Nat,log2_unique, 199 | -- apply Nat,le_0_l, 200 | -- split, 201 | -- auto, 202 | -- auto, 203 | -- rewrite H, 204 | -- apply Nat,le_refl, 205 | } 206 | { 207 | intros value tree_idx h_verifies 208 | simp 209 | sorry 210 | -- (* simpl in h_verifies, *) 211 | -- unfold MerkleTreeVerifyAux in h_verifies, 212 | -- fold MerkleTreeVerifyAux in h_verifies, 213 | 214 | -- destruct (tree_idx mod 2 =? 1 ), 215 | -- { 216 | -- apply (IHbranch (hash a value) (tree_idx / 2)) in h_verifies, 217 | -- rewrite h_verifies, 218 | -- (* Not quite true for tree_idx <= 1, add edge case*) 219 | -- admit, 220 | -- } 221 | -- { 222 | -- apply (IHbranch (hash value a) (tree_idx / 2)) in h_verifies, 223 | -- rewrite h_verifies, 224 | -- admit, 225 | -- } 226 | 227 | } 228 | 229 | 230 | 231 | -- -- (* Proves the output of `MerkleTreeCollisionExtractor` returns two distinct values, when given two branches that both verify against that same top. *) 232 | def MerkleTreeCollisionExtractor__CollidesAux 233 | [Inhabited A] [DecidableEq A] 234 | (hash: A -> A -> A) 235 | (top_layer : Nat) (top : List A) (row_size : Nat) -- (* A Merkle Tree *) 236 | (idx : Nat) -- (* A specific leaf location *) 237 | (value1 value2 : A) -- (* Two (ostensibly different) leaf values *) 238 | (branch1 branch2 : List A) -- (* Two branches to those values *) 239 | (hbranch1 : merkle_tree.verify_aux hash top_layer top idx value1 branch1) 240 | (hbranch2 : merkle_tree.verify_aux hash top_layer top idx value2 branch2) 241 | (hneq : value1 ≠ value2) : 242 | (MerkleTreeCollisionExtractor hash idx value1 value2 branch1 branch2).casesOn' false (λ p => p.fst.fst = p.snd.fst ∧ p.fst.snd = p.snd.snd) := 243 | by 244 | revert value1 value2 idx branch2 245 | sorry 246 | -- induction branch1 247 | -- { --(* Case where one branch is empty *) 248 | -- intros value1 value2 idx branch2 hv1 hv2 neq 249 | -- pose proof BranchLengthOfMerkleTreeVerifyAux hv1 as H1 250 | -- pose proof BranchLengthOfMerkleTreeVerifyAux hv2 as H2 251 | -- rewrite <-H1 in H2. 252 | 253 | -- simpl. 254 | -- apply neq. 255 | 256 | -- simpl in H1. 257 | 258 | -- simpl in H2. 259 | -- rewrite length_zero_iff_nil in H2. 260 | -- rewrite H2 in hv2. 261 | -- unfold MerkleTreeVerifyAux in hv1. 262 | -- unfold MerkleTreeVerifyAux in hv2. 263 | -- destruct hv1 as [hv1a [hv1b hv1e]]. 264 | -- destruct hv2 as [hv2a [hv2b hv2e]]. 265 | -- rewrite hv1e in hv2e. 266 | -- inversion hv2e. 267 | -- rewrite (refl_eq value2). 268 | -- simpl. 269 | -- constructor. 270 | -- } 271 | -- intros value1 value2 idx branch2 hv1 hv2 neq. 272 | -- destruct branch2. 273 | -- { 274 | -- exfalso. 275 | -- pose proof BranchLengthOfMerkleTreeVerifyAux hv1 as H1. 276 | -- pose proof BranchLengthOfMerkleTreeVerifyAux hv2 as H2. 277 | -- rewrite <-H1 in H2. 278 | -- simpl in H2. 279 | -- inversion H2. 280 | -- } 281 | -- unfold MerkleTreeCollisionExtractor. 282 | -- fold MerkleTreeCollisionExtractor. 283 | -- unfold MerkleTreeVerifyAux in hv1. 284 | -- unfold MerkleTreeVerifyAux in hv2. 285 | -- fold MerkleTreeVerifyAux in hv1. 286 | -- fold MerkleTreeVerifyAux in hv2. 287 | 288 | -- destruct (idx mod 2 =? 1) eqn:?. 289 | -- { pose proof IHbranch1 (hash a value1) (hash a0 value2) (idx / 2) branch2 hv1 hv2. 290 | 291 | -- destruct (eq (hash a value1) (hash a0 value2)) eqn:?. 292 | -- { 293 | -- intro H2. 294 | -- apply neq. 295 | -- elim (andb_prop_elim _ _ H2). 296 | -- intros left_ right_. 297 | -- exact right_. 298 | -- } 299 | -- { 300 | -- apply H. 301 | -- simpl. (* What is the canonical way of solving this? *) 302 | -- intro f. 303 | -- exact f. 304 | -- } 305 | -- } 306 | -- { pose proof IHbranch1 (hash value1 a) (hash value2 a0) (idx / 2) branch2 hv1 hv2. 307 | -- destruct (eq (hash value1 a) (hash value2 a0)) eqn:?. 308 | -- { 309 | -- intro H2. 310 | -- apply neq. 311 | -- elim (andb_prop_elim _ _ H2). 312 | -- intros left_ right_. 313 | -- exact left_. 314 | -- } 315 | -- { 316 | -- apply H. 317 | -- simpl. 318 | -- intro f. 319 | -- exact f. 320 | -- } 321 | -- (* Similar to previous block *) 322 | -- }, 323 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /Soundness/ProofSystem.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import Zkvm 18 | import R0sy.Hash 19 | import Std.Data.Rat.Basic 20 | import Std.Data.List.Basic 21 | 22 | namespace Soundness.ProofSystem 23 | 24 | open R0sy.Hash 25 | 26 | /-- A Type of all possible things that could be hashed -/ 27 | def HashPreimage (D : Type) := ByteArray ⊕ Subarray UInt32 ⊕ (D × D) ⊕ Array (Array UInt32) 28 | 29 | /-- A function to evaluate the hash on an arbitrary Hash-able type -/ 30 | def HashEval (h : Hash D): (input : HashPreimage D) -> D 31 | | Sum.inl x => h.hash x 32 | | Sum.inr (Sum.inl x) => h.hash_words x 33 | | Sum.inr (Sum.inr (Sum.inl ⟨x, y⟩)) => h.hash_pair x y 34 | | Sum.inr (Sum.inr (Sum.inr x)) => h.hash_array_array x 35 | 36 | /-- Given a hash function and an adversary choosing a next query from a list of previous oracle responses, return the list of all responses -/ 37 | def query_list {D : Type} : 38 | Nat -> (Hash D) -> (List D -> HashPreimage D) -> List D 39 | | 0, _, _ => List.nil 40 | | (Nat.succ n), ro, next => (HashEval ro (next (query_list n ro next))) :: (query_list n ro next) 41 | 42 | /-- Structure for an agent which works by querying a hashing scheme several times and then returning a value based on the output of the hashes -/ 43 | structure query_bounded_adversary (D Out : Type) := 44 | (adversary_query_generator : List D -> HashPreimage D) 45 | (adversary_out_from_list : List D -> Out) 46 | 47 | /-- Returns the output of the query bounded adversary, with a particular query bound and hashing scheme -/ 48 | def query_bounded_adversary.to_fun {D Out : Type} 49 | (𝓐 : query_bounded_adversary D Out) (query_count : Nat) (hash_function : Hash D) : 50 | Out := 51 | 𝓐.adversary_out_from_list (query_list query_count hash_function (𝓐.adversary_query_generator)) 52 | 53 | /-- A monadic type constructor for representing Probability mass functions -/ 54 | def Pmf α := List α 55 | 56 | instance : Monad Pmf where 57 | pure := List.pure 58 | bind := List.bind 59 | 60 | def Pmf.prod (a : Pmf α) (b : Pmf β) : Pmf (α × β) := do 61 | let a_ <- a 62 | let b_ <- b 63 | return ⟨a_, b_⟩ 64 | 65 | def Pmf.prob [BEq α] (p : Pmf α) (a : α) : Rat := List.count a p / List.length p 66 | 67 | /-- Given a list of items of type α and a Pmf over β, returns a Pmf over functions α -> β, 68 | sending each α in the list independently to the Pmf, and all other α to the default -/ 69 | def Pmf.func [BEq α] (as : List α) (default : β) (bs : Pmf β) : Pmf (α -> β) := 70 | match as with 71 | | [] => [λ _ => default] 72 | | head :: tail => do 73 | let rec <- Pmf.func tail default bs 74 | let b <- bs 75 | return λ a => if a == head then b else rec a 76 | 77 | /-- 78 | Defines the soundness criterion: there must exist an extractor, such that for any adversary making a certain number of queries and trying to convince the verifier of a particular statement, if the adversary is likely to succeed, then we can extract a witness from the adversary. 79 | -/ 80 | def verifier_soundness {Stmt Wit D Proof : Type} (relation : Stmt -> Wit -> Prop) (random_oracles : Pmf (Hash D)) 81 | (verifier : [Hash D] -> Stmt -> Proof -> Bool) (soundness_bound : Nat -> Rat) : Prop := 82 | ∃ (knowledge_extractor : (query_bounded_adversary D Proof) -> Wit), 83 | -- adversary query generator takes a input and outputs a next query 84 | ∀ (query_count : Nat), ∀ (stmt : Stmt), ∀ (𝓐 : query_bounded_adversary D Proof), 85 | -- If the adversary has probability greater than the soundness bound of convincing the verifier ... 86 | (let adv_verifies : Pmf Bool := (do 87 | let (h : Hash D) <- random_oracles 88 | return (@verifier h stmt (@query_bounded_adversary.to_fun _ _ 𝓐 query_count h))) 89 | Pmf.prob adv_verifies true ≥ soundness_bound query_count) 90 | -- ... then the extractor obtains a correct witness. 91 | -> relation stmt (knowledge_extractor 𝓐) 92 | 93 | /-- 94 | A structure for a non-interactive proof system in the random oracle model. 95 | This could be modified to use a hybrid random-oracle/collision-resistant-hash-function model by adding more types for the input and output of that function. 96 | -/ 97 | structure noninteractive_random_oracle_proof_scheme := -- n_ro_codomain= 2^256 usually 98 | (Stmt : Type) 99 | (Wit : Type) 100 | (D : Type) 101 | (relation : Stmt -> Wit → Prop) 102 | (random_oracles : Pmf (Hash D)) -- A pmf uniform over all random oracles 103 | (Proof : Type) 104 | (prover : 105 | [Hash D] -> Stmt -> Wit -> Proof 106 | ) 107 | (verifier : 108 | [Hash D] -> Stmt -> Proof -> Bool 109 | ) 110 | (completeness : -- Perfect completeness here 111 | ∀ (ro : Hash D), ∀ (stmt : Stmt), ∀ (wit : Wit), 112 | relation stmt wit -> @verifier ro stmt (@prover ro stmt wit) 113 | ) 114 | -- Given n queries to oracle, what is worst case probability of compromise? 115 | (soundness_bound : Nat -> Rat) 116 | (soundness : verifier_soundness relation random_oracles verifier soundness_bound ) 117 | 118 | 119 | end Soundness.ProofSystem 120 | 121 | 122 | /-- A verifier function, exactly analogous to that in check_seal, 123 | but defined as a pure function with the type signature needed to pass into the soundness proposition -/ 124 | def verifier [h : R0sy.Hash.Hash R0sy.Hash.Sha2.Sha256.Digest] 125 | (stmt : Zkvm.ArithVM.Circuit.Circuit × Zkvm.MethodId.MethodId R0sy.Hash.Sha2.Sha256.Digest × Array UInt32) 126 | (seal: Array UInt32) : Bool := 127 | let ⟨circuit, method_id, journal⟩ := stmt 128 | let result := Zkvm.Verify.ReadIop.ReadIop.run seal (@Zkvm.Verify.verify _ h circuit method_id journal) 129 | match result with 130 | | Except.ok _ => True 131 | | Except.error _ => False 132 | 133 | def soundness_bound (queries : Nat) : Rat := queries / ((2 ^ 128 : Nat) : Rat) 134 | 135 | instance : BEq ByteArray := 136 | { beq := λ a b => a.data == b.data } 137 | 138 | instance : BEq (Subarray UInt32) := 139 | { beq := λ a b => a.toArray == b.toArray } 140 | 141 | /-- A list of all byte arrays that can be hashed -/ 142 | def used_byte_arrays : List ByteArray := sorry 143 | 144 | /-- A list of all byte arrays that can be hashed -/ 145 | def used_subarrays : List (Subarray UInt32) := sorry 146 | 147 | /-- A list of all byte arrays that can be hashed -/ 148 | def used_array_arrays : List (Array (Array UInt32)) := sorry 149 | 150 | /-- All digests -/ 151 | def all_digests : List (R0sy.Hash.Sha2.Sha256.Digest) := sorry 152 | 153 | /-- All digest pairs -/ 154 | def all_digest_pairs : List (R0sy.Hash.Sha2.Sha256.Digest × R0sy.Hash.Sha2.Sha256.Digest) := sorry 155 | 156 | def random_oracles : Soundness.ProofSystem.Pmf (R0sy.Hash.Hash R0sy.Hash.Sha2.Sha256.Digest) := do 157 | let byte_array_maps <- Soundness.ProofSystem.Pmf.func used_byte_arrays default all_digests 158 | let subarray_maps <- Soundness.ProofSystem.Pmf.func used_subarrays default all_digests 159 | let pair_maps : R0sy.Hash.Sha2.Sha256.Digest × R0sy.Hash.Sha2.Sha256.Digest → R0sy.Hash.Sha2.Sha256.Digest <- Soundness.ProofSystem.Pmf.func all_digest_pairs default all_digests 160 | let curried_pair_maps := λ x y => pair_maps ⟨x, y⟩ 161 | let array_array_maps <- Soundness.ProofSystem.Pmf.func used_array_arrays default all_digests 162 | return R0sy.Hash.Hash.mk byte_array_maps subarray_maps curried_pair_maps array_array_maps 163 | 164 | 165 | def relation (stmt : Zkvm.ArithVM.Circuit.Circuit × Zkvm.MethodId.MethodId R0sy.Hash.Sha2.Sha256.Digest × Array UInt32) (wit : Nat) : Prop := sorry 166 | 167 | def main_soundness_claim : Soundness.ProofSystem.verifier_soundness relation random_oracles verifier soundness_bound := 168 | by 169 | unfold Soundness.ProofSystem.verifier_soundness 170 | sorry -------------------------------------------------------------------------------- /Zkvm.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import Zkvm.Algebra.Classes 18 | import Zkvm.Algebra.BabyBear 19 | import Zkvm.Algebra.Ntt 20 | import Zkvm.ArithVM.AST 21 | import Zkvm.ArithVM.Circuit 22 | import Zkvm.ArithVM.Taps 23 | import Zkvm.Constants 24 | import Zkvm.MethodId 25 | import Zkvm.Platform.Elf 26 | import Zkvm.Platform.Mem 27 | import Zkvm.Seal.CheckCommitments 28 | import Zkvm.Seal.Fri 29 | import Zkvm.Seal.Header 30 | import Zkvm.Seal.TraceCommitments 31 | import Zkvm.Verify 32 | import Zkvm.Verify.Error 33 | import Zkvm.Verify.Merkle 34 | import Zkvm.Verify.ReadIop 35 | 36 | /-! 37 | # ZKVM 38 | 39 | The RISC Zero ZKVM. 40 | -/ 41 | 42 | namespace Zkvm 43 | open R0sy.Hash.Sha2 44 | open Zkvm.ArithVM.Circuit 45 | open Zkvm.MethodId 46 | open Zkvm.Verify.ReadIop 47 | 48 | def verify 49 | (seal: Array UInt32) 50 | (circuit: Circuit) 51 | (methodId: MethodId Sha256.Digest) 52 | (journal: Array UInt32) 53 | : Bool 54 | := (ReadIop.run seal (Zkvm.Verify.verify circuit methodId journal)).toBool 55 | 56 | end Zkvm 57 | -------------------------------------------------------------------------------- /Zkvm/Algebra/BabyBear.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | 20 | namespace Zkvm.Algebra.BabyBear 21 | 22 | open R0sy.Hash 23 | open R0sy.Serial 24 | open Zkvm.Algebra.Classes 25 | 26 | /- Base field -/ 27 | 28 | @[always_inline, inline] 29 | def P: UInt32 := (15 * 2^27 + 1).toUInt32 30 | 31 | @[always_inline, inline] 32 | def P_U64: UInt64 := P.toUInt64 33 | 34 | structure Elem where 35 | val: UInt32 36 | 37 | @[always_inline, inline] 38 | def M: UInt32 := 0x88000001 39 | 40 | @[always_inline, inline] 41 | def R2: Elem := { val := 1172168163 } 42 | 43 | @[always_inline, inline] 44 | def prim_mul (lhs rhs: Elem): Elem := 45 | let o64: UInt64 := lhs.val.toUInt64 * rhs.val.toUInt64 46 | let low: UInt32 := 0 - o64.toUInt32 47 | let red: UInt32 := M * low 48 | let o64' := o64 + red.toUInt64 * P_U64 49 | let r := (o64' >>> 32).toUInt32 50 | if r >= P then { val := r - P } else { val := r } 51 | 52 | @[always_inline, inline] 53 | def prim_add (lhs rhs: Elem): Elem := 54 | let z := lhs.val + rhs.val 55 | if z >= P then { val := z - P } else { val := z } 56 | 57 | @[always_inline, inline] 58 | def prim_sub (lhs rhs: Elem): Elem := 59 | let z := lhs.val - rhs.val 60 | if z >= P then { val := z + P } else { val := z } 61 | 62 | @[always_inline, inline] 63 | def encode (a: UInt32): Elem 64 | := prim_mul R2 { val := a } 65 | 66 | @[always_inline, inline] 67 | def decode (a: Elem): UInt32 68 | := (prim_mul { val := 1 } a).val 69 | 70 | @[always_inline, inline] 71 | def BETA: Elem := encode 11 72 | 73 | @[always_inline, inline] 74 | def NBETA: Elem := encode (P - 11) 75 | 76 | @[always_inline] 77 | instance : ToString Elem where toString x := toString (decode x) 78 | 79 | @[always_inline] 80 | instance : Inhabited Elem where default := encode 0 81 | 82 | @[always_inline] 83 | instance : OfNat Elem n where ofNat := encode (UInt32.ofNat (n % P.toNat)) 84 | 85 | @[always_inline] 86 | instance : BEq Elem where beq x y := x.val == y.val 87 | 88 | @[always_inline] 89 | instance : Add Elem where add x y := prim_add x y 90 | 91 | @[always_inline] 92 | instance : Sub Elem where sub x y := prim_sub x y 93 | 94 | @[always_inline] 95 | instance : Neg Elem where neg x := prim_sub 0 x 96 | 97 | @[always_inline] 98 | instance : Mul Elem where mul x y := prim_mul x y 99 | 100 | @[always_inline, inline] 101 | partial def prim_pow (x_mul: X -> X -> X) (one x: X) (n: Nat): X := 102 | if n == 0 then one 103 | else if n == 1 then x 104 | else if n == 2 then x_mul x x 105 | else if n &&& 1 == 0 then prim_pow x_mul one (x_mul x x) (n / 2) 106 | else x_mul x (prim_pow x_mul one (x_mul x x) ((n - 1) / 2)) 107 | 108 | @[always_inline] 109 | instance : HPow Elem Nat Elem where hPow x n := prim_pow prim_mul (encode 1) x n 110 | 111 | @[always_inline] 112 | instance : Ring Elem where ofNat n := encode (UInt32.ofNat (n % P.toNat)) 113 | 114 | @[always_inline] 115 | instance : Div Elem where div x y := prim_mul x (prim_pow prim_mul (encode 1) y (P - 2).toNat) 116 | 117 | @[always_inline] 118 | instance : SerialUInt32 Elem where 119 | words := 1 120 | toUInt32Words x := #[x.val] 121 | fromUInt32Words x := { val := x[0]! } 122 | 123 | @[always_inline, inline] 124 | def prim_random {M: Type -> Type} [Monad M] [MonadRng M]: M Elem 125 | := do let mut val: UInt64 := 0 126 | for _ in [0:6] do 127 | let r <- MonadRng.nextUInt32 128 | val := ((val <<< 32) + r.toUInt64) % P_U64 129 | pure (encode val.toUInt32) 130 | 131 | @[always_inline] 132 | instance : Field Elem where 133 | inv x := prim_pow prim_mul (encode 1) x (P - 2).toNat 134 | random := prim_random 135 | fromUInt64 x := encode x.toUInt32 136 | 137 | @[always_inline] 138 | instance Elem.PrimeField : PrimeField Elem where 139 | toNat x := (decode x).toNat 140 | 141 | @[always_inline] 142 | instance Elem.RootsOfUnity : RootsOfUnity Elem where 143 | MAX_ROU_SIZE := 27 144 | ROU_FWD := #[ 145 | 1, 2013265920, 284861408, 1801542727, 567209306, 740045640, 918899846, 1881002012, 146 | 1453957774, 65325759, 1538055801, 515192888, 483885487, 157393079, 1695124103, 2005211659, 147 | 1540072241, 88064245, 1542985445, 1269900459, 1461624142, 825701067, 682402162, 1311873874, 148 | 1164520853, 352275361, 18769, 137 149 | ] 150 | ROU_REV := #[ 151 | 1, 2013265920, 1728404513, 1592366214, 196396260, 1253260071, 72041623, 1091445674, 152 | 145223211, 1446820157, 1030796471, 2010749425, 1827366325, 1239938613, 246299276, 153 | 596347512, 1893145354, 246074437, 1525739923, 1194341128, 1463599021, 704606912, 95395244, 154 | 15672543, 647517488, 584175179, 137728885, 749463956 155 | ] 156 | 157 | 158 | structure ExtElem where 159 | c0: Elem 160 | c1: Elem 161 | c2: Elem 162 | c3: Elem 163 | deriving BEq 164 | 165 | @[always_inline, inline] 166 | def ExtElem.new (c0 c1 c2 c3: Elem): ExtElem 167 | := { 168 | c0, 169 | c1, 170 | c2, 171 | c3 172 | } 173 | 174 | @[always_inline] 175 | instance : ToString ExtElem where toString x := toString #[x.c0, x.c1, x.c2, x.c3] 176 | 177 | @[always_inline] 178 | instance : Inhabited ExtElem where default := ExtElem.new Inhabited.default Inhabited.default Inhabited.default Inhabited.default 179 | 180 | @[always_inline, inline] 181 | def prim_ext_of_base (c0: Elem): ExtElem 182 | := { 183 | c0, 184 | c1 := encode 0, 185 | c2 := encode 0, 186 | c3 := encode 0, 187 | } 188 | 189 | @[always_inline, inline] 190 | def prim_ext_of_nat (n: Nat): ExtElem 191 | := prim_ext_of_base (encode n.toUInt32) 192 | 193 | @[always_inline] 194 | instance : OfNat ExtElem n where ofNat := prim_ext_of_nat n 195 | 196 | @[always_inline] 197 | instance : Add ExtElem where add x y := { 198 | c0 := x.c0 + y.c0, 199 | c1 := x.c1 + y.c1, 200 | c2 := x.c2 + y.c2, 201 | c3 := x.c3 + y.c3 202 | } 203 | 204 | @[always_inline] 205 | instance : Neg ExtElem where neg x := { 206 | c0 := - x.c0, 207 | c1 := - x.c1, 208 | c2 := - x.c2, 209 | c3 := - x.c3 210 | } 211 | 212 | @[always_inline] 213 | instance : Sub ExtElem where sub x y := { 214 | c0 := x.c0 - y.c0, 215 | c1 := x.c1 - y.c1, 216 | c2 := x.c2 - y.c2, 217 | c3 := x.c3 - y.c3 218 | } 219 | 220 | @[always_inline, inline] 221 | def prim_ext_mul (a b: ExtElem): ExtElem 222 | := { 223 | c0 := a.c0 * b.c0 + NBETA * (a.c1 * b.c3 + a.c2 * b.c2 + a.c3 * b.c1), 224 | c1 := a.c0 * b.c1 + a.c1 * b.c0 + NBETA * (a.c2 * b.c3 + a.c3 * b.c2), 225 | c2 := a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0 + NBETA * (a.c3 * b.c3), 226 | c3 := a.c0 * b.c3 + a.c1 * b.c2 + a.c2 * b.c1 + a.c3 * b.c0 227 | } 228 | 229 | @[always_inline] 230 | instance : Mul ExtElem where mul x y := prim_ext_mul x y 231 | 232 | @[always_inline] 233 | instance : HPow ExtElem Nat ExtElem where hPow x n := prim_pow prim_ext_mul (prim_ext_of_nat 1) x n 234 | 235 | @[always_inline] 236 | instance : Ring ExtElem where ofNat n := prim_ext_of_nat n 237 | 238 | @[always_inline, inline] 239 | def prim_ext_inv (a: ExtElem): ExtElem := 240 | let b0 := a.c0 * a.c0 + BETA * (a.c1 * (a.c3 + a.c3) - a.c2 * a.c2) 241 | let b2 := a.c0 * (a.c2 + a.c2) - a.c1 * a.c1 + BETA * (a.c3 * a.c3) 242 | let c := b0 * b0 + BETA * b2 * b2 243 | let ic := Field.inv c 244 | let b0' := b0 * ic; 245 | let b2' := b2 * ic; 246 | { 247 | c0 := a.c0 * b0' + BETA * a.c2 * b2', 248 | c1 := -a.c1 * b0' + NBETA * a.c3 * b2', 249 | c2 := -a.c0 * b2' + a.c2 * b0', 250 | c3 := a.c1 * b2' - a.c3 * b0' 251 | } 252 | 253 | @[always_inline] 254 | instance : Div ExtElem where div x y := prim_ext_mul x (prim_ext_inv y) 255 | 256 | @[always_inline] 257 | instance : SerialUInt32 ExtElem where 258 | words := 4 259 | toUInt32Words x := #[x.c0.val, x.c1.val, x.c2.val, x.c3.val] 260 | fromUInt32Words x := { 261 | c0 := { val := x[0]! }, 262 | c1 := { val := x[1]! }, 263 | c2 := { val := x[2]! }, 264 | c3 := { val := x[3]! } 265 | } 266 | 267 | @[always_inline] 268 | instance ExtElem.Field : Field ExtElem where 269 | inv x := prim_ext_inv x 270 | random 271 | := do let c0: Elem <- Field.random 272 | let c1: Elem <- Field.random 273 | let c2: Elem <- Field.random 274 | let c3: Elem <- Field.random 275 | pure { c0, c1, c2, c3 } 276 | fromUInt64 x := prim_ext_of_nat x.toNat 277 | 278 | 279 | /- The extension is an algebra over the base -/ 280 | @[always_inline] 281 | instance ExtElem.Elem.Algebra : Algebra Elem ExtElem where 282 | ofBase c := prim_ext_of_base c 283 | ofBasis i x 284 | := match i with 285 | | 0 => { c0 := x, c1 := 0, c2 := 0, c3 := 0 } 286 | | 1 => { c0 := 0, c1 := x, c2 := 0, c3 := 0 } 287 | | 2 => { c0 := 0, c1 := 0, c2 := x, c3 := 0 } 288 | | 3 => { c0 := 0, c1 := 0, c2 := 0, c3 := x } 289 | | _ => 0 290 | 291 | @[always_inline] 292 | instance ExtElem.RootsOfUnity : RootsOfUnity ExtElem where 293 | MAX_ROU_SIZE := Elem.RootsOfUnity.MAX_ROU_SIZE 294 | ROU_FWD := Array.map Algebra.ofBase Elem.RootsOfUnity.ROU_FWD 295 | ROU_REV := Array.map Algebra.ofBase Elem.RootsOfUnity.ROU_REV 296 | 297 | @[always_inline] 298 | instance ExtElem.Elem.ExtField : ExtField Elem ExtElem where 299 | EXT_DEG := 4 300 | ofSubelems x := ExtElem.new x[0]! x[1]! x[2]! x[3]! 301 | 302 | 303 | end Zkvm.Algebra.BabyBear 304 | -------------------------------------------------------------------------------- /Zkvm/Algebra/Classes.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace Zkvm.Algebra.Classes 20 | 21 | open R0sy.Hash 22 | open R0sy.Serial 23 | 24 | class Ring (R: Type) 25 | extends 26 | ToString R, 27 | Inhabited R, 28 | BEq R, 29 | Add R, 30 | Neg R, 31 | Sub R, 32 | Mul R, 33 | HPow R Nat R 34 | where 35 | ofNat: Nat -> R 36 | zero: R := ofNat 0 37 | one: R := ofNat 1 38 | 39 | class Field (F: Type) 40 | extends 41 | Ring F, 42 | Div F, 43 | SerialUInt32 F 44 | where 45 | inv: F -> F 46 | random: [Monad M] -> [MonadRng M] -> M F 47 | fromUInt64: UInt64 -> F 48 | 49 | class PrimeField (F: Type) 50 | extends 51 | Field F 52 | where 53 | toNat: F -> Nat 54 | 55 | class RootsOfUnity (F: Type) where 56 | MAX_ROU_SIZE: Nat 57 | ROU_FWD: Array F 58 | ROU_REV: Array F 59 | 60 | class Algebra (F: Type) (R: Type) 61 | extends 62 | Ring R, 63 | HMul R F R 64 | where 65 | ofBase: F -> R 66 | ofBasis: Nat -> F -> R 67 | hMul := λ r f => (r * (ofBase f)) 68 | 69 | class ExtField (F R: Type) 70 | extends 71 | Algebra F R 72 | where 73 | EXT_DEG: Nat 74 | ofSubelems : Array F -> R 75 | 76 | class Algebraic (Elem ExtElem: Type) where 77 | prime_field: PrimeField Elem 78 | prime_rou: RootsOfUnity Elem 79 | ext_field: Field ExtElem 80 | ext_rou: RootsOfUnity ExtElem 81 | alg: Algebra Elem ExtElem 82 | ext: ExtField Elem ExtElem 83 | 84 | @[always_inline] 85 | instance [Algebraic Elem ExtElem] : PrimeField Elem := Algebraic.prime_field ExtElem 86 | 87 | @[always_inline] 88 | instance [Algebraic Elem ExtElem] : RootsOfUnity Elem := Algebraic.prime_rou ExtElem 89 | 90 | @[always_inline] 91 | instance [Algebraic Elem ExtElem] : Field ExtElem := Algebraic.ext_field Elem 92 | 93 | @[always_inline] 94 | instance [Algebraic Elem ExtElem] : RootsOfUnity ExtElem := Algebraic.ext_rou Elem 95 | 96 | @[always_inline] 97 | instance [Algebraic Elem ExtElem] : Algebra Elem ExtElem := Algebraic.alg 98 | 99 | @[always_inline] 100 | instance [Algebraic Elem ExtElem] : ExtField Elem ExtElem := Algebraic.ext 101 | 102 | def polyEval [Ring Elem] (coeffs: Subarray Elem) (x: Elem): Elem 103 | := Id.run do 104 | let mut out: Elem := Ring.zero 105 | let mut x_pow: Elem := Ring.one 106 | for coeff in coeffs do 107 | out := out + x_pow * coeff 108 | x_pow := x_pow * x 109 | pure out 110 | 111 | end Zkvm.Algebra.Classes 112 | -------------------------------------------------------------------------------- /Zkvm/Algebra/Ntt.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | 20 | namespace Zkvm.Algebra.Ntt 21 | 22 | open R0sy.Lean.Nat 23 | open Zkvm.Algebra.Classes 24 | 25 | def bit_rev_32 (in_x: UInt32): UInt32 26 | := Id.run do 27 | let mut x := in_x 28 | x := ((x &&& 0xaaaaaaaa) >>> 1) ||| ((x &&& 0x55555555) <<< 1) 29 | x := ((x &&& 0xcccccccc) >>> 2) ||| ((x &&& 0x33333333) <<< 2) 30 | x := ((x &&& 0xf0f0f0f0) >>> 4) ||| ((x &&& 0x0f0f0f0f) <<< 4) 31 | x := ((x &&& 0xff00ff00) >>> 8) ||| ((x &&& 0x00ff00ff) <<< 8) 32 | (x >>> 16) ||| (x <<< 16) 33 | 34 | def bit_reverse [Inhabited T] (io: Array T): Array T 35 | := Id.run do 36 | let mut out := io 37 | let n := Nat.log2_ceil io.size 38 | if 1 <<< n != io.size then panic! s!"n:{n} io.size:{io.size}" 39 | for i in [0:io.size] do 40 | let rev_idx := (bit_rev_32 i.toUInt32).toNat >>> (32 - n) 41 | if rev_idx >= out.size then panic! s!"n:{n} i:{i} rev_idx:{rev_idx} out.size:{out.size}" 42 | if i < rev_idx then do 43 | let x := out[i]! 44 | let y := out[rev_idx]! 45 | out := Array.set! out i y 46 | out := Array.set! out rev_idx x 47 | pure out 48 | 49 | instance [Add T] : Add (Array T) where add x y := Array.zipWith x y (λ a b => a + b) 50 | 51 | instance [Sub T] : Sub (Array T) where sub x y := Array.zipWith x y (λ a b => a - b) 52 | 53 | instance [Mul T] : Mul (Array T) where mul x y := Array.zipWith x y (λ a b => a * b) 54 | 55 | instance [Mul T] : HMul (Array T) T (Array T) where hMul x y := x.map (λ a => a * y) 56 | 57 | instance [HPow S T S] : HPow S (Array T) (Array S) where hPow x n := n.map (λ k => x ^ k) 58 | 59 | partial def fwd_butterfly [Field ExtElem] [RootsOfUnity ExtElem] (n expand_bits : Nat) (arr : Array ExtElem) : Array ExtElem := 60 | if n == 0 61 | then arr 62 | else if n == expand_bits 63 | then arr 64 | else 65 | let first_half : Array ExtElem := fwd_butterfly (n-1) expand_bits (arr.toSubarray 0 (2 ^ (n-1))).toArray 66 | let second_half : Array ExtElem := fwd_butterfly (n-1) expand_bits (arr.toSubarray (2 ^ (n-1)) (2 ^ n)).toArray 67 | let second_half' : Array ExtElem := 68 | second_half * 69 | ((RootsOfUnity.ROU_FWD[n]! : ExtElem) ^ ((List.range n).toArray) : Array ExtElem) 70 | (first_half + second_half') ++ (first_half - second_half') 71 | 72 | partial def rev_butterfly [Field ExtElem] [RootsOfUnity ExtElem] (n : Nat) (arr : Array ExtElem) : Array ExtElem := 73 | match n with 74 | | 0 => arr 75 | | n + 1 => 76 | let half := 1 <<< n 77 | let step: ExtElem := RootsOfUnity.ROU_REV[n + 1]! 78 | let a : Array ExtElem := arr.toSubarray 0 half 79 | let b : Array ExtElem := arr.toSubarray half (2 * half) 80 | let first_half : Array ExtElem := rev_butterfly n <| a + b 81 | let second_half : Array ExtElem := rev_butterfly n <| (a - b) * (step ^ ((List.range half).toArray) : Array ExtElem) 82 | first_half ++ second_half 83 | 84 | def interpolate_ntt [Field ExtElem] [RootsOfUnity ExtElem] (io : Array ExtElem) : Array ExtElem 85 | := Id.run do 86 | let n := Nat.log2_ceil io.size 87 | (rev_butterfly n io) * (Field.inv (Ring.ofNat io.size : ExtElem)) 88 | 89 | end Zkvm.Algebra.Ntt 90 | -------------------------------------------------------------------------------- /Zkvm/ArithVM/AST.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Taps 20 | 21 | namespace Zkvm.ArithVM.AST 22 | 23 | open R0sy.ByteDeserial 24 | open Zkvm.Algebra.Classes 25 | 26 | structure MixState (ExtElem: Type) where 27 | tot: ExtElem 28 | mul: ExtElem 29 | deriving Inhabited 30 | 31 | structure Arg where 32 | rep: UInt32 33 | 34 | def Arg.byteRead [Monad M] [MonadByteReader M]: M Arg 35 | := do let rep <- MonadByteReader.readUInt32le 36 | pure { rep } 37 | 38 | structure Var where 39 | rep: UInt32 40 | 41 | def Var.byteRead [Monad M] [MonadByteReader M]: M Var 42 | := do let rep <- MonadByteReader.readUInt32le 43 | pure { rep } 44 | 45 | inductive PolyExtStep where 46 | | Const: UInt32 -> PolyExtStep 47 | | Get: UInt32 -> PolyExtStep 48 | | GetGlobal: Arg -> UInt32 -> PolyExtStep 49 | | Add: Var -> Var -> PolyExtStep 50 | | Sub: Var -> Var -> PolyExtStep 51 | | Mul: Var -> Var -> PolyExtStep 52 | | True: PolyExtStep 53 | | AndEqz: Var -> Var -> PolyExtStep 54 | | AndCond: Var -> Var -> Var -> PolyExtStep 55 | 56 | def PolyExtStep.byteRead [Monad M] [MonadByteReader M]: M PolyExtStep 57 | := do let opcode <- MonadByteReader.readUInt32le 58 | match opcode with 59 | | 1 => do let val <- MonadByteReader.readUInt32le 60 | pure (PolyExtStep.Const val) 61 | | 2 => do let val <- MonadByteReader.readUInt32le 62 | pure (PolyExtStep.Get val) 63 | | 3 => do let base <- Arg.byteRead 64 | let offset <- MonadByteReader.readUInt32le 65 | pure (PolyExtStep.GetGlobal base offset) 66 | | 4 => do let x1 <- Var.byteRead 67 | let x2 <- Var.byteRead 68 | pure (PolyExtStep.Add x1 x2) 69 | | 5 => do let x1 <- Var.byteRead 70 | let x2 <- Var.byteRead 71 | pure (PolyExtStep.Sub x1 x2) 72 | | 6 => do let x1 <- Var.byteRead 73 | let x2 <- Var.byteRead 74 | pure (PolyExtStep.Mul x1 x2) 75 | | 7 => pure PolyExtStep.True 76 | | 8 => do let x <- Var.byteRead 77 | let val <- Var.byteRead 78 | pure (PolyExtStep.AndEqz x val) 79 | | 9 => do let x <- Var.byteRead 80 | let cond <- Var.byteRead 81 | let inner <- Var.byteRead 82 | pure (PolyExtStep.AndCond x cond inner) 83 | | _ => throw ByteReaderError.InvalidData 84 | 85 | def PolyExtStep.step [Field Elem] [Field ExtElem] [Algebra Elem ExtElem] (self: PolyExtStep) (fp_vars: Array ExtElem) (mix_vars: Array (MixState ExtElem)) (mix: ExtElem) (u: Array ExtElem) (args: Array (Array Elem)): Array ExtElem × Array (MixState ExtElem) 86 | := match self with 87 | | Const val => 88 | let elem: Elem := Ring.ofNat val.toNat 89 | let fp_vars' := fp_vars.push (Algebra.ofBase elem) 90 | (fp_vars', mix_vars) 91 | | Get tap => (fp_vars.push u[tap.toNat]!, mix_vars) 92 | | GetGlobal base offset => 93 | let fp_vars' := fp_vars.push (Algebra.ofBase args[base.rep.toNat]![offset.toNat]!) 94 | (fp_vars', mix_vars) 95 | | Add x1 x2 => 96 | let fp_vars' := fp_vars.push (fp_vars[x1.rep.toNat]! + fp_vars[x2.rep.toNat]!) 97 | (fp_vars', mix_vars) 98 | | Sub x1 x2 => 99 | let fp_vars' := fp_vars.push (fp_vars[x1.rep.toNat]! - fp_vars[x2.rep.toNat]!) 100 | (fp_vars', mix_vars) 101 | | Mul x1 x2 => 102 | let fp_vars' := fp_vars.push (fp_vars[x1.rep.toNat]! * fp_vars[x2.rep.toNat]!) 103 | (fp_vars', mix_vars) 104 | | True => 105 | let mix_vars' := mix_vars.push { 106 | tot := Ring.zero 107 | mul := Ring.one 108 | } 109 | (fp_vars, mix_vars') 110 | | AndEqz x val => 111 | let x := mix_vars[x.rep.toNat]! 112 | let val := fp_vars[val.rep.toNat]! 113 | let mix_vars' := mix_vars.push { 114 | tot := x.tot + x.mul * val 115 | mul := x.mul * mix 116 | } 117 | (fp_vars, mix_vars') 118 | | AndCond x cond inner => 119 | let x := mix_vars[x.rep.toNat]! 120 | let cond := fp_vars[cond.rep.toNat]! 121 | let inner := mix_vars[inner.rep.toNat]! 122 | let mix_vars' := mix_vars.push { 123 | tot := x.tot + cond * inner.tot * x.mul 124 | mul := x.mul * inner.mul 125 | } 126 | (fp_vars, mix_vars') 127 | 128 | 129 | structure PolyExtStepDef where 130 | block: Array PolyExtStep 131 | ret: Var 132 | 133 | def PolyExtStepDef.byteRead [Monad M] [MonadByteReader M]: M PolyExtStepDef 134 | := do let block <- MonadByteReader.readArray PolyExtStep.byteRead 135 | let ret <- Var.byteRead 136 | pure { block, ret } 137 | 138 | def PolyExtStepDef.run [Field Elem] [Field ExtElem] [Algebra Elem ExtElem] (self: PolyExtStepDef) (mix: ExtElem) (u: Array ExtElem) (args: Array (Array Elem)): MixState ExtElem := 139 | Id.run do let mut fp_vars: Array ExtElem := Array.mkEmpty (self.block.size - (self.ret.rep.toNat + 1)) 140 | let mut mix_vars: Array (MixState ExtElem) := Array.mkEmpty (self.ret.rep.toNat + 1) 141 | for op in self.block do 142 | (fp_vars, mix_vars) := PolyExtStep.step op fp_vars mix_vars mix u args 143 | pure mix_vars[self.ret.rep.toNat]! 144 | 145 | end Zkvm.ArithVM.AST 146 | -------------------------------------------------------------------------------- /Zkvm/ArithVM/Circuit.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.Algebra.BabyBear 20 | import Zkvm.ArithVM.AST 21 | import Zkvm.ArithVM.Taps 22 | import Zkvm.Constants 23 | 24 | namespace Zkvm.ArithVM.Circuit 25 | 26 | open R0sy.ByteDeserial 27 | open Zkvm.Algebra 28 | open Zkvm.Algebra.Classes 29 | open Zkvm.ArithVM.AST 30 | open Zkvm.ArithVM.Taps 31 | 32 | 33 | inductive CircuitField where 34 | | BabyBear 35 | 36 | @[always_inline, inline] 37 | def CircuitField.Elem: CircuitField -> Type 38 | | .BabyBear => BabyBear.Elem 39 | 40 | @[always_inline, inline] 41 | def CircuitField.ExtElem: CircuitField -> Type 42 | | .BabyBear => BabyBear.ExtElem 43 | 44 | @[always_inline] 45 | instance : Algebraic (CircuitField.Elem f) (CircuitField.ExtElem f) where 46 | prime_field := 47 | match f with 48 | | .BabyBear => BabyBear.Elem.PrimeField 49 | prime_rou := 50 | match f with 51 | | .BabyBear => BabyBear.Elem.RootsOfUnity 52 | ext_field := 53 | match f with 54 | | .BabyBear => BabyBear.ExtElem.Field 55 | ext_rou := 56 | match f with 57 | | .BabyBear => BabyBear.ExtElem.RootsOfUnity 58 | alg := 59 | match f with 60 | | .BabyBear => BabyBear.ExtElem.Elem.Algebra 61 | ext := 62 | match f with 63 | | .BabyBear => BabyBear.ExtElem.Elem.ExtField 64 | 65 | 66 | structure Circuit where 67 | field: CircuitField 68 | output_size: Nat 69 | mix_size: Nat 70 | taps: TapSet 71 | polydef: PolyExtStepDef 72 | 73 | def Circuit.byteRead [Monad M] [MonadByteReader M] (field: CircuitField): M Circuit 74 | := do let output_size <- MonadByteReader.readUInt32le >>= (fun x => pure <| x.toNat) 75 | let mix_size <- MonadByteReader.readUInt32le >>= (fun x => pure <| x.toNat) 76 | let taps <- TapSet.byteRead 77 | let polydef <- PolyExtStepDef.byteRead 78 | pure { 79 | field, 80 | output_size, 81 | mix_size, 82 | taps, 83 | polydef 84 | } 85 | 86 | def Circuit.ofFile (field: CircuitField) (filename: System.FilePath): IO Circuit 87 | := do let meta <- filename.metadata 88 | let byteSize := meta.byteSize 89 | let handle <- IO.FS.Handle.mk filename IO.FS.Mode.read 90 | let bytes <- handle.read (byteSize.toNat.toUSize) 91 | let result := R0sy.ByteDeserial.ByteReader.run (Circuit.byteRead field) bytes.data.toSubarray 92 | match result with 93 | | Except.ok circuit => pure circuit 94 | | Except.error error => panic! s!"ERROR: {error}" 95 | 96 | def Circuit.check_size (self: Circuit): Nat := Constants.INV_RATE * (ExtField.EXT_DEG self.field.Elem self.field.ExtElem) 97 | 98 | def Circuit.poly_ext (self: Circuit) (mix: self.field.ExtElem) (u: Array self.field.ExtElem) (args: Array (Array self.field.Elem)): MixState self.field.ExtElem 99 | := PolyExtStepDef.run self.polydef mix u args 100 | 101 | structure TapCache (ExtElem: Type) where 102 | taps: TapSet 103 | check_size: Nat 104 | tap_mix_pows: Array ExtElem 105 | check_mix_pows: Array ExtElem 106 | 107 | def Circuit.tap_cache (self: Circuit) (mix: self.field.ExtElem): TapCache self.field.ExtElem 108 | := Id.run do 109 | let mut cur_mix := Ring.one 110 | let mut tap_mix_pows := Array.mkEmpty self.taps.reg_count.toNat 111 | for _ in self.taps.regIter do 112 | tap_mix_pows := tap_mix_pows.push cur_mix 113 | cur_mix := cur_mix * mix 114 | let mut check_mix_pows := Array.mkEmpty self.check_size 115 | for _ in [0:self.check_size] do 116 | check_mix_pows := check_mix_pows.push cur_mix 117 | cur_mix := cur_mix * mix 118 | pure { 119 | taps := self.taps, 120 | check_size := self.check_size, 121 | tap_mix_pows, 122 | check_mix_pows 123 | } 124 | 125 | def TapCache.eval_taps 126 | [Algebraic Elem ExtElem] 127 | (tap_cache: TapCache ExtElem) 128 | (combo_u: Array ExtElem) 129 | (back_one: Elem) 130 | (z: ExtElem) 131 | (rows: Array (Array Elem)) 132 | (check_row: Array Elem) 133 | (x: ExtElem) 134 | : ExtElem 135 | := Id.run do 136 | let combos_count := tap_cache.taps.combos_count.toNat 137 | let mut tot := Array.mkArray (combos_count + 1) Ring.zero 138 | -- Tap group 139 | let mut tap_cache_idx := 0 140 | for reg in tap_cache.taps.regIter do 141 | let idx := reg.combo_id 142 | let val := tot[idx]! + tap_cache.tap_mix_pows[tap_cache_idx]! * rows[reg.group.toNat]![reg.offset]! 143 | tot := Array.set! tot idx val 144 | tap_cache_idx := tap_cache_idx + 1 145 | -- Check group 146 | for i in [0:tap_cache.check_size] do 147 | tot := Array.setD tot combos_count (tot[combos_count]! + tap_cache.check_mix_pows[i]! * check_row[i]!) 148 | -- Compute the return value 149 | let mut ret := Ring.zero 150 | for i in [0:combos_count] do 151 | let start := tap_cache.taps.combo_begin[i]!.toNat 152 | let stop := tap_cache.taps.combo_begin[i + 1]!.toNat 153 | let poly: Subarray ExtElem := combo_u.toSubarray start stop 154 | let mut divisor := Ring.one 155 | for back in (tap_cache.taps.getCombo i).slice do 156 | divisor := divisor * (x - z * back_one ^ back.toNat) 157 | ret := ret + (tot[i]! - polyEval poly x) / divisor 158 | let check_num := tot[combos_count]! - combo_u[tap_cache.taps.tot_combo_backs.toNat]! 159 | let check_div := x - z ^ Constants.INV_RATE 160 | ret := ret + check_num / check_div 161 | pure ret 162 | 163 | end Zkvm.ArithVM.Circuit 164 | -------------------------------------------------------------------------------- /Zkvm/ArithVM/Taps.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace Zkvm.ArithVM.Taps 20 | 21 | open R0sy.ByteDeserial 22 | 23 | /- Register groups -/ 24 | 25 | inductive RegisterGroup where 26 | | Accum 27 | | Code 28 | | Data 29 | deriving Inhabited 30 | 31 | def RegisterGroup.toNat (x: RegisterGroup): Nat 32 | := match x with 33 | | RegisterGroup.Accum => 0 34 | | RegisterGroup.Code => 1 35 | | RegisterGroup.Data => 2 36 | 37 | def RegisterGroup.byteRead [Monad M] [MonadByteReader M]: M RegisterGroup 38 | := do let val <- MonadByteReader.readUInt16le 39 | match val with 40 | | 0 => pure RegisterGroup.Accum 41 | | 1 => pure RegisterGroup.Code 42 | | 2 => pure RegisterGroup.Data 43 | | _ => throw ByteReaderError.InvalidData 44 | 45 | def REGISTER_GROUPS: Array RegisterGroup := #[ 46 | RegisterGroup.Accum, 47 | RegisterGroup.Code, 48 | RegisterGroup.Data 49 | ] 50 | 51 | 52 | /- TapData -/ 53 | 54 | structure TapData where 55 | offset: UInt16 56 | back: UInt16 57 | group: RegisterGroup 58 | combo: UInt8 59 | skip: UInt8 60 | deriving Inhabited 61 | 62 | def TapData.byteRead [Monad M] [MonadByteReader M]: M TapData 63 | := do let offset <- MonadByteReader.readUInt16le 64 | let back <- MonadByteReader.readUInt16le 65 | let group <- RegisterGroup.byteRead 66 | let combo <- MonadByteReader.readUInt8 67 | let skip <- MonadByteReader.readUInt8 68 | pure { 69 | offset, 70 | back, 71 | group, 72 | combo, 73 | skip 74 | } 75 | 76 | 77 | /- TapIter -/ 78 | 79 | structure TapIter where 80 | iter_data: Array TapData 81 | iter_cursor: Nat 82 | iter_end: Nat 83 | 84 | 85 | /- RegRef -/ 86 | 87 | structure RegRef where 88 | data: Array TapData 89 | cursor: Nat 90 | 91 | def RegRef.group (self: RegRef): RegisterGroup := self.data[self.cursor]!.group 92 | 93 | def RegRef.offset (self: RegRef): Nat := self.data[self.cursor]!.offset.toNat 94 | 95 | def RegRef.combo_id (self: RegRef): Nat := self.data[self.cursor]!.combo.toNat 96 | 97 | def RegRef.size (self: RegRef): Nat := self.data[self.cursor]!.skip.toNat 98 | 99 | def RegRef.back (self: RegRef) (i: Nat): Nat := self.data[self.cursor + i]!.back.toNat 100 | 101 | def RegRef.into_iter (self: RegRef): TapIter := { 102 | iter_data := self.data 103 | iter_cursor := self.cursor 104 | iter_end := self.cursor + RegRef.size self 105 | } 106 | 107 | 108 | /- RegIter -/ 109 | 110 | structure RegIter where 111 | iter_data: Array TapData 112 | iter_cursor: Nat 113 | iter_end: Nat 114 | 115 | partial def RegIter.forIn [Monad M] [Inhabited X] (self: RegIter) (x: X) (f: RegRef -> X -> M (ForInStep X)): M X 116 | := do let cursor := self.iter_cursor 117 | if cursor >= self.iter_data.size then return x 118 | let next := cursor + self.iter_data[cursor]!.skip.toNat 119 | if next > self.iter_end then return x 120 | let step <- f { data := self.iter_data, cursor } x 121 | match step with 122 | | ForInStep.done x' => pure x' 123 | | ForInStep.yield x' => RegIter.forIn { self with iter_cursor := next } x' f 124 | 125 | instance : ForIn M RegIter RegRef where 126 | forIn iter b f := @RegIter.forIn _ _ _ { default := b } iter b f 127 | 128 | 129 | /- Combo -/ 130 | 131 | structure ComboData where 132 | taps: Array UInt16 133 | offsets: Array UInt16 134 | 135 | 136 | structure ComboIter where 137 | iter_data: ComboData 138 | iter_id: Nat 139 | iter_end: Nat 140 | 141 | 142 | structure ComboRef where 143 | data: ComboData 144 | id: Nat 145 | 146 | def ComboRef.self_offset (self: ComboRef): Nat 147 | := self.data.offsets[self.id]!.toNat 148 | 149 | def ComboRef.next_offset (self: ComboRef): Nat 150 | := self.data.offsets[self.id + 1]!.toNat 151 | 152 | def ComboRef.slice (self: ComboRef): Subarray UInt16 153 | := self.data.taps.toSubarray self.self_offset self.next_offset 154 | 155 | 156 | /- TapSet -/ 157 | 158 | structure TapSet where 159 | taps: Array TapData 160 | combo_taps: Array UInt16 161 | combo_begin: Array UInt16 162 | group_begin: Array UInt32 163 | combos_count: UInt32 164 | reg_count: UInt32 165 | tot_combo_backs: UInt32 166 | deriving Inhabited 167 | 168 | def TapSet.byteRead [Monad M] [MonadByteReader M]: M TapSet 169 | := do let taps <- MonadByteReader.readArray TapData.byteRead 170 | let combo_taps <- MonadByteReader.readUInt16le_array 171 | let combo_begin <- MonadByteReader.readUInt16le_array 172 | let group_begin <- MonadByteReader.readUInt32le_array 173 | let combos_count <- MonadByteReader.readUInt32le 174 | let reg_count <- MonadByteReader.readUInt32le 175 | let tot_combo_backs <- MonadByteReader.readUInt32le 176 | pure { 177 | taps, 178 | combo_taps, 179 | combo_begin, 180 | group_begin, 181 | combos_count, 182 | reg_count, 183 | tot_combo_backs 184 | } 185 | 186 | def TapSet.tapSize (self: TapSet): Nat := self.group_begin[REGISTER_GROUPS.size]!.toNat 187 | 188 | def TapSet.tapIter (self: TapSet): TapIter := { 189 | iter_data := self.taps 190 | iter_cursor := 0 191 | iter_end := self.group_begin[REGISTER_GROUPS.size]!.toNat 192 | } 193 | 194 | def TapSet.regIter (self: TapSet): RegIter := { 195 | iter_data := self.taps 196 | iter_cursor := 0 197 | iter_end := self.group_begin[REGISTER_GROUPS.size]!.toNat 198 | } 199 | 200 | def TapSet.groupSize (self: TapSet) (group: RegisterGroup): Nat := 201 | let group_id := RegisterGroup.toNat group 202 | let idx := (self.group_begin[group_id + 1]! - 1).toNat 203 | let last := self.taps[idx]!.offset.toNat 204 | last + 1 205 | 206 | def TapSet.groupTapIter (self: TapSet) (group: RegisterGroup): TapIter := 207 | let group_id := RegisterGroup.toNat group 208 | { 209 | iter_data := self.taps 210 | iter_cursor := self.group_begin[group_id]!.toNat 211 | iter_end := self.group_begin[group_id + 1]!.toNat 212 | } 213 | 214 | def TapSet.groupRegIter (self: TapSet) (group: RegisterGroup): RegIter := 215 | let group_id := RegisterGroup.toNat group 216 | { 217 | iter_data := self.taps 218 | iter_cursor := self.group_begin[group_id]!.toNat 219 | iter_end := self.group_begin[group_id + 1]!.toNat 220 | } 221 | 222 | def TapSet.combosIter (self: TapSet): ComboIter := { 223 | iter_data := { 224 | taps := self.combo_taps 225 | offsets := self.combo_begin 226 | } 227 | iter_id := 0 228 | iter_end := self.combos_count.toNat 229 | } 230 | 231 | def TapSet.getCombo (self: TapSet) (id: Nat): ComboRef := { 232 | data := { 233 | taps := self.combo_taps 234 | offsets := self.combo_begin 235 | } 236 | id 237 | } 238 | 239 | end Zkvm.ArithVM.Taps 240 | -------------------------------------------------------------------------------- /Zkvm/Constants.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace Zkvm.Constants 20 | 21 | open R0sy.Lean.Nat 22 | 23 | def MIN_CYCLES_PO2: Nat := 10 24 | 25 | def MIN_CYCLES: Nat := 1 <<< MIN_CYCLES_PO2 -- 1K 26 | 27 | def MAX_CYCLES_PO2: Nat := 24 28 | 29 | def MAX_CYCLES: Nat := 1 <<< MAX_CYCLES_PO2 -- 16M 30 | 31 | /- ~100 bits of conjectured security -/ 32 | def QUERIES: Nat := 50 33 | 34 | def ZK_CYCLES: Nat := QUERIES 35 | 36 | def MIN_PO2: Nat := Nat.log2_ceil (1 + ZK_CYCLES) 37 | 38 | def INV_RATE: Nat := 4 39 | 40 | def FRI_FOLD_PO2: Nat := 4 41 | 42 | def FRI_FOLD: Nat := 1 <<< FRI_FOLD_PO2 43 | 44 | def FRI_MIN_DEGREE: Nat := 256 45 | 46 | end Zkvm.Constants 47 | -------------------------------------------------------------------------------- /Zkvm/MainEmu.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV 18 | import Zkvm 19 | 20 | open RiscV.Monad 21 | 22 | def main : IO Unit 23 | := do let filename := "rust/output/hw.bin" 24 | IO.println s!"Loading {filename}" 25 | let result <- Elf.ofFile filename 26 | let elf 27 | <- match result with 28 | | Except.ok elf => pure elf 29 | | Except.error error 30 | => do IO.println s!"Error: {error}" 31 | return () 32 | IO.println s!"Creating initial machine state" 33 | let initialMachine 34 | <- match Zkvm.Platform.Elf.loadElf elf with 35 | | Except.ok mach => pure mach 36 | | Except.error exception 37 | => do IO.println s!"Error: {exception}" 38 | return () 39 | for block in initialMachine.mem.blocks do 40 | IO.println s!"Memory block: {R0sy.Data.Hex.UInt32.toHex block.base.toUInt32} - {R0sy.Data.Hex.UInt32.toHex block.limit.toUInt32}" 41 | IO.println s!"Running ..." 42 | let isa := RiscV.ISA.RV32IM.ISA 43 | let debug: Bool := false 44 | let maxClock := 16 <<< 20 45 | let (result, machine) <- initialMachine.run do 46 | for clock in [0:maxClock] do 47 | if debug 48 | then do let machine <- MonadMachine.getMachine 49 | let pc <- MonadMachine.getReg .PC 50 | let instr <- tryCatch (MonadMachine.fetchWord pc) (fun _ => pure 0) 51 | monadLift <| IO.println s!"Register File: {machine.reg_file}" 52 | monadLift <| IO.println s!"Next instr: {R0sy.Data.Hex.UInt32.toHex instr} {isa.decode_to_string instr}" 53 | monadLift <| IO.println s!"" 54 | if clock % (64 <<< 10) == 0 then IO.println s!"... clock {clock}" 55 | /- Run the next instruction -/ 56 | let result <- 57 | tryCatch (isa.step >>= fun _ => pure none) 58 | <| fun exception 59 | => do IO.println s!"Exception at clock: {clock}" 60 | pure (some exception) 61 | match result with 62 | | none => pure () 63 | | some exception => throw exception 64 | match result with 65 | | Except.ok _ => IO.println s!"Ended normally after {maxClock} clock cycles" 66 | | Except.error exception => IO.println s!"Ended with exception: {exception}" 67 | IO.println s!"Final register file: {machine.reg_file}" 68 | -------------------------------------------------------------------------------- /Zkvm/MainVerify.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm 19 | 20 | open R0sy.Lean.ByteArray 21 | 22 | def read_file (filename : System.FilePath): IO (Array UInt32) 23 | := do let meta <- filename.metadata 24 | let byteSize := meta.byteSize 25 | let handle <- IO.FS.Handle.mk filename IO.FS.Mode.read 26 | let bytes <- handle.read (byteSize.toNat.toUSize) 27 | pure (ByteArray.to_le32 bytes) 28 | 29 | def check_seal (circuit: Zkvm.ArithVM.Circuit.Circuit) (base_name: String): IO Unit 30 | := do IO.println s!"Checking {base_name} ..." 31 | let id <- read_file s!"{base_name}.id" 32 | let journal <- read_file s!"{base_name}.journal" 33 | let seal <- read_file s!"{base_name}.seal" 34 | IO.println s!"ID size: {id.size} words" 35 | IO.println s!"Journal size: {journal.size} words" 36 | IO.println s!"Seal size: {seal.size} words" 37 | let method_id := Zkvm.MethodId.MethodId.ofWords R0sy.Hash.Sha2.Sha256.Digest id.toSubarray 38 | let result := Zkvm.Verify.ReadIop.ReadIop.run seal (Zkvm.Verify.verify circuit method_id journal) 39 | match result with 40 | | Except.ok _ => IO.println "Seal is OK" 41 | | Except.error error => IO.println s!"ERROR: {error}" 42 | IO.println "" 43 | 44 | def read_circuit (filename : System.FilePath): IO Zkvm.ArithVM.Circuit.Circuit 45 | := do IO.println s!"Reading circuit ..." 46 | let circuit <- Zkvm.ArithVM.Circuit.Circuit.ofFile .BabyBear filename 47 | IO.println s!"output_size: {circuit.output_size}" 48 | IO.println s!"mix_size: {circuit.mix_size}" 49 | IO.println s!"TapSet size: {circuit.taps.taps.size}" 50 | IO.println s!"Step size: {circuit.polydef.block.size}" 51 | IO.println "" 52 | pure circuit 53 | 54 | def main : IO Unit 55 | := do -- Read the circuit 56 | let circuit <- read_circuit "rust/output/riscv.circuit" 57 | -- Check a seal 58 | check_seal circuit "rust/output/hello_world" 59 | check_seal circuit "rust/output/hw" 60 | -------------------------------------------------------------------------------- /Zkvm/MethodId.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | 19 | namespace Zkvm.MethodId 20 | 21 | open R0sy.Hash 22 | open R0sy.Lean.Subarray 23 | open R0sy.Serial 24 | 25 | structure MethodId (D: Type) where 26 | table: Array D 27 | deriving Inhabited 28 | 29 | def MethodId.ofWords (D: Type) [Hash D] (words: Subarray UInt32): MethodId D 30 | := Id.run do 31 | let num_digest := words.size / SerialUInt32.words D 32 | let mut buf := words 33 | let mut table: Array D := Array.mkEmpty num_digest 34 | for _ in [0:num_digest] do 35 | let digest_buf := Subarray.take buf (SerialUInt32.words D) 36 | buf := digest_buf.2 37 | table := table.push <| SerialUInt32.fromUInt32Words digest_buf.1 38 | pure { table } 39 | 40 | end Zkvm.MethodId 41 | -------------------------------------------------------------------------------- /Zkvm/Platform/Elf.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV 18 | import Zkvm.Platform.Mem 19 | 20 | namespace Zkvm.Platform.Elf 21 | 22 | open RiscV.Monad 23 | 24 | def loadElf: Elf -> Except RiscV.Mach.Exception.RiscVException MachineState 25 | := RiscV.Elf.loadElfInfo 26 | { 27 | reg_file := RiscV.Mach.Reg.RegFile.new, 28 | mem := Zkvm.Platform.Mem.emptyMem 29 | } 30 | 31 | end Zkvm.Platform.Elf 32 | -------------------------------------------------------------------------------- /Zkvm/Platform/Mem.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import RiscV 18 | 19 | namespace Zkvm.Platform.Mem 20 | 21 | open RiscV.Mach.Mem 22 | 23 | structure Region where 24 | base: Nat 25 | size_in_bytes: Nat 26 | 27 | def Region.zeroBlock (self: Region): Block 28 | := { 29 | base := self.base, 30 | data := Array.mkArray self.size_in_bytes 0 31 | } 32 | 33 | def STACK: Region 34 | := { 35 | base := 0x00000000, 36 | size_in_bytes := 16 <<< 20 37 | } 38 | 39 | def DATA: Region 40 | := { 41 | base := 0x01000000, 42 | size_in_bytes := 16 <<< 20 43 | } 44 | 45 | def HEAP: Region 46 | := { 47 | base := 0x02000000, 48 | size_in_bytes := 32 <<< 20 49 | } 50 | 51 | def INPUT: Region 52 | := { 53 | base := 0x04000000, 54 | size_in_bytes := 32 <<< 20 55 | } 56 | 57 | def PROG: Region 58 | := { 59 | base := 0x06000000, 60 | size_in_bytes := 32 <<< 20 61 | } 62 | 63 | def OUTPUT: Region 64 | := { 65 | base := 0x08000000, 66 | size_in_bytes := 32 <<< 20 67 | } 68 | 69 | def COMMIT: Region 70 | := { 71 | base := 0x0A000000, 72 | size_in_bytes := 32 <<< 20 73 | } 74 | 75 | def SYSTEM: Region 76 | := { 77 | base := 0x0C000000, 78 | size_in_bytes := 64 <<< 20 79 | } 80 | 81 | def emptyMem: Mem 82 | := { 83 | endian := .Little, 84 | blocks := #[ 85 | STACK.zeroBlock, 86 | DATA.zeroBlock, 87 | HEAP.zeroBlock, 88 | INPUT.zeroBlock, 89 | PROG.zeroBlock, 90 | OUTPUT.zeroBlock, 91 | COMMIT.zeroBlock, 92 | SYSTEM.zeroBlock 93 | ] 94 | } 95 | 96 | end Zkvm.Platform.Mem 97 | -------------------------------------------------------------------------------- /Zkvm/Seal/CheckCommitments.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Circuit 20 | import Zkvm.ArithVM.Taps 21 | import Zkvm.Seal.Header 22 | import Zkvm.Seal.TraceCommitments 23 | import Zkvm.Verify.Error 24 | import Zkvm.Verify.Merkle 25 | import Zkvm.Verify.ReadIop 26 | 27 | namespace Zkvm.Seal.CheckCommitments 28 | 29 | open R0sy.Hash 30 | open Zkvm.Algebra.Classes 31 | open Zkvm.ArithVM.Circuit 32 | open Zkvm.ArithVM.Taps 33 | open Zkvm.Verify.Error 34 | open Zkvm.Verify.Merkle 35 | open Zkvm.Verify.ReadIop 36 | 37 | 38 | def compute_u 39 | (circuit: Circuit) 40 | (header: Header.Header circuit.field.Elem) 41 | (z: circuit.field.ExtElem) 42 | (coeff_u: Array circuit.field.ExtElem) 43 | (mix: circuit.field.ExtElem) 44 | (args: Array (Array circuit.field.Elem)) 45 | : circuit.field.ExtElem 46 | := Id.run do 47 | let mut cur_pos := 0 48 | let mut eval_u := Array.mkEmpty (TapSet.tapSize circuit.taps) 49 | for reg in TapSet.regIter circuit.taps do 50 | let reg_size := RegRef.size reg 51 | for i in [0:reg_size] do 52 | let x := z * Algebra.ofBase (header.back_one ^ (RegRef.back reg i)) 53 | let poly: Subarray circuit.field.ExtElem := coeff_u.toSubarray cur_pos (cur_pos + reg_size) 54 | let fx := polyEval poly x 55 | eval_u := eval_u.push fx 56 | cur_pos := cur_pos + reg_size 57 | pure (circuit.poly_ext mix eval_u args).tot 58 | 59 | def compute_check_u 60 | [Algebraic Elem ExtElem] 61 | (header: Header.Header Elem) 62 | (num_taps: Nat) 63 | (z: ExtElem) 64 | (coeff_u: Array ExtElem) 65 | : ExtElem 66 | := Id.run do 67 | -- Generate the check polynomial 68 | let ext0: ExtElem := Algebra.ofBasis 0 (Ring.one : Elem) 69 | let ext1: ExtElem := Algebra.ofBasis 1 (Ring.one : Elem) 70 | let ext2: ExtElem := Algebra.ofBasis 2 (Ring.one : Elem) 71 | let ext3: ExtElem := Algebra.ofBasis 3 (Ring.one : Elem) 72 | let remap := #[0, 2, 1, 3] 73 | let mut check: ExtElem := Ring.zero 74 | for i in [0:4] do 75 | let rmi := remap[i]! 76 | let zpi := z ^ i 77 | check := check + ext0 * coeff_u[num_taps + rmi + 0]! * zpi 78 | check := check + ext1 * coeff_u[num_taps + rmi + 4]! * zpi 79 | check := check + ext2 * coeff_u[num_taps + rmi + 8]! * zpi 80 | check := check + ext3 * coeff_u[num_taps + rmi + 12]! * zpi 81 | pure (check * ((Ring.ofNat 3 * z) ^ header.size - Ring.one)) 82 | 83 | 84 | structure CheckCommitments (D ExtElem: Type) where 85 | check_merkle: MerkleTreeVerifier D 86 | z: ExtElem 87 | coeff_u: Array ExtElem 88 | 89 | def read_and_commit 90 | (D: Type) 91 | [Monad M] 92 | [MonadReadIop M] 93 | [MonadCommitIop D M] 94 | [MonadExceptOf VerificationError M] 95 | [Hash D] 96 | (circuit: Circuit) 97 | (header: Header.Header circuit.field.Elem) 98 | (trace_commitments_mix: Array circuit.field.Elem) 99 | : M (CheckCommitments D circuit.field.ExtElem) 100 | := do let poly_mix: circuit.field.ExtElem <- Field.random 101 | let check_merkle <- MerkleTreeVerifier.read_and_commit header.domain circuit.check_size Constants.QUERIES 102 | let z: circuit.field.ExtElem <- Field.random 103 | let num_taps := TapSet.tapSize circuit.taps 104 | -- One 105 | let coeff_u <- MonadReadIop.readFields circuit.field.ExtElem (num_taps + circuit.check_size) 106 | -- Verifier manually computes CheckPoly evaluation from tapset (TODO check logic) 107 | let result := compute_u circuit header z coeff_u poly_mix #[header.output, trace_commitments_mix] 108 | -- Verifier computes CheckPoly evaluation from purported u coefficients (TODO check logic) 109 | let check := compute_check_u header num_taps z coeff_u 110 | -- Returns an error if there's a mismatch between the two values of CheckPoly above 111 | if check != result then throw (VerificationError.CheckPolyMismatch (ToString.toString result) (ToString.toString check)) 112 | let h_coeff: D := Hash.hash_pod coeff_u 113 | MonadCommitIop.commit h_coeff 114 | pure { 115 | check_merkle, 116 | z, 117 | coeff_u, 118 | } 119 | 120 | 121 | def CheckCommitments.compute_combos 122 | [Field ExtElem] 123 | (self: CheckCommitments.CheckCommitments D ExtElem) 124 | (tap_cache: TapCache ExtElem) 125 | : Array ExtElem 126 | := Id.run do 127 | let mut combo_u: Array ExtElem := Array.mkArray (tap_cache.taps.tot_combo_backs.toNat + 1) Ring.zero 128 | let mut cur_pos := 0 129 | -- Tap group 130 | let mut tap_cache_idx := 0 131 | for reg in TapSet.regIter tap_cache.taps do 132 | let reg_size := RegRef.size reg 133 | for i in [0:reg_size] do 134 | let idx := tap_cache.taps.combo_begin[reg.combo_id]!.toNat + i 135 | let val := combo_u[idx]! + tap_cache.tap_mix_pows[tap_cache_idx]! * self.coeff_u[cur_pos + i]! 136 | combo_u := Array.set! combo_u idx val 137 | tap_cache_idx := tap_cache_idx + 1 138 | cur_pos := cur_pos + reg_size 139 | -- Check group 140 | for i in [0:tap_cache.check_size] do 141 | let idx := tap_cache.taps.tot_combo_backs.toNat 142 | let val := combo_u[idx]! + tap_cache.check_mix_pows[i]! * self.coeff_u[cur_pos]! 143 | combo_u := Array.set! combo_u idx val 144 | cur_pos := cur_pos + 1 145 | pure combo_u 146 | 147 | end Zkvm.Seal.CheckCommitments 148 | -------------------------------------------------------------------------------- /Zkvm/Seal/Fri.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | 18 | import R0sy 19 | import Zkvm.Algebra.Classes 20 | import Zkvm.Algebra.Ntt 21 | import Zkvm.ArithVM.Circuit 22 | import Zkvm.Constants 23 | import Zkvm.Verify.Error 24 | import Zkvm.Verify.Merkle 25 | import Zkvm.Verify.ReadIop 26 | 27 | namespace Zkvm.Seal.Fri 28 | 29 | open R0sy.Lean.Subarray 30 | open R0sy.Lean.Nat 31 | open R0sy.Hash 32 | open Zkvm.Algebra.Classes 33 | open Zkvm.Algebra.Ntt 34 | open Zkvm.ArithVM.Circuit 35 | open Zkvm.Constants 36 | open Zkvm.Verify.Error 37 | open Zkvm.Verify.Merkle 38 | open Zkvm.Verify.ReadIop 39 | 40 | 41 | -- Takes a array of `T`s of length (outersize * inner_size) 42 | -- and returns a list of outer_size lists of `T`s, each sublist having inner_size elements 43 | -- Adjacent elems do not go in the same sublist 44 | def collate [Inhabited T] (arr : Array T) (outer_size inner_size : Nat) : (Array (Array T)) 45 | := ((List.range outer_size).map (λ i => ((List.range inner_size).map (λ j => arr[outer_size * j + i]!)).toArray)).toArray 46 | -- (arr.toList.toChunks outer_size).transpose.toArray -- Uses Std 47 | 48 | 49 | structure FriGoalState (ExtElem: Type) where 50 | pos: Nat 51 | goal: ExtElem 52 | deriving Inhabited 53 | 54 | namespace FriGoalState 55 | def run [Monad M] [Inhabited ExtElem] (f: StateT (FriGoalState ExtElem) M X): M X 56 | := StateT.run' f Inhabited.default 57 | 58 | def get_pos [Monad M]: StateT (FriGoalState ExtElem) M Nat 59 | := do let self <- get 60 | pure self.pos 61 | 62 | def set_pos [Monad M] (pos: Nat): StateT (FriGoalState ExtElem) M Unit 63 | := do let self <- get 64 | set { self with pos } 65 | 66 | def get_goal [Monad M]: StateT (FriGoalState ExtElem) M ExtElem 67 | := do let self <- get 68 | pure self.goal 69 | 70 | def set_goal [Monad M] (goal: ExtElem): StateT (FriGoalState ExtElem) M Unit 71 | := do let self <- get 72 | set { self with goal } 73 | end FriGoalState 74 | 75 | 76 | structure FriRoundVerifier (D ExtElem: Type) where 77 | domain: Nat 78 | merkle: MerkleTreeVerifier D 79 | mix: ExtElem 80 | 81 | namespace FriRoundVerifier 82 | def read_and_commit [Monad M] [MonadReadIop M] [MonadCommitIop D M] [Hash D] (circuit: Circuit) (in_domain: Nat) : M (FriRoundVerifier D circuit.field.ExtElem) 83 | := do let domain := in_domain / FRI_FOLD 84 | let merkle <- MerkleTreeVerifier.read_and_commit domain (FRI_FOLD * ExtField.EXT_DEG circuit.field.Elem circuit.field.ExtElem) QUERIES 85 | let mix : circuit.field.ExtElem <- Field.random 86 | pure { 87 | domain, 88 | merkle, 89 | mix 90 | } 91 | 92 | def verify (Elem ExtElem: Type) [Monad M] [MonadReadIop M] [Hash D] [MonadExceptOf VerificationError M] [Algebraic Elem ExtElem] (self: FriRoundVerifier D ExtElem) : StateT (FriGoalState ExtElem) M Unit 93 | := do let pos <- FriGoalState.get_pos 94 | let goal <- FriGoalState.get_goal 95 | -- 96 | let quot := pos / self.domain 97 | let group := pos % self.domain 98 | let data : Array Elem <- MonadLift.monadLift (self.merkle.verify group: M (Array Elem)) 99 | -- collect field elements into groups of size EXT_SIZE 100 | let collate_data : Array (Array Elem) := collate data FRI_FOLD (ExtField.EXT_DEG Elem ExtElem) 101 | let data_ext : Array ExtElem := collate_data.map ExtField.ofSubelems 102 | -- Returns error if there's a mismatch from one round of FRI to the next 103 | if data_ext[quot]! != goal then throw VerificationError.FRICommitRoundMismatch 104 | let root_po2 : Nat := Nat.log2_ceil (FRI_FOLD * self.domain) 105 | let inv_wk : Elem := (RootsOfUnity.ROU_REV[root_po2]! : Elem) ^ group 106 | -- Track the states of the mutable arguments 107 | FriGoalState.set_pos group 108 | let new_goal := polyEval (bit_reverse (interpolate_ntt data_ext)).toSubarray (self.mix * inv_wk) 109 | FriGoalState.set_goal new_goal 110 | pure () 111 | end FriRoundVerifier 112 | 113 | 114 | structure FriVerifier (D Elem ExtElem: Type) where 115 | orig_domain: Nat 116 | domain: Nat 117 | rounds: Array (FriRoundVerifier D ExtElem) 118 | final_coeffs: Array Elem 119 | poly: Subarray ExtElem 120 | 121 | def read_and_commit 122 | (D: Type) 123 | [Monad M] 124 | [MonadReadIop M] 125 | [MonadCommitIop D M] 126 | [MonadExceptOf VerificationError M] 127 | [Hash D] 128 | (circuit: Circuit) (in_degree : Nat): M (FriVerifier D circuit.field.Elem circuit.field.ExtElem) 129 | := do let mut degree := in_degree 130 | let orig_domain := INV_RATE * in_degree 131 | let mut domain := orig_domain 132 | -- Prep the folding verfiers 133 | let rounds_capacity := ((Nat.log2_ceil ((in_degree + FRI_FOLD - 1) / FRI_FOLD)) + FRI_FOLD_PO2 - 1) / FRI_FOLD_PO2 -- this is just for performance in the rust 134 | let mut rounds := Array.mkEmpty rounds_capacity 135 | while degree > FRI_MIN_DEGREE do 136 | let round <- FriRoundVerifier.read_and_commit circuit domain 137 | rounds := rounds.push round 138 | domain := domain / FRI_FOLD 139 | degree := degree / FRI_FOLD 140 | let final_coeffs <- MonadReadIop.readFields circuit.field.Elem (ExtField.EXT_DEG circuit.field.Elem circuit.field.ExtElem * degree) 141 | let collate_final_coeffs := collate final_coeffs degree (ExtField.EXT_DEG circuit.field.Elem circuit.field.ExtElem) 142 | let poly: Subarray circuit.field.ExtElem := (collate_final_coeffs.map ExtField.ofSubelems).toSubarray 143 | let h_coeffs: D := Hash.hash_pod final_coeffs 144 | MonadCommitIop.commit h_coeffs 145 | pure { 146 | orig_domain, 147 | domain, 148 | rounds, 149 | final_coeffs, 150 | poly 151 | } 152 | 153 | def verify [Monad M] [MonadReadIop M] [MonadExceptOf VerificationError M] [Hash D] [Algebraic Elem ExtElem] (fri_verify_params: FriVerifier D Elem ExtElem) (inner : Nat -> M ExtElem) : M Unit 154 | := do -- // Get the generator for the final polynomial evaluations 155 | let gen : Elem := RootsOfUnity.ROU_FWD[Nat.log2_ceil (fri_verify_params.domain)]! 156 | -- // Do queries 157 | FriGoalState.run do 158 | for query_no in [0:QUERIES] do 159 | let rng: UInt32 <- MonadLift.monadLift (MonadRng.nextUInt32: M UInt32) 160 | let pos_val := rng.toNat % fri_verify_params.orig_domain 161 | FriGoalState.set_pos pos_val 162 | inner pos_val >>= FriGoalState.set_goal 163 | -- // Verify the per-round proofs 164 | for round in fri_verify_params.rounds do 165 | FriRoundVerifier.verify Elem ExtElem round 166 | -- // Do final verification 167 | let x : Elem := gen ^ (<- FriGoalState.get_pos) 168 | -- TODO clarify logic 169 | let goal <- FriGoalState.get_goal 170 | -- TODO clarify logic 171 | let actual : ExtElem := polyEval fri_verify_params.poly (Algebra.ofBase x) 172 | -- Returns an error if there's a mismatch between the verifier-computed evaluation of the FRI polynomial and the FRI query on the seal 173 | if actual != goal then throw (VerificationError.FriGoalMismatch query_no s!"{goal}" s!"{actual}") 174 | return () 175 | 176 | end Zkvm.Seal.Fri 177 | -------------------------------------------------------------------------------- /Zkvm/Seal/Header.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Circuit 20 | import Zkvm.Verify.Error 21 | import Zkvm.Verify.ReadIop 22 | 23 | namespace Zkvm.Seal.Header 24 | 25 | open R0sy.Hash 26 | open R0sy.Lean.Nat 27 | open R0sy.Lean.UInt32 28 | open R0sy.Serial 29 | open Zkvm.Algebra.Classes 30 | open Zkvm.ArithVM.Circuit 31 | open Zkvm.Verify.Error 32 | open Zkvm.Verify.ReadIop 33 | 34 | structure Header (Elem: Type) where 35 | po2: Nat 36 | size: Nat 37 | domain: Nat 38 | fri_gen: Elem 39 | back_one: Elem 40 | output: Array Elem 41 | deserialized_output: Array UInt32 42 | 43 | 44 | def read [Monad M] [MonadReadIop M] (circuit: Circuit): M (Header circuit.field.Elem) 45 | := do let output <- MonadReadIop.readFields circuit.field.Elem circuit.output_size 46 | let mut deserialized_output := Array.mkEmpty (output.size / 2) 47 | for i in [0:output.size / 2] do 48 | let hi := PrimeField.toNat output[2 * i + 1]! 49 | let lo := PrimeField.toNat output[2 * i]! 50 | deserialized_output := deserialized_output.push ((hi <<< 16) ||| lo).toUInt32 51 | let po2 <- MonadReadIop.readU32s 1 >>= (fun x => pure <| x[0]!.toNat) 52 | let size := 1 <<< po2 53 | let domain := Constants.INV_RATE * size 54 | let fri_gen := RootsOfUnity.ROU_FWD[Nat.log2_ceil (domain)]! 55 | let back_one := RootsOfUnity.ROU_REV[po2]! 56 | pure { 57 | po2, 58 | size, 59 | domain, 60 | fri_gen, 61 | back_one, 62 | output, 63 | deserialized_output 64 | } 65 | 66 | def verify_journal_size [Monad M] [MonadExceptOf VerificationError M] [PrimeField Elem] (self: Header Elem) (journal: Array UInt32): M Unit 67 | := do let output_len_idx := self.deserialized_output.size - 1 68 | let output_len := self.deserialized_output[output_len_idx]!.toNat 69 | let journal_len := journal.size * 4 70 | if output_len != journal_len 71 | -- Returns error if there's a mismatch between the length of the journal and the purported journal-length on the seal 72 | then throw (VerificationError.JournalLengthMismatch output_len journal_len) 73 | 74 | def verify_journal (D: Type) [Monad M] [MonadExceptOf VerificationError M] [PrimeField Elem] [Hash D] (self: Header Elem) (journal: Array UInt32): M Unit 75 | := do verify_journal_size self journal 76 | let journal 77 | := if journal.size <= SerialUInt32.words D 78 | then journal 79 | else SerialUInt32.toUInt32Words (Hash.hash_pod journal: D) 80 | for i in [0:journal.size] do 81 | let s := self.deserialized_output[i]! 82 | let j := journal[i]! 83 | -- Returns error if there's a mismatch between the journal on the receipt and the purported journal-hash on the seal (TODO confirm logic) 84 | if j != s then throw (VerificationError.JournalHashMismatch i s j) 85 | pure () 86 | 87 | end Zkvm.Seal.Header 88 | -------------------------------------------------------------------------------- /Zkvm/Seal/TraceCommitments.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Circuit 20 | import Zkvm.ArithVM.Taps 21 | import Zkvm.MethodId 22 | import Zkvm.Seal.Header 23 | import Zkvm.Verify.Error 24 | import Zkvm.Verify.Merkle 25 | import Zkvm.Verify.ReadIop 26 | 27 | namespace Zkvm.Seal.TraceCommitments 28 | 29 | open R0sy.Hash 30 | open Zkvm.Algebra.Classes 31 | open Zkvm.ArithVM.Circuit 32 | open Zkvm.ArithVM.Taps 33 | open Zkvm.MethodId 34 | open Zkvm.Verify.Error 35 | open Zkvm.Verify.Merkle 36 | open Zkvm.Verify.ReadIop 37 | 38 | 39 | structure TraceCommitments (D Elem: Type) where 40 | code_merkle: MerkleTreeVerifier D 41 | data_merkle: MerkleTreeVerifier D 42 | accum_merkle: MerkleTreeVerifier D 43 | mix: Array Elem 44 | 45 | def check_code_root 46 | [Monad M] 47 | [MonadExceptOf VerificationError M] 48 | [Hash D] 49 | [RootsOfUnity Elem] 50 | (method_id: MethodId D) 51 | (header: Header.Header Elem) 52 | (code_merkle: MerkleTreeVerifier D) 53 | : M Unit 54 | := do let which := header.po2 - Constants.MIN_CYCLES_PO2 55 | -- Returns error if zkvm execution was shorter than min PO2 56 | if which >= method_id.table.size then throw (VerificationError.TooFewCycles header.po2) 57 | -- Returns error if Merkle commitment for control tree (aka code tree) mismatches with the appropriate row of MethodID table 58 | if method_id.table[which]! != MerkleTreeVerifier.root code_merkle then throw VerificationError.MethodIDMismatch 59 | pure () 60 | 61 | def get_mix [Monad M] [MonadReadIop M] (circuit: Circuit): M (Array circuit.field.Elem) 62 | := do let mut mix := Array.mkEmpty circuit.mix_size 63 | for _ in [0:circuit.mix_size] do 64 | let x <- Field.random 65 | mix := mix.push x 66 | pure mix 67 | 68 | def read_and_commit 69 | (D: Type) 70 | [Monad M] 71 | [MonadReadIop M] 72 | [MonadCommitIop D M] 73 | [MonadExceptOf VerificationError M] 74 | [Hash D] 75 | (circuit: Circuit) 76 | (header: Header.Header circuit.field.Elem) 77 | (method_id: MethodId D) 78 | : M (TraceCommitments D circuit.field.Elem) 79 | := do let code_merkle <- MerkleTreeVerifier.read_and_commit header.domain (TapSet.groupSize circuit.taps RegisterGroup.Code) Constants.QUERIES 80 | check_code_root method_id header code_merkle 81 | let data_merkle <- MerkleTreeVerifier.read_and_commit header.domain (TapSet.groupSize circuit.taps RegisterGroup.Data) Constants.QUERIES 82 | let mix <- get_mix circuit 83 | let accum_merkle <- MerkleTreeVerifier.read_and_commit header.domain (TapSet.groupSize circuit.taps RegisterGroup.Accum) Constants.QUERIES 84 | pure { 85 | code_merkle, 86 | data_merkle, 87 | accum_merkle, 88 | mix 89 | } 90 | 91 | end Zkvm.Seal.TraceCommitments 92 | -------------------------------------------------------------------------------- /Zkvm/Verify.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Circuit 20 | import Zkvm.Constants 21 | import Zkvm.MethodId 22 | import Zkvm.Seal.CheckCommitments 23 | import Zkvm.Seal.Fri 24 | import Zkvm.Seal.Header 25 | import Zkvm.Seal.TraceCommitments 26 | import Zkvm.Verify.Error 27 | import Zkvm.Verify.ReadIop 28 | 29 | namespace Zkvm.Verify 30 | 31 | open R0sy.Hash 32 | open R0sy.Lean.Nat 33 | open Zkvm.Algebra.Classes 34 | open Zkvm.ArithVM.Circuit 35 | open Zkvm.MethodId 36 | open Zkvm.Seal 37 | open Zkvm.Verify.Error 38 | open Zkvm.Verify.ReadIop 39 | 40 | 41 | def verify [Hash D] (circuit: Circuit) (method_id: MethodId D) (journal: Array UInt32) {M: Type -> Type} [Monad M] [MonadExceptOf VerificationError M] [MonadReadIop M] [MonadCommitIop D M]: M Unit 42 | := do -- Read the header and verify the journal 43 | let header <- Header.read circuit 44 | Header.verify_journal D header journal 45 | -- Returns error if zkvm execution exceeds cycle limit 46 | if header.po2 > Constants.MAX_CYCLES_PO2 then throw (VerificationError.TooManyCycles header.po2 Constants.MAX_CYCLES_PO2) 47 | -- Read the trace commitments and set entropy for generating constraint batching randomness (alpha_constraints) 48 | let trace_commitments <- TraceCommitments.read_and_commit D circuit header method_id 49 | -- Read the validity (aka checkpoly) commitments and set entropy for generating random DEEP query point (z) 50 | let check_commitments <- CheckCommitments.read_and_commit D circuit header trace_commitments.mix 51 | -- FRI verify 52 | let alpha_FRI: circuit.field.ExtElem <- Field.random 53 | let tap_cache := circuit.tap_cache alpha_FRI 54 | let combo_u := check_commitments.compute_combos tap_cache 55 | let fri_verify_params <- Fri.read_and_commit D circuit header.size 56 | Fri.verify fri_verify_params (fun idx 57 | => do let rows: Array (Array circuit.field.Elem) := #[ 58 | <- trace_commitments.accum_merkle.verify idx, 59 | <- trace_commitments.code_merkle.verify idx, 60 | <- trace_commitments.data_merkle.verify idx 61 | ] 62 | let check_row: Array circuit.field.Elem <- check_commitments.check_merkle.verify idx 63 | pure <| tap_cache.eval_taps 64 | combo_u 65 | header.back_one 66 | check_commitments.z 67 | rows 68 | check_row 69 | (Algebra.ofBase (header.fri_gen ^ idx)) 70 | ) 71 | -- Ensure proof buffer is empty 72 | MonadReadIop.verifyComplete 73 | 74 | end Zkvm.Verify 75 | -------------------------------------------------------------------------------- /Zkvm/Verify/Error.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | namespace Zkvm.Verify.Error 18 | 19 | inductive VerificationError where 20 | | MethodIDMismatch 21 | | TooFewCycles (required: Nat) 22 | | TooManyCycles (po2 max_po2: Nat) 23 | | MerkleQueryOutOfRange (idx: Nat) (rows: Nat) 24 | | FRICommitRoundMismatch 25 | | MerkleBranchMismatch 26 | | CheckPolyMismatch (result check: String) 27 | | JournalHashMismatch (idx: Nat) (seal: UInt32) (journal: UInt32) 28 | | JournalLengthMismatch (seal_len: Nat) (journal_len: Nat) 29 | | FriGoalMismatch (query_no: Nat) (goal actual: String) 30 | | ReadIopIncomplete (words_remaining: Nat) 31 | deriving Repr 32 | 33 | instance : ToString VerificationError where 34 | toString error 35 | := match error with 36 | | .MethodIDMismatch => s!"MethodIDMismatch" 37 | | .TooFewCycles required => s!"TooFewCycles required:{required}" 38 | | .TooManyCycles po2 max_po2 => s!"TooManycycles po2:{po2} max_po2:{max_po2}" 39 | | .MerkleQueryOutOfRange idx rows => s!"MerkleQueryOutOfRange idx:{idx} rows:{rows}" 40 | | .FRICommitRoundMismatch => s!"FRICommitRoundMismatch" 41 | | .MerkleBranchMismatch => s!"MerkleBranchMismatch" 42 | | .CheckPolyMismatch result check => s!"CheckPolyMismatch result:{result} check:{check}" 43 | | .JournalHashMismatch idx seal journal => s!"JournalHashMismatch idx:{idx} seal:{seal} journal:{journal}" 44 | | .JournalLengthMismatch seal_len journal_len => s!"JournalLengthMismatch seal_len:{seal_len} journal_len:{journal_len}" 45 | | .FriGoalMismatch query_no goal actual => s!"FriGoalMismatch query_no:{query_no} goal:{goal} actual:{actual}" 46 | | .ReadIopIncomplete words_remaining => s!"ReadIopIncomplete words_remaining:{words_remaining}" 47 | 48 | end Zkvm.Verify.Error 49 | -------------------------------------------------------------------------------- /Zkvm/Verify/Merkle.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.Verify.Error 20 | import Zkvm.Verify.ReadIop 21 | 22 | namespace Zkvm.Verify.Merkle 23 | 24 | open R0sy.Hash 25 | open R0sy.Lean.Nat 26 | open Zkvm.Algebra.Classes 27 | open Zkvm.Verify.Error 28 | open Zkvm.Verify.ReadIop 29 | 30 | 31 | structure MerkleTreeParams where 32 | row_size: Nat 33 | col_size: Nat 34 | queries: Nat 35 | layers: Nat 36 | top_layer: Nat 37 | top_size: Nat 38 | deriving Inhabited 39 | 40 | def MerkleTreeParams.new (row_size col_size queries: Nat): MerkleTreeParams := 41 | let layers: Nat := Nat.log2_floor row_size 42 | let top_layer: Nat := Nat.log2_floor queries 43 | let top_size: Nat := 1 <<< top_layer 44 | if 1 <<< layers != row_size 45 | then panic "row_size not a power of 2" 46 | else 47 | { 48 | row_size, 49 | col_size, 50 | queries, 51 | layers, 52 | top_layer, 53 | top_size 54 | } 55 | 56 | def MerkleTreeParams.idx_to_top (self: MerkleTreeParams) (idx: Nat): Nat := idx - self.top_size 57 | 58 | def MerkleTreeParams.idx_to_rest (_self: MerkleTreeParams) (idx: Nat): Nat := idx - 1 59 | 60 | namespace Examples 61 | 62 | def ex_1: MerkleTreeParams := MerkleTreeParams.new 1024 1234 50 63 | 64 | #eval ex_1.layers == 10 65 | #eval ex_1.top_layer == 5 66 | #eval ex_1.top_size == 32 67 | 68 | def ex_2: MerkleTreeParams := MerkleTreeParams.new 2048 31337 128 69 | 70 | #eval ex_2.layers == 11 71 | #eval ex_2.top_layer == 7 72 | #eval ex_2.top_size == 128 73 | 74 | end Examples 75 | 76 | 77 | structure MerkleTreeVerifier (D: Type) where 78 | params: MerkleTreeParams 79 | top: Array D 80 | rest: Array D 81 | 82 | namespace MerkleTreeVerifier 83 | def root [Inhabited D] (self: MerkleTreeVerifier D): D 84 | := if self.rest.size == 0 85 | then self.top[MerkleTreeParams.idx_to_top self.params 1]! 86 | else self.rest[MerkleTreeParams.idx_to_rest self.params 1]! 87 | 88 | partial def fillUpperRest [Hash D] (params: MerkleTreeParams) (top out: Array D) (fr to: Nat) (i: Nat := fr - 1): Array D 89 | := if i < to 90 | then out 91 | else 92 | let top_idx := MerkleTreeParams.idx_to_top params (2 * i) 93 | let out_idx := MerkleTreeParams.idx_to_rest params i 94 | let out' := Array.set! out out_idx (Hash.hash_pair top[top_idx]! top[top_idx + 1]!) 95 | fillUpperRest params top out' fr to (i - 1) 96 | 97 | partial def fillLowerRest [Hash D] (params: MerkleTreeParams) (out: Array D) (fr to: Nat) (i: Nat := fr - 1): Array D 98 | := if i < to 99 | then out 100 | else 101 | let upper_rest_idx := MerkleTreeParams.idx_to_rest params (2 * i) 102 | let out_idx := MerkleTreeParams.idx_to_rest params i 103 | let out' := Array.set! out out_idx (Hash.hash_pair out[upper_rest_idx]! out[upper_rest_idx + 1]!) 104 | fillLowerRest params out' fr to (i - 1) 105 | 106 | 107 | def read_and_commit [Monad M] [MonadReadIop M] [MonadCommitIop D M] [Hash D] (row_size col_size queries: Nat): M (MerkleTreeVerifier D) 108 | := do let params := MerkleTreeParams.new row_size col_size queries 109 | let top <- MonadReadIop.readPodSlice D params.top_size 110 | let mut rest := Array.mkArray (params.top_size - 1) Inhabited.default 111 | rest := fillUpperRest params top rest params.top_size (params.top_size / 2) 112 | rest := fillLowerRest params rest (params.top_size / 2) 1 113 | let verifier: MerkleTreeVerifier D := { 114 | params, 115 | top, 116 | rest 117 | } 118 | MonadCommitIop.commit (root verifier) 119 | pure verifier 120 | 121 | 122 | def verify [Monad M] [MonadReadIop M] [MonadExceptOf VerificationError M] [Hash D] [Field Elem] 123 | (self: MerkleTreeVerifier D) (base_idx : Nat) : M (Array Elem) 124 | := do let mut idx := base_idx 125 | if idx >= self.params.row_size then throw (VerificationError.MerkleQueryOutOfRange idx self.params.row_size) 126 | let out <- MonadReadIop.readFields Elem self.params.col_size 127 | let mut cur := Hash.hash_pod out 128 | idx := idx + self.params.row_size 129 | while idx >= 2 * self.params.top_size do 130 | let low_bit := idx % 2 131 | let otherArray <- MonadReadIop.readPodSlice D 1 132 | let other := otherArray[0]! 133 | idx := idx / 2 134 | if low_bit == 1 135 | then cur := Hash.hash_pair other cur 136 | else cur := Hash.hash_pair cur other 137 | let present_hash := 138 | if idx >= self.params.top_size 139 | then self.top[self.params.idx_to_top idx]! 140 | else self.top[self.params.idx_to_rest idx]! 141 | -- Returns an error if there's a mismatch in a Merkle branch 142 | if present_hash != cur then throw VerificationError.MerkleBranchMismatch 143 | pure out 144 | end MerkleTreeVerifier 145 | 146 | end Zkvm.Verify.Merkle 147 | -------------------------------------------------------------------------------- /Zkvm/Verify/ReadIop.lean: -------------------------------------------------------------------------------- 1 | /- 2 | Copyright 2023 RISC Zero, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -/ 16 | 17 | import R0sy 18 | import Zkvm.Algebra.Classes 19 | import Zkvm.ArithVM.Circuit 20 | import Zkvm.Verify.Error 21 | 22 | namespace Zkvm.Verify.ReadIop 23 | 24 | open R0sy.Lean.Subarray 25 | open R0sy.Hash 26 | open R0sy.Hash.Sha2 27 | open R0sy.Serial 28 | open Zkvm.Algebra.Classes 29 | open Zkvm.ArithVM.Circuit 30 | open Zkvm.Verify.Error 31 | 32 | 33 | class MonadReadIop (M: Type -> Type) 34 | extends 35 | MonadRng M 36 | where 37 | readU32s: Nat -> M (Subarray UInt32) 38 | readPodSlice (X: Type): [SerialUInt32 X] -> Nat -> M (Array X) 39 | readFields (F: Type): [Field F] -> Nat -> M (Array F) 40 | verifyComplete: M Unit 41 | 42 | class MonadCommitIop (D: Type) (M: Type -> Type) 43 | where 44 | commit: D -> M Unit 45 | 46 | structure ReadIop where 47 | proof: Subarray UInt32 48 | rng: Sha256.Rng 49 | 50 | 51 | namespace ReadIop 52 | instance [Monad M] [MonadStateOf ReadIop M] : MonadStateOf Sha256.Rng M where 53 | get 54 | := do let self <- get 55 | return self.rng 56 | set rng 57 | := do let self <- get 58 | set { self with rng } 59 | modifyGet f 60 | := do let self <- get 61 | let (result, rng) := f self.rng 62 | set { self with rng } 63 | return result 64 | 65 | 66 | def new (proof: Subarray UInt32): ReadIop := { 67 | proof, 68 | rng := Sha256.Rng.new 69 | } 70 | 71 | def readU32s [Monad M] [MonadStateOf ReadIop M] (n: Nat): M (Subarray UInt32) 72 | := do let read_iop <- get 73 | let (result, proof) := Subarray.take read_iop.proof n 74 | set { read_iop with proof } 75 | return result 76 | 77 | def readPodSlice [Monad M] [MonadStateOf ReadIop M] (F: Type) [SerialUInt32 F] (n: Nat): M (Array F) 78 | := do let mut out: Array F := Array.mkEmpty n 79 | for _ in [0:n] do 80 | let u32s <- readU32s (SerialUInt32.words F) 81 | let f: F := SerialUInt32.fromUInt32Words u32s 82 | out := out.push f 83 | pure out 84 | 85 | def commit [Monad M] [MonadStateOf ReadIop M] (digest: Sha256.Digest): M Unit 86 | := Sha256.Rng.mix digest 87 | 88 | def verifyComplete [Monad M] [MonadExceptOf VerificationError M] [MonadStateOf ReadIop M]: M Unit 89 | := do let self <- get 90 | if 0 < self.proof.size then throw (VerificationError.ReadIopIncomplete self.proof.size) 91 | 92 | 93 | instance [Monad M] [MonadExceptOf VerificationError M] [MonadStateOf ReadIop M] : MonadReadIop M where 94 | readU32s := ReadIop.readU32s 95 | readPodSlice X := ReadIop.readPodSlice X 96 | readFields F := ReadIop.readPodSlice F 97 | verifyComplete := ReadIop.verifyComplete 98 | 99 | instance [Monad M] [MonadExceptOf VerificationError M] [MonadStateOf ReadIop M] : MonadCommitIop Sha256.Digest M where 100 | commit := ReadIop.commit 101 | 102 | def run (seal: Array UInt32) (f: {M: Type -> Type} -> [Monad M] -> [MonadExceptOf VerificationError M] -> [MonadReadIop M] -> [MonadCommitIop Sha256.Digest M] -> M Unit): Except VerificationError Unit 103 | := Id.run do 104 | let M := StateT ReadIop (ExceptT VerificationError Id) 105 | ExceptT.run (StateT.run' (@f M _ _ _ _) (new seal.toSubarray)) 106 | end ReadIop 107 | 108 | end Zkvm.Verify.ReadIop 109 | -------------------------------------------------------------------------------- /lake-manifest.json: -------------------------------------------------------------------------------- 1 | {"version": 4, 2 | "packagesDir": "./lake-packages", 3 | "packages": 4 | [{"git": 5 | {"url": "https://github.com/xubaiw/CMark.lean", 6 | "subDir?": null, 7 | "rev": "2cc7cdeef67184f84db6731450e4c2b258c28fe8", 8 | "name": "CMark", 9 | "inputRev?": "main"}}, 10 | {"git": 11 | {"url": "https://github.com/leanprover/lake", 12 | "subDir?": null, 13 | "rev": "d0b530530f14dde97a547b03abf87eee06360d60", 14 | "name": "lake", 15 | "inputRev?": "master"}}, 16 | {"git": 17 | {"url": "https://github.com/leanprover/doc-gen4", 18 | "subDir?": null, 19 | "rev": "bdf803b1002786b87da7b8db8d2746c0002a715f", 20 | "name": "doc-gen4", 21 | "inputRev?": "main"}}, 22 | {"git": 23 | {"url": "https://github.com/mhuisi/lean4-cli", 24 | "subDir?": null, 25 | "rev": "5a858c32963b6b19be0d477a30a1f4b6c120be7e", 26 | "name": "Cli", 27 | "inputRev?": "nightly"}}, 28 | {"git": 29 | {"url": "https://github.com/leanprover-community/mathlib4.git", 30 | "subDir?": null, 31 | "rev": "3cd4748a8bef6db519783d443962eda019266e36", 32 | "name": "mathlib", 33 | "inputRev?": null}}, 34 | {"git": 35 | {"url": "https://github.com/gebner/quote4", 36 | "subDir?": null, 37 | "rev": "7ac99aa3fac487bec1d5860e751b99c7418298cf", 38 | "name": "Qq", 39 | "inputRev?": "master"}}, 40 | {"git": 41 | {"url": "https://github.com/gebner/aesop", 42 | "subDir?": null, 43 | "rev": "6f04ed73886f455a8ec41fbbae21ef6b870c61ff", 44 | "name": "aesop", 45 | "inputRev?": "master"}}, 46 | {"git": 47 | {"url": "https://github.com/hargonix/LeanInk", 48 | "subDir?": null, 49 | "rev": "2447df5cc6e48eb965c3c3fba87e46d353b5e9f1", 50 | "name": "leanInk", 51 | "inputRev?": "doc-gen"}}, 52 | {"git": 53 | {"url": "https://github.com/xubaiw/Unicode.lean", 54 | "subDir?": null, 55 | "rev": "6dd6ae3a3839c8350a91876b090eda85cf538d1d", 56 | "name": "Unicode", 57 | "inputRev?": "main"}}, 58 | {"git": 59 | {"url": "https://github.com/leanprover/std4", 60 | "subDir?": null, 61 | "rev": "a109889bf299f0d086f93a228e4c152f40ff5b0d", 62 | "name": "std", 63 | "inputRev?": "main"}}]} 64 | -------------------------------------------------------------------------------- /lakefile.lean: -------------------------------------------------------------------------------- 1 | import Lake 2 | open Lake DSL 3 | 4 | package «risc0-lean4» { 5 | -- add package configuration options here 6 | } 7 | 8 | require mathlib from git 9 | "https://github.com/leanprover-community/mathlib4.git" 10 | 11 | @[default_target] 12 | lean_lib Elf { 13 | -- add library configuration options here 14 | } 15 | 16 | @[default_target] 17 | lean_exe «elf-dump-lean4» { 18 | root := `Elf.Main 19 | } 20 | 21 | @[default_target] 22 | lean_lib R0sy { 23 | -- add library configuration options here 24 | } 25 | 26 | @[default_target] 27 | lean_lib RiscV { 28 | -- add library configuration options here 29 | } 30 | 31 | @[default_target] 32 | lean_lib Zkvm { 33 | -- add library configuration options here 34 | } 35 | 36 | lean_lib Soundness 37 | 38 | @[default_target] 39 | lean_exe «zkvm-verify-lean4» { 40 | root := `Zkvm.MainVerify 41 | } 42 | 43 | @[default_target] 44 | lean_exe «zkvm-emu-lean4» { 45 | root := `Zkvm.MainEmu 46 | } 47 | 48 | meta if get_config? doc = some "on" then -- do not download and build doc-gen4 by default 49 | require «doc-gen4» from git "https://github.com/leanprover/doc-gen4" @ "main" 50 | -------------------------------------------------------------------------------- /lean-toolchain: -------------------------------------------------------------------------------- 1 | leanprover/lean4:nightly-2022-12-23 -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "circuit", 4 | "hello_world", 5 | "hw", 6 | ] 7 | 8 | [workspace.package] 9 | version = "0.1.0" 10 | edition = "2021" 11 | 12 | [workspace.dependencies] 13 | serde = { version = "1.0", default-features = false, features = ["derive"] } 14 | snafu = { version = "0.7", default-features = false } 15 | 16 | [workspace.dependencies.risc0-circuit-rv32im] 17 | version = "1.0.0-rc.2" 18 | git = "ssh://git@github.com/risc0/risc0.git" 19 | 20 | [workspace.dependencies.risc0-build] 21 | version = "1.0.0-rc.2" 22 | git = "ssh://git@github.com/risc0/risc0.git" 23 | 24 | [workspace.dependencies.risc0-zkvm] 25 | version = "1.0.0-rc.2" 26 | git = "ssh://git@github.com/risc0/risc0.git" 27 | default-features = false 28 | 29 | [workspace.dependencies.risc0-zkp] 30 | version = "1.0.0-rc.2" 31 | git = "ssh://git@github.com/risc0/risc0.git" 32 | default-features = false 33 | 34 | [profile.dev] 35 | opt-level = 3 36 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | # hello-world 2 | 3 | Generates a receipt for a simple "hello world" guest, then serializes to a Lean file. 4 | 5 | ## Running 6 | 7 | ### Saving the circuit to disk 8 | 9 | ```console 10 | risc0-lean4/rust$ cargo run --release --bin circuit -- disk --out-base=output/riscv 11 | ``` 12 | 13 | ### Saving receipts to disk 14 | 15 | ```console 16 | risc0-lean4/rust$ cargo run --release --bin hello-world -- disk --out-base=output/hello_world 17 | risc0-lean4/rust$ cargo run --release --bin hw -- disk --out-base=output/hw 18 | ``` 19 | -------------------------------------------------------------------------------- /rust/circuit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "circuit" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | 6 | [dependencies] 7 | clap = { version = "4.0.27", features = ["derive"] } 8 | risc0-circuit-rv32im = { workspace = true } 9 | risc0-zkp = { workspace = true } 10 | serde = { workspace = true } 11 | snafu = { workspace = true } 12 | 13 | [dev-dependencies] 14 | env_logger = "0.9" 15 | escargot = "0.5" 16 | tempfile = "3.3" 17 | tokio = { version = "1", features = ["macros", "rt"] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | 21 | [features] 22 | default = ["std"] 23 | std = [] 24 | -------------------------------------------------------------------------------- /rust/circuit/src/disk.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::fs::File; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | 7 | use risc0_zkp::{ 8 | adapter::{PolyExtStep, PolyExtStepDef}, 9 | taps::{RegisterGroup, TapData, TapSet}, 10 | }; 11 | 12 | fn write_u8(file: &mut File, val: u8) -> std::io::Result<()> { 13 | file.write_all(&[val]) 14 | } 15 | 16 | fn write_u16(file: &mut File, val: u16) -> std::io::Result<()> { 17 | file.write_all(&[(val & 0xff) as u8, ((val >> 8) & 0xff) as u8]) 18 | } 19 | 20 | fn write_u32(file: &mut File, val: u32) -> std::io::Result<()> { 21 | file.write_all(&[ 22 | (val & 0xff) as u8, 23 | ((val >> 8) & 0xff) as u8, 24 | ((val >> 16) & 0xff) as u8, 25 | ((val >> 24) & 0xff) as u8, 26 | ]) 27 | } 28 | 29 | fn write_u16_array(file: &mut File, val: &[u16]) -> std::io::Result<()> { 30 | // Length 31 | write_u32(file, val.len() as u32)?; 32 | // Data 33 | for d in val { 34 | write_u16(file, *d)?; 35 | } 36 | 37 | Ok(()) 38 | } 39 | 40 | fn write_usize_array(file: &mut File, val: &[usize]) -> std::io::Result<()> { 41 | // Length 42 | write_u32(file, val.len() as u32)?; 43 | // Data 44 | for d in val { 45 | write_u32(file, *d as u32)?; 46 | } 47 | 48 | Ok(()) 49 | } 50 | 51 | fn write_register_group(file: &mut File, val: RegisterGroup) -> std::io::Result<()> { 52 | match val { 53 | RegisterGroup::Accum => write_u16(file, 0)?, 54 | RegisterGroup::Code => write_u16(file, 1)?, 55 | RegisterGroup::Data => write_u16(file, 2)?, 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | fn write_tapdata(file: &mut File, val: &TapData) -> std::io::Result<()> { 62 | write_u16(file, val.offset)?; 63 | write_u16(file, val.back)?; 64 | write_register_group(file, val.group)?; 65 | write_u8(file, val.combo)?; 66 | write_u8(file, val.skip)?; 67 | 68 | Ok(()) 69 | } 70 | 71 | fn write_tapdatas(file: &mut File, val: &[TapData]) -> std::io::Result<()> { 72 | // Length 73 | write_u32(file, val.len() as u32)?; 74 | // Data 75 | for tap in val { 76 | write_tapdata(file, tap)?; 77 | } 78 | 79 | Ok(()) 80 | } 81 | 82 | fn write_tapset(file: &mut File, val: &TapSet) -> std::io::Result<()> { 83 | write_tapdatas(file, val.taps)?; 84 | write_u16_array(file, val.combo_taps)?; 85 | write_u16_array(file, val.combo_begin)?; 86 | write_usize_array(file, &val.group_begin)?; 87 | write_u32(file, val.combos_count as u32)?; 88 | write_u32(file, val.reg_count as u32)?; 89 | write_u32(file, val.tot_combo_backs as u32)?; 90 | 91 | Ok(()) 92 | } 93 | 94 | fn write_step(file: &mut File, val: &PolyExtStep) -> std::io::Result<()> { 95 | match val { 96 | PolyExtStep::Const(value, _loc) => { 97 | write_u32(file, 1)?; 98 | write_u32(file, *value as u32)?; 99 | } 100 | PolyExtStep::Get(tap, _loc) => { 101 | write_u32(file, 2)?; 102 | write_u32(file, *tap as u32)?; 103 | } 104 | PolyExtStep::GetGlobal(base, offset, _loc) => { 105 | write_u32(file, 3)?; 106 | write_u32(file, *base as u32)?; 107 | write_u32(file, *offset as u32)?; 108 | } 109 | PolyExtStep::Add(x1, x2, _loc) => { 110 | write_u32(file, 4)?; 111 | write_u32(file, *x1 as u32)?; 112 | write_u32(file, *x2 as u32)?; 113 | } 114 | PolyExtStep::Sub(x1, x2, _loc) => { 115 | write_u32(file, 5)?; 116 | write_u32(file, *x1 as u32)?; 117 | write_u32(file, *x2 as u32)?; 118 | } 119 | PolyExtStep::Mul(x1, x2, _loc) => { 120 | write_u32(file, 6)?; 121 | write_u32(file, *x1 as u32)?; 122 | write_u32(file, *x2 as u32)?; 123 | } 124 | PolyExtStep::True(_loc) => { 125 | write_u32(file, 7)?; 126 | } 127 | PolyExtStep::AndEqz(x, val, _loc) => { 128 | write_u32(file, 8)?; 129 | write_u32(file, *x as u32)?; 130 | write_u32(file, *val as u32)?; 131 | } 132 | PolyExtStep::AndCond(x, cond, inner, _loc) => { 133 | write_u32(file, 9)?; 134 | write_u32(file, *x as u32)?; 135 | write_u32(file, *cond as u32)?; 136 | write_u32(file, *inner as u32)?; 137 | } 138 | } 139 | 140 | Ok(()) 141 | } 142 | 143 | fn write_stepdef(file: &mut File, val: &PolyExtStepDef) -> std::io::Result<()> { 144 | // Block length 145 | write_u32(file, val.block.len() as u32)?; 146 | // Block 147 | for op in val.block { 148 | write_step(file, op)?; 149 | } 150 | // Ret 151 | write_u32(file, val.ret as u32)?; 152 | 153 | Ok(()) 154 | } 155 | 156 | pub fn save_to_disk( 157 | mut out_base: PathBuf, 158 | output_size: usize, 159 | mix_size: usize, 160 | tapset: &TapSet, 161 | stepdef: &PolyExtStepDef, 162 | ) -> std::io::Result<()> { 163 | out_base.set_extension("circuit"); 164 | let mut file = File::create(&out_base)?; 165 | 166 | write_u32(&mut file, output_size as u32)?; 167 | write_u32(&mut file, mix_size as u32)?; 168 | write_tapset(&mut file, tapset)?; 169 | write_stepdef(&mut file, stepdef)?; 170 | 171 | Ok(()) 172 | } 173 | -------------------------------------------------------------------------------- /rust/circuit/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::path::PathBuf; 4 | 5 | use clap::{Parser, Subcommand}; 6 | use risc0_circuit_rv32im::{poly_ext, CircuitImpl}; 7 | use risc0_zkp::adapter::{CircuitInfo, TapsProvider}; 8 | 9 | mod disk; 10 | 11 | #[derive(Parser)] 12 | #[command(author, version, about, long_about = None)] 13 | struct Cli { 14 | #[command(subcommand)] 15 | command: Command, 16 | } 17 | 18 | #[derive(Subcommand)] 19 | enum Command { 20 | Disk { 21 | #[arg(long, value_name = "FILE")] 22 | out_base: PathBuf, 23 | }, 24 | } 25 | 26 | pub fn main() { 27 | let cli = Cli::parse(); 28 | 29 | let circuit = CircuitImpl::new(); 30 | 31 | let output_size = CircuitImpl::OUTPUT_SIZE; 32 | let mix_size = CircuitImpl::MIX_SIZE; 33 | let tapset = circuit.get_taps(); 34 | let stepdef = &poly_ext::DEF; 35 | 36 | match cli.command { 37 | Command::Disk { out_base } => { 38 | disk::save_to_disk(out_base, output_size, mix_size, tapset, stepdef) 39 | .expect("Could not write to disk") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rust/hello_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | 6 | [dependencies] 7 | clap = { version = "4.0.27", features = ["derive"] } 8 | hello-world-guard = { path = "guard" } 9 | risc0-zkvm = { workspace = true } 10 | serde = { workspace = true } 11 | snafu = { workspace = true } 12 | 13 | [dev-dependencies] 14 | env_logger = "0.9" 15 | escargot = "0.5" 16 | tempfile = "3.3" 17 | tokio = { version = "1", features = ["macros", "rt"] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | 21 | [features] 22 | cuda = ["risc0-zkvm/cuda"] 23 | default = ["std"] 24 | metal = ["risc0-zkvm/metal"] 25 | std = [] 26 | -------------------------------------------------------------------------------- /rust/hello_world/guard/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-guard" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | 6 | [build-dependencies] 7 | risc0-build = { workspace = true } 8 | 9 | [package.metadata.risc0] 10 | methods = ["guest"] 11 | -------------------------------------------------------------------------------- /rust/hello_world/guard/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | fn main() { 4 | risc0_build::embed_methods(); 5 | } 6 | -------------------------------------------------------------------------------- /rust/hello_world/guard/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-guard-guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies.risc0-zkvm] 9 | version = "1.0.0-rc.2" 10 | git = "ssh://git@github.com/risc0/risc0.git" 11 | default-features = false 12 | -------------------------------------------------------------------------------- /rust/hello_world/guard/guest/src/bin/hello_world.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | use risc0_zkvm::guest::env; 7 | 8 | risc0_zkvm::entry!(main); 9 | 10 | pub fn main() { 11 | env::commit(b"hello world"); 12 | } 13 | -------------------------------------------------------------------------------- /rust/hello_world/guard/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 2 | -------------------------------------------------------------------------------- /rust/hello_world/src/disk.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::fs::File; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | 7 | use risc0_zkvm::Receipt; 8 | 9 | pub fn save_to_disk( 10 | mut out_base: PathBuf, 11 | image: Vec, 12 | id: &'static [u8], 13 | receipt: Receipt, 14 | ) -> std::io::Result<()> { 15 | { 16 | out_base.set_extension("bin"); 17 | File::create(&out_base)?.write_all(&image)?; 18 | } 19 | { 20 | out_base.set_extension("id"); 21 | File::create(&out_base)?.write_all(id)?; 22 | } 23 | { 24 | out_base.set_extension("journal"); 25 | File::create(&out_base)?.write_all(receipt.get_journal_bytes())?; 26 | } 27 | { 28 | out_base.set_extension("seal"); 29 | File::create(&out_base)?.write_all(receipt.get_seal_bytes())?; 30 | } 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rust/hello_world/src/lean.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use risc0_zkvm::Receipt; 4 | 5 | fn print_bytes(data: &[u8]) { 6 | print!(" "); 7 | for i in (0..data.len()).step_by(4) { 8 | let more_to_come = i + 4 < data.len(); 9 | let sep = if more_to_come { ", " } else { "" }; 10 | print!( 11 | "0x{:02x?}{:02x?}{:02x?}{:02x?}{}", 12 | data[i + 3], 13 | data[i + 2], 14 | data[i + 1], 15 | data[i], 16 | sep 17 | ); 18 | if 0 < i && more_to_come && (i + 4) % 32 == 0 { 19 | println!(""); 20 | print!(" "); 21 | } 22 | } 23 | println!(""); 24 | } 25 | 26 | pub fn print_lean_file(id: &[u8], receipt: &Receipt, print_seal: bool) { 27 | let journal = receipt.get_journal_bytes(); 28 | let seal = receipt.get_seal_bytes(); 29 | 30 | let namespace = "Zkvm.Test.HelloWorld"; 31 | 32 | println!("/-"); 33 | println!("Copyright (c) 2022 RISC Zero. All rights reserved."); 34 | println!("-/"); 35 | 36 | println!(""); 37 | println!(""); 38 | 39 | println!("namespace {}", namespace); 40 | 41 | println!(""); 42 | 43 | println!("def ID: Array UInt32 := #["); 44 | print_bytes(id); 45 | println!("]"); 46 | 47 | println!(""); 48 | 49 | println!("def JOURNAL: Array UInt32 := #["); 50 | print_bytes(journal); 51 | println!("]"); 52 | 53 | println!(""); 54 | 55 | if print_seal { 56 | let seal_parts = { 57 | let mut seal_parts = 0; 58 | let chunk_size = 1024; 59 | 60 | println!("namespace SEAL_PARTS"); 61 | println!(""); 62 | for chunk in (0..seal.len()).step_by(chunk_size) { 63 | let remaining = seal.len() - chunk; 64 | let size = usize::min(chunk_size, remaining); 65 | 66 | println!("def PART_{}: Array UInt32 := #[", seal_parts); 67 | print_bytes(&seal[chunk..chunk + size]); 68 | println!("]"); 69 | println!(""); 70 | 71 | seal_parts += 1; 72 | } 73 | println!("end SEAL_PARTS"); 74 | println!(""); 75 | 76 | seal_parts 77 | }; 78 | 79 | println!("def SEAL: Array (Array UInt32) := #["); 80 | for i in 0..seal_parts { 81 | let more_to_come = i + 1 < seal_parts; 82 | let sep = if more_to_come { "," } else { "" }; 83 | println!(" SEAL_PARTS.PART_{}{}", i, sep) 84 | } 85 | println!("]"); 86 | 87 | println!(""); 88 | } 89 | 90 | println!("end {}", namespace); 91 | } 92 | -------------------------------------------------------------------------------- /rust/hello_world/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::path::PathBuf; 4 | 5 | use clap::{Parser, Subcommand}; 6 | use risc0_zkvm::Prover; 7 | 8 | mod disk; 9 | mod lean; 10 | 11 | #[derive(Parser)] 12 | #[command(author, version, about, long_about = None)] 13 | struct Cli { 14 | #[command(subcommand)] 15 | command: Command, 16 | } 17 | 18 | #[derive(Subcommand)] 19 | enum Command { 20 | Disk { 21 | #[arg(long, value_name = "FILE")] 22 | out_base: PathBuf, 23 | }, 24 | Lean {}, 25 | } 26 | 27 | pub fn main() { 28 | let cli = Cli::parse(); 29 | 30 | let path: &str = hello_world_guard::HELLO_WORLD_PATH; 31 | let id: &[u8] = hello_world_guard::HELLO_WORLD_ID; 32 | 33 | let image = std::fs::read(path).expect("Could not load image"); 34 | 35 | let receipt = { 36 | let mut prover = Prover::new(&image, id).expect("Could not create prover"); 37 | 38 | prover.run().expect("Could not get receipt") 39 | }; 40 | 41 | receipt.verify(id).expect("Could not verify receipt"); 42 | 43 | match cli.command { 44 | Command::Disk { out_base } => { 45 | disk::save_to_disk(out_base, image, id, receipt).expect("Could not write to disk") 46 | } 47 | Command::Lean {} => lean::print_lean_file(id, &receipt, false), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rust/hw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hw" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | 6 | [dependencies] 7 | clap = { version = "4.0.27", features = ["derive"] } 8 | hw-guard = { path = "guard" } 9 | risc0-zkvm = { workspace = true } 10 | serde = { workspace = true } 11 | snafu = { workspace = true } 12 | 13 | [dev-dependencies] 14 | env_logger = "0.9" 15 | escargot = "0.5" 16 | tempfile = "3.3" 17 | tokio = { version = "1", features = ["macros", "rt"] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | 21 | [features] 22 | cuda = ["risc0-zkvm/cuda"] 23 | default = ["std"] 24 | metal = ["risc0-zkvm/metal"] 25 | std = [] 26 | -------------------------------------------------------------------------------- /rust/hw/guard/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hw-guard" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | 6 | [build-dependencies] 7 | risc0-build = { workspace = true } 8 | 9 | [package.metadata.risc0] 10 | methods = ["guest"] 11 | -------------------------------------------------------------------------------- /rust/hw/guard/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | fn main() { 4 | risc0_build::embed_methods(); 5 | } 6 | -------------------------------------------------------------------------------- /rust/hw/guard/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hw-guard-guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies.risc0-zkvm] 9 | version = "1.0.0-rc.2" 10 | git = "ssh://git@github.com/risc0/risc0.git" 11 | default-features = false 12 | -------------------------------------------------------------------------------- /rust/hw/guard/guest/src/bin/hw.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | use risc0_zkvm::guest::env; 7 | 8 | risc0_zkvm::entry!(main); 9 | 10 | pub fn main() { 11 | env::commit(b"hw"); 12 | } 13 | -------------------------------------------------------------------------------- /rust/hw/guard/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 2 | -------------------------------------------------------------------------------- /rust/hw/src/disk.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::fs::File; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | 7 | use risc0_zkvm::Receipt; 8 | 9 | pub fn save_to_disk( 10 | mut out_base: PathBuf, 11 | image: Vec, 12 | id: &'static [u8], 13 | receipt: Receipt, 14 | ) -> std::io::Result<()> { 15 | { 16 | out_base.set_extension("bin"); 17 | File::create(&out_base)?.write_all(&image)?; 18 | } 19 | { 20 | out_base.set_extension("id"); 21 | File::create(&out_base)?.write_all(id)?; 22 | } 23 | { 24 | out_base.set_extension("journal"); 25 | File::create(&out_base)?.write_all(receipt.get_journal_bytes())?; 26 | } 27 | { 28 | out_base.set_extension("seal"); 29 | File::create(&out_base)?.write_all(receipt.get_seal_bytes())?; 30 | } 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rust/hw/src/lean.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use risc0_zkvm::Receipt; 4 | 5 | fn print_bytes(data: &[u8]) { 6 | print!(" "); 7 | for i in (0..data.len()).step_by(4) { 8 | let more_to_come = i + 4 < data.len(); 9 | let sep = if more_to_come { ", " } else { "" }; 10 | print!( 11 | "0x{:02x?}{:02x?}{:02x?}{:02x?}{}", 12 | data[i + 3], 13 | data[i + 2], 14 | data[i + 1], 15 | data[i], 16 | sep 17 | ); 18 | if 0 < i && more_to_come && (i + 4) % 32 == 0 { 19 | println!(""); 20 | print!(" "); 21 | } 22 | } 23 | println!(""); 24 | } 25 | 26 | pub fn print_lean_file(id: &[u8], receipt: &Receipt, print_seal: bool) { 27 | let journal = receipt.get_journal_bytes(); 28 | let seal = receipt.get_seal_bytes(); 29 | 30 | let namespace = "Zkvm.Test.HW"; 31 | 32 | println!("/-"); 33 | println!("Copyright (c) 2022 RISC Zero. All rights reserved."); 34 | println!("-/"); 35 | 36 | println!(""); 37 | println!(""); 38 | 39 | println!("namespace {}", namespace); 40 | 41 | println!(""); 42 | 43 | println!("def ID: Array UInt32 := #["); 44 | print_bytes(id); 45 | println!("]"); 46 | 47 | println!(""); 48 | 49 | println!("def JOURNAL: Array UInt32 := #["); 50 | print_bytes(journal); 51 | println!("]"); 52 | 53 | println!(""); 54 | 55 | if print_seal { 56 | let seal_parts = { 57 | let mut seal_parts = 0; 58 | let chunk_size = 1024; 59 | 60 | println!("namespace SEAL_PARTS"); 61 | println!(""); 62 | for chunk in (0..seal.len()).step_by(chunk_size) { 63 | let remaining = seal.len() - chunk; 64 | let size = usize::min(chunk_size, remaining); 65 | 66 | println!("def PART_{}: Array UInt32 := #[", seal_parts); 67 | print_bytes(&seal[chunk..chunk + size]); 68 | println!("]"); 69 | println!(""); 70 | 71 | seal_parts += 1; 72 | } 73 | println!("end SEAL_PARTS"); 74 | println!(""); 75 | 76 | seal_parts 77 | }; 78 | 79 | println!("def SEAL: Array (Array UInt32) := #["); 80 | for i in 0..seal_parts { 81 | let more_to_come = i + 1 < seal_parts; 82 | let sep = if more_to_come { "," } else { "" }; 83 | println!(" SEAL_PARTS.PART_{}{}", i, sep) 84 | } 85 | println!("]"); 86 | 87 | println!(""); 88 | } 89 | 90 | println!("end {}", namespace); 91 | } 92 | -------------------------------------------------------------------------------- /rust/hw/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | 3 | use std::path::PathBuf; 4 | 5 | use clap::{Parser, Subcommand}; 6 | use risc0_zkvm::Prover; 7 | 8 | mod disk; 9 | mod lean; 10 | 11 | #[derive(Parser)] 12 | #[command(author, version, about, long_about = None)] 13 | struct Cli { 14 | #[command(subcommand)] 15 | command: Command, 16 | } 17 | 18 | #[derive(Subcommand)] 19 | enum Command { 20 | Disk { 21 | #[arg(long, value_name = "FILE")] 22 | out_base: PathBuf, 23 | }, 24 | Lean {}, 25 | } 26 | 27 | pub fn main() { 28 | let cli = Cli::parse(); 29 | 30 | let path: &str = hw_guard::HW_PATH; 31 | let id: &[u8] = hw_guard::HW_ID; 32 | 33 | let image = std::fs::read(path).expect("Could not load image"); 34 | 35 | let receipt = { 36 | let mut prover = Prover::new(&image, id).expect("Could not create prover"); 37 | 38 | prover.run().expect("Could not get receipt") 39 | }; 40 | 41 | receipt.verify(id).expect("Could not verify receipt"); 42 | 43 | match cli.command { 44 | Command::Disk { out_base } => { 45 | disk::save_to_disk(out_base, image, id, receipt).expect("Could not write to disk") 46 | } 47 | Command::Lean {} => lean::print_lean_file(id, &receipt, false), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rust/output/hello_world.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hello_world.bin -------------------------------------------------------------------------------- /rust/output/hello_world.id: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hello_world.id -------------------------------------------------------------------------------- /rust/output/hello_world.journal: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /rust/output/hello_world.seal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hello_world.seal -------------------------------------------------------------------------------- /rust/output/hw.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hw.bin -------------------------------------------------------------------------------- /rust/output/hw.id: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hw.id -------------------------------------------------------------------------------- /rust/output/hw.journal: -------------------------------------------------------------------------------- 1 | hw -------------------------------------------------------------------------------- /rust/output/hw.seal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/hw.seal -------------------------------------------------------------------------------- /rust/output/riscv.circuit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risc0/risc0-lean4/31c956fc9246bbfc84359021d66ed94972afd86b/rust/output/riscv.circuit -------------------------------------------------------------------------------- /rust/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-08-16" 3 | components = [ "rustfmt", "rust-src" ] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /rust/rustfmt.toml: -------------------------------------------------------------------------------- 1 | wrap_comments = true 2 | normalize_comments = true 3 | reorder_modules = true 4 | group_imports = "StdExternalCrate" 5 | --------------------------------------------------------------------------------