Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### monad ignore

build
build-zkvm
perf.data*
scratch

Expand Down
107 changes: 27 additions & 80 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,69 +62,24 @@ include(cmake/test.cmake)

set(THIRD_PARTY_DIR "${PROJECT_SOURCE_DIR}/third_party")

function(monad_compile_options target)
set_property(TARGET ${target} PROPERTY C_STANDARD 23)
set_property(TARGET ${target} PROPERTY C_STANDARD_REQUIRED ON)
set_property(TARGET ${target} PROPERTY CXX_STANDARD 23)
set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED ON)

target_compile_options(${target} PRIVATE -Wall -Wextra -Wconversion -Werror)
target_compile_definitions(${target} PUBLIC "_GNU_SOURCE")

target_compile_options(
${target} PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wno-missing-field-initializers>)

target_compile_options(${target} PRIVATE $<$<CONFIG:Debug>:-Og>)

target_compile_definitions(${target} PUBLIC QUILL_ROOT_LOGGER_ONLY)

if(MONAD_COMPILER_TESTING)
target_compile_definitions(${target} PUBLIC "MONAD_COMPILER_TESTING=1")
target_compile_definitions(${target} PUBLIC "MONAD_CORE_FORCE_DEBUG_ASSERT=1")
endif()

if(MONAD_COMPILER_STATS)
target_compile_definitions(${target} PUBLIC "MONAD_COMPILER_STATS=1")
endif()

if(MONAD_COMPILER_HOT_PATH_STATS)
target_compile_definitions(${target} PUBLIC "MONAD_COMPILER_HOT_PATH_STATS=1")
endif()

target_compile_options(
${target}
PUBLIC $<$<CXX_COMPILER_ID:GNU>:-Wno-attributes=clang::no_sanitize>)

# this is needed to turn off ranges support in nlohmann_json, because the
# ranges standard header triggers a clang bug which is fixed in trunk but not
# currently available to us
# https://gcc.gnu.org/bugzilla//show_bug.cgi?id=109647
target_compile_definitions(${target} PUBLIC "JSON_HAS_RANGES=0")
endfunction()
include(cmake/compile_options.cmake)

find_package(Boost REQUIRED COMPONENTS context filesystem json CONFIG)
find_package(PkgConfig REQUIRED)
pkg_check_modules(brotli REQUIRED IMPORTED_TARGET libbrotlienc libbrotlidec)
pkg_check_modules(crypto++ REQUIRED IMPORTED_TARGET libcrypto++)
pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)

# ankerl
add_library(ankerl_hash INTERFACE)
target_include_directories(ankerl_hash INTERFACE "third_party/ankerl")

# asmjit
set(ASMJIT_STATIC ON)
add_subdirectory(third_party/asmjit)

# BLAKE3
add_subdirectory(third_party/BLAKE3/c)

# blst
# Precompile deps: blst, silkpre, c-kzg-4844, cryptopp, immer,
# nlohmann_json, unordered_dense, asmjit
set(DOWNLOAD_BLST OFF)
include(cmake/blst.cmake)

# c-kzg-4844
add_subdirectory("third_party/c-kzg-4844-builder")
include(cmake/precompile_deps.cmake)

# cli11
find_package(CLI11 REQUIRED)
Expand All @@ -139,11 +94,20 @@ add_subdirectory("third_party/cthash")
set(ETHASH_TESTING NO)
add_subdirectory("third_party/ethash")

# fiber (boost)
# Boost.Fiber's CMakeLists.txt expects granular Boost targets from the
# superproject build. The system-installed Boost only provides Boost::headers
# for header-only libs. Create shim targets so fiber can link them.
foreach(_boost_hdr_lib assert config core intrusive predef smart_ptr algorithm format)
# fiber (boost) Boost.Fiber's CMakeLists.txt expects granular Boost targets from
# the superproject build. The system-installed Boost only provides
# Boost::headers for header-only libs. Create shim targets so fiber can link
# them.
foreach(
_boost_hdr_lib
assert
config
core
intrusive
predef
smart_ptr
algorithm
format)
if(NOT TARGET Boost::${_boost_hdr_lib})
add_library(boost_${_boost_hdr_lib} INTERFACE)
target_link_libraries(boost_${_boost_hdr_lib} INTERFACE Boost::headers)
Expand All @@ -152,13 +116,8 @@ foreach(_boost_hdr_lib assert config core intrusive predef smart_ptr algorithm f
endforeach()
add_subdirectory("third_party/fiber" SYSTEM)
# Boost.Fiber triggers warnings under C++23 strict flags.
target_compile_options(boost_fiber PRIVATE -Wno-deprecated-declarations -Wno-conversion)

# immer
option(immer_BUILD_TESTS OFF)
option(immer_BUILD_EXAMPLES OFF)
option(immer_BUILD_EXTRAS OFF)
add_subdirectory("third_party/immer" SYSTEM)
target_compile_options(boost_fiber PRIVATE -Wno-deprecated-declarations
-Wno-conversion)

# intx
add_subdirectory("third_party/intx")
Expand All @@ -168,7 +127,7 @@ set(HUNTER_ENABLED OFF)
add_subdirectory("third_party/evmc")

# evmone
if (MONAD_COMPILER_TESTING OR MONAD_COMPILER_BENCHMARKS)
if(MONAD_COMPILER_TESTING OR MONAD_COMPILER_BENCHMARKS)
include(cmake/evmone.cmake)
endif()

Expand All @@ -182,9 +141,6 @@ add_subdirectory("third_party/magic_enum")
# nanobench
add_subdirectory("third_party/nanobench")

# nlohmann_json
add_subdirectory("third_party/nlohmann_json" SYSTEM)

# quill
add_subdirectory("third_party/quill")
# TODO
Expand All @@ -195,22 +151,9 @@ target_compile_options(
target_compile_options(
quill PUBLIC $<$<CXX_COMPILER_ID:GNU>:-Wno-tautological-compare>)

# silkpre
set(OPTIONAL_BUILD_TESTS OFF)
set(OLD_CMAKE_POLICY_VERSION_MINIMUM "${CMAKE_POLICY_VERSION_MINIMUM}")
set(CMAKE_POLICY_VERSION_MINIMUM "3.5")
add_subdirectory(third_party/silkpre)
set(CMAKE_POLICY_VERSION_MINIMUM "${OLD_CMAKE_POLICY_VERSION_MINIMUM}")
# undo the injection of ccache silkpre/ff does
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK)

# tbb
find_package(TBB REQUIRED)

# unordered_dense
add_subdirectory("third_party/unordered_dense")

# ##############################################################################
# unit tests
# ##############################################################################
Expand Down Expand Up @@ -240,8 +183,11 @@ function(monad_add_test target)
target_link_libraries(${target} monad_execution GTest::GTest GTest::Main)
gtest_discover_tests(
${target} DISCOVERY_MODE PRE_TEST
PROPERTIES ENVIRONMENT ASAN_OPTIONS=abort_on_error=1 ENVIRONMENT
UBSAN_OPTIONS=halt_on_error=1,print_stacktrace=1 ENVIRONMENT
PROPERTIES ENVIRONMENT
ASAN_OPTIONS=abort_on_error=1
ENVIRONMENT
UBSAN_OPTIONS=halt_on_error=1,print_stacktrace=1
ENVIRONMENT
TSAN_OPTIONS=external_symbolizer_path=/usr/bin/llvm-symbolizer
${AT_PROPERTIES})
endfunction()
Expand Down Expand Up @@ -307,6 +253,7 @@ target_link_libraries(
monad_trie
monad_execution_ethereum
monad_execution_native
monad_precompiles
monad_rpc
monad_statesync
monad-vm
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ You can also run the full test suite in parallel with:
CTEST_PARALLEL_LEVEL=$(nproc) ctest
```

## Compiling zkVM binary

To compile monad as a guest program for various zkVMs, such as ZisK or SP1, we need to use a riscv64 cross-compiler. The cmake build extracts only the needed libc objects (setjmp/longjmp) from the unmodified newlib; malloc and syscalls are weakly linked by the zkVM frameworks.

```shell
cmake -B build-zkvm -S category/zkvm \
-DCMAKE_TOOLCHAIN_FILE=category/core/toolchains/riscv64-elf-toolchain.cmake \
-DRISCV_TOOLCHAIN_DIR=PATH-TO-RISCV-COMPILER \
-DCMAKE_BUILD_TYPE=Release -GNinja
```

We can then build the static library:

```shell
cmake --build build-zkvm --target monad-zkvm
```

## A tour of execution

To understand how the source code is organized, you should start by reading
Expand Down
16 changes: 14 additions & 2 deletions category/core/runtime/non_temporal_memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@

#include <category/core/assert.h>

#include <immintrin.h>

#include <cstddef>
#include <cstdint>

#ifdef MONAD_ZKVM
#include <cstring>
#else
#include <immintrin.h>
#endif

namespace monad::vm::runtime
{
inline void non_temporal_bzero(void *dest, size_t n)
{
#ifdef MONAD_ZKVM
std::memset(dest, 0, n);
#else
MONAD_ASSERT((reinterpret_cast<uintptr_t>(dest) & 31) == 0);
MONAD_ASSERT((n & 31) == 0);
auto *d = static_cast<uint8_t *>(dest);
Expand All @@ -35,10 +42,14 @@ namespace monad::vm::runtime
_mm256_stream_si256(reinterpret_cast<__m256i *>(d), zero);
d += 32;
}
#endif
}

inline void non_temporal_memcpy(void *dest, void *src, size_t n)
{
#ifdef MONAD_ZKVM
std::memcpy(dest, src, n);
#else
MONAD_ASSERT((reinterpret_cast<uintptr_t>(dest) & 31) == 0);
MONAD_ASSERT((reinterpret_cast<uintptr_t>(src) & 31) == 0);
MONAD_ASSERT((n & 31) == 0);
Expand All @@ -52,5 +63,6 @@ namespace monad::vm::runtime
d += 32;
s += 32;
}
#endif
}
}
45 changes: 45 additions & 0 deletions category/core/toolchains/riscv64-elf-toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (C) 2025 Category Labs, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

# Configurable toolchain directory — pass -DRISCV_TOOLCHAIN_DIR=<path> to cmake.
set(RISCV_TOOLCHAIN_DIR "/opt/riscv" CACHE PATH "RISC-V toolchain directory")
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES RISCV_TOOLCHAIN_DIR)

# Auto-detect target prefix (riscv64-none-elf- for nix, riscv64-unknown-elf- for ZisK).
if(EXISTS "${RISCV_TOOLCHAIN_DIR}/bin/riscv64-none-elf-gcc")
set(RISCV_PREFIX "riscv64-none-elf-")
elseif(EXISTS "${RISCV_TOOLCHAIN_DIR}/bin/riscv64-unknown-elf-gcc")
set(RISCV_PREFIX "riscv64-unknown-elf-")
else()
message(FATAL_ERROR "No riscv64 gcc found in ${RISCV_TOOLCHAIN_DIR}/bin/")
endif()

set(CMAKE_C_COMPILER "${RISCV_TOOLCHAIN_DIR}/bin/${RISCV_PREFIX}gcc")
set(CMAKE_CXX_COMPILER "${RISCV_TOOLCHAIN_DIR}/bin/${RISCV_PREFIX}g++")
set(CMAKE_AR "${RISCV_TOOLCHAIN_DIR}/bin/${RISCV_PREFIX}ar")

set(CMAKE_C_FLAGS_INIT
"-march=rv64ima -mabi=lp64 -mcmodel=medany -nostartfiles -nostdlib -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS_INIT
"-march=rv64ima -mabi=lp64 -mcmodel=medany -nostartfiles -nostdlib++ -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections")
set(CMAKE_ASM_FLAGS_INIT "-march=rv64ima -mabi=lp64")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
44 changes: 36 additions & 8 deletions category/execution/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,6 @@ add_library(
"ethereum/fmt/event_trace_fmt.hpp"
"ethereum/process_requests.cpp"
"ethereum/process_requests.hpp"
"ethereum/precompiles.cpp"
"ethereum/precompiles.hpp"
"ethereum/precompiles_bls12.cpp"
"ethereum/precompiles_bls12.hpp"
"ethereum/precompiles_impl.cpp"
"ethereum/precompiles_gas_cost_impl.cpp"
"ethereum/reserve_balance.cpp"
"ethereum/reserve_balance.hpp"
"ethereum/trace/call_frame.cpp"
Expand Down Expand Up @@ -276,6 +270,21 @@ add_library(
# monad/state2
"monad/state2/proposal_state.hpp")

# ##############################################################################
# Precompiles — extracted into a standalone OBJECT library so that the zkvm
# build can reuse it without pulling in the full execution dependency tree.
# ##############################################################################

add_library(
monad_precompiles OBJECT
"ethereum/precompiles.cpp"
"ethereum/precompiles.hpp"
"ethereum/precompiles_gas_cost_impl.cpp"
"ethereum/precompiles_impl.cpp"
"ethereum/precompiles_bls12.cpp"
"ethereum/precompiles_bls12.hpp"
)

target_include_directories(
monad_execution_ethereum
PUBLIC ${CATEGORY_MAIN_DIR}
Expand All @@ -298,6 +307,27 @@ target_compile_definitions(
monad_execution_native PUBLIC MONAD_CXX_CTYPES_USE_EVMC_HPP
MONAD_CXX_CTYPES_USE_INTX)

target_include_directories(
monad_precompiles
PUBLIC ${CATEGORY_MAIN_DIR}
PUBLIC ${silkpre_SOURCE_DIR}/lib)

target_link_libraries(
monad_precompiles
PUBLIC monad-vm
PUBLIC monad_core
PUBLIC blst::blst
PUBLIC c-kzg-4844
PRIVATE PkgConfig::crypto++
PUBLIC ethash::keccak
PUBLIC evmc
PUBLIC immer
PUBLIC intx::intx
PUBLIC nlohmann_json::nlohmann_json
PUBLIC silkpre
PUBLIC unordered_dense)
monad_compile_options(monad_precompiles)

target_link_libraries(
monad_execution_ethereum
PUBLIC monad-vm
Expand All @@ -306,9 +336,7 @@ target_link_libraries(
PUBLIC blst::blst
PRIVATE Boost::fiber
PRIVATE Boost::json
PUBLIC c-kzg-4844
PRIVATE PkgConfig::brotli
PRIVATE PkgConfig::crypto++
PUBLIC ethash::keccak
PUBLIC evmc
PUBLIC immer
Expand Down
8 changes: 8 additions & 0 deletions category/execution/ethereum/core/contract/big_endian.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ struct BigEndian
unaligned_store(bytes, be);
return *this;
}

[[gnu::always_inline]] static inline BigEndian<T>
unsafe_from(uint8_t const *src) noexcept
{
BigEndian<T> be;
std::memcpy(&be.bytes, src, sizeof(T));
return be;
}
};

using u8_be = BigEndian<uint8_t>;
Expand Down
Loading
Loading