feat: Migrate circuits from nomos-pocs. (#1)

feat: Migrate circuits from nomos-pocs.
This commit is contained in:
Álex 2025-10-03 12:44:03 +02:00 committed by GitHub
commit 50bdad858b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 3838 additions and 0 deletions

View File

@ -0,0 +1,128 @@
name: "Compile and Bundle Circuit"
description: "Compiles and bundles the witness generator of a Circom Circuit"
branding:
icon: "package"
color: "blue"
inputs:
circuit-name-display:
description: "The name of the Circom circuit to compile."
required: true
circuit-name-binary:
description: "The final name of the compiled binary. The name should be extensionless."
required: true
circuit-path:
description: "The path to the Circom circuit file relative to the repository root."
required: true
resources-path:
description: "The path to the CI resources directory relative to the repository root."
required: false
default: ".github/resources"
version:
description: "The version of the bundle. E.g.: v1.0.0."
required: true
os:
description: "The target operating system for the bundle (linux, windows, macos)."
required: true
arch:
description: "The target architecture for the bundle (x86_64, aarch64)."
required: true
runs:
using: "composite"
steps:
- name: Parse Circuit Path
id: parse-circuit-path
shell: bash
env:
CIRCUIT_PATH: ${{ inputs.circuit-path }}
BUNDLE_TRIPLET: ${{ inputs.version }}-${{ inputs.os }}-${{ inputs.arch }}
CIRCUIT_NAME_BINARY: ${{ inputs.circuit-name-binary }}
RESOURCES_PATH: ${{ inputs.resources-path }}
OS: ${{ inputs.os }}
run: |
CIRCUIT_DIRECTORY="$(dirname ${CIRCUIT_PATH})"
CIRCUIT_FILENAME="$(basename ${CIRCUIT_PATH})"
CIRCUIT_FILESTEM="${CIRCUIT_FILENAME%.circom}"
CIRCUIT_CPP_DIRNAME="${CIRCUIT_FILESTEM}_cpp"
platform_binary_name="${CIRCUIT_NAME_BINARY}"
if [ "${OS}" = "windows" ]; then
platform_binary_name="${platform_binary_name}.exe"
fi
{
echo "CIRCUIT_DIRECTORY=${CIRCUIT_DIRECTORY}"
echo "CIRCUIT_FILENAME=${CIRCUIT_FILENAME}"
echo "CIRCUIT_FILESTEM=${CIRCUIT_FILESTEM}"
echo "CIRCUIT_CPP_DIRNAME=${CIRCUIT_CPP_DIRNAME}"
echo "CIRCUIT_CPP_PATH=${CIRCUIT_DIRECTORY}/${CIRCUIT_CPP_DIRNAME}"
echo "WITNESS_GENERATOR_RESOURCES_PATH=${RESOURCES_PATH}/witness-generator"
echo "BUNDLE_TRIPLET=${BUNDLE_TRIPLET}"
echo "PLATFORM_BINARY_NAME=${platform_binary_name}"
} >> "${GITHUB_OUTPUT}"
- name: Generate ${{ inputs.circuit-name-display }}
shell: bash
working-directory: ${{ steps.parse-circuit-path.outputs.CIRCUIT_DIRECTORY }}
env:
CIRCUIT_FILENAME: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILENAME }}
run: circom --c --r1cs --no_asm --O2 "${CIRCUIT_FILENAME}"
# TODO: Instead of replace, make a fork that generates the appropriate Makefile
- name: Replace ${{ inputs.circuit-name-display }}'s Makefile
shell: bash
env:
WITNESS_GENERATOR_RESOURCES_PATH: ${{ steps.parse-circuit-path.outputs.WITNESS_GENERATOR_RESOURCES_PATH }}
CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
run: cp "${WITNESS_GENERATOR_RESOURCES_PATH}/Makefile" "${CIRCUIT_CPP_PATH}/Makefile"
# TODO: Instead of insertion, make a fork that includes the appropriate patch (or the actual fix)
- name: Patch MacOS GMP
shell: bash
if: ${{ inputs.os == 'macos' }}
env:
WITNESS_GENERATOR_RESOURCES_PATH: ${{ steps.parse-circuit-path.outputs.WITNESS_GENERATOR_RESOURCES_PATH }}
CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
OS: ${{ inputs.os }}
run: cp "${WITNESS_GENERATOR_RESOURCES_PATH}/${{ env.OS }}.gmp_patch.hpp" "${CIRCUIT_CPP_PATH}/gmp_patch.hpp"
- name: Compile ${{ inputs.circuit-name-display }}
if: ${{ inputs.os != 'windows' }}
shell: bash
working-directory: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
env:
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
OS: ${{ inputs.os }}
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}"
- name: Compile ${{ inputs.circuit-name-display }}
if: ${{ inputs.os == 'windows' }}
shell: msys2 {0}
working-directory: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
env:
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
OS: ${{ inputs.os }}
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}"
- name: Bundle ${{ inputs.circuit-name-display }}
shell: bash
env:
CIRCUIT_NAME: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
PLATFORM_BINARY_NAME: ${{ steps.parse-circuit-path.outputs.PLATFORM_BINARY_NAME }}
BUNDLE_NAME: ${{ inputs.circuit-name-binary }}-${{ steps.parse-circuit-path.outputs.BUNDLE_TRIPLET }}
WITNESS_GENERATOR_DIR: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
run: |
BUNDLE_DIR="${BUNDLE_NAME}/witness-generator"
mkdir -p "$BUNDLE_DIR"
mv "${WITNESS_GENERATOR_DIR}/${CIRCUIT_NAME}" "$BUNDLE_DIR/${PLATFORM_BINARY_NAME}"
mv "${WITNESS_GENERATOR_DIR}/${CIRCUIT_NAME}.dat" "$BUNDLE_DIR/${PLATFORM_BINARY_NAME}.dat"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Upload ${{ inputs.circuit-name-display }}
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: ${{ inputs.circuit-name-binary }}-${{ steps.parse-circuit-path.outputs.BUNDLE_TRIPLET }}.tar.gz
path: ${{ inputs.circuit-name-binary }}-${{ steps.parse-circuit-path.outputs.BUNDLE_TRIPLET }}.tar.gz

118
.github/resources/prover/Makefile vendored Normal file
View File

@ -0,0 +1,118 @@
###
EXTRA_CMAKE_FLAGS ?=
LIBGOMP_A := $(shell $(CXX) --print-file-name=libgomp.a)
#Build targets
# Add EXTRA_CMAKE_FLAGS
host:
rm -rf build_prover && mkdir build_prover && cd build_prover && \
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=../package \
$(EXTRA_CMAKE_FLAGS) && \
make -j$(nproc) -vvv && make install
host_linux_x86_64_static:
rm -rf build_prover && mkdir build_prover && cd build_prover && \
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=../package \
-DCMAKE_EXE_LINKER_FLAGS="-static -static-libstdc++ -static-libgcc -no-pie" \
-DOpenMP_gomp_LIBRARY=$(LIBGOMP_A) \
-DCMAKE_PREFIX_PATH=depends/gmp/package && \
make -j$(nproc) -vvv && make install
host_windows_x86_64_static:
rm -rf build_prover && mkdir build_prover && cd build_prover && \
cmake .. \
-DUSE_ASM=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=../package \
-DCMAKE_EXE_LINKER_FLAGS="-static -static-libstdc++ -static-libgcc -L/lib -lmman" \
-DCMAKE_CXX_FLAGS="-I/include -include mman_patch.hpp -include cstdint -Duint=unsigned\ int -Du_int32_t=uint32_t -Du_int64_t=uint64_t" && \
make -j$(nproc) -vvv && make install
host_noasm:
rm -rf build_prover_noasm && mkdir build_prover_noasm && cd build_prover_noasm && \
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_noasm -DUSE_ASM=NO && \
make -j$(nproc) -vvv && make install
host_arm64:
rm -rf build_prover_arm64 && mkdir build_prover_arm64 && cd build_prover_arm64 && \
cmake .. -DTARGET_PLATFORM=aarch64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_arm64 && \
make -j$(nproc) -vvv && make install
android:
rm -rf build_prover_android && mkdir build_prover_android && cd build_prover_android && \
cmake .. -DTARGET_PLATFORM=ANDROID -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android -DBUILD_TESTS=OFF -DUSE_OPENMP=OFF && \
make -j$(nproc) -vvv && make install
android_openmp:
rm -rf build_prover_android_openmp && mkdir build_prover_android_openmp && cd build_prover_android_openmp && \
cmake .. -DTARGET_PLATFORM=ANDROID -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android_openmp -DBUILD_TESTS=OFF -DUSE_OPENMP=ON && \
make -j$(nproc) -vvv && make install
android_x86_64:
rm -rf build_prover_android_x86_64 && mkdir build_prover_android_x86_64 && cd build_prover_android_x86_64 && \
cmake .. -DTARGET_PLATFORM=ANDROID_x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android_x86_64 -DBUILD_TESTS=OFF -DUSE_OPENMP=OFF && \
make -j$(nproc) -vvv && make install
android_openmp_x86_64:
rm -rf build_prover_android_openmp_x86_64 && mkdir build_prover_android_openmp_x86_64 && cd build_prover_android_openmp_x86_64 && \
cmake .. -DTARGET_PLATFORM=ANDROID_x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_android_openmp_x86_64 -DBUILD_TESTS=OFF -DUSE_OPENMP=ON && \
make -j$(nproc) -vvv && make install
ios:
@if [ ! -d "./depends/gmp/package_ios_arm64" ]; then echo "Looks like gmp lib is not built. Run './build_gmp.sh ios' first." && exit 1; fi
rm -rf build_prover_ios && mkdir build_prover_ios && cd build_prover_ios && \
cmake .. -GXcode -DTARGET_PLATFORM=IOS -DCMAKE_INSTALL_PREFIX=../package_ios && \
xcodebuild -destination 'generic/platform=iOS' -scheme rapidsnarkStatic -project rapidsnark.xcodeproj -configuration Release && \
xcodebuild -destination 'generic/platform=iOS' -scheme rapidsnark -project rapidsnark.xcodeproj -configuration Release CODE_SIGNING_ALLOWED=NO && \
cp ../depends/gmp/package_ios_arm64/lib/libgmp.a src/Release-iphoneos && \
echo "" && echo "iOS Simulator artifacts built in build_prover_ios/src/Release-iphoneos" && echo ""
ios_simulator:
@if [ ! -d "./depends/gmp/package_iphone_simulator" ]; then echo "Looks like gmp lib is not built. Run './build_gmp.sh ios_simulator' first." && exit 1; fi
rm -rf build_prover_ios_simulator && mkdir build_prover_ios_simulator && cd build_prover_ios_simulator && \
cmake .. -GXcode -DTARGET_PLATFORM=IOS_SIMULATOR -DCMAKE_INSTALL_PREFIX=../package_ios_simulator -DUSE_ASM=NO && \
xcodebuild -destination 'generic/platform=iOS Simulator' -scheme rapidsnarkStatic -project rapidsnark.xcodeproj && \
xcodebuild -destination 'generic/platform=iOS Simulator' -scheme rapidsnark -project rapidsnark.xcodeproj CODE_SIGNING_ALLOWED=NO ARCHS=arm64 && \
cp ../depends/gmp/package_iphone_simulator/lib/libgmp.a src/Debug-iphonesimulator && \
echo "" && echo "iOS Simulator artifacts built in build_prover_ios_simulator/src/Debug-iphonesimulator" && echo ""
macos_arm64:
@if [ ! -d "./depends/gmp/package_macos_arm64" ]; then echo "Looks like gmp lib is not built. Run './build_gmp.sh macos_arm64' first." && exit 1; fi
rm -rf build_prover_macos_arm64 && mkdir build_prover_macos_arm64 && cd build_prover_macos_arm64 && \
cmake .. -DTARGET_PLATFORM=macos_arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_macos_arm64 && \
make -j$(nproc) -vvv && make install
macos_x86_64:
@if [ ! -d "./depends/gmp/package_macos_x86_64" ]; then echo "Looks like gmp lib is not built. Run './build_gmp.sh macos_x86_64' first." && exit 1; fi
rm -rf build_prover_macos_x86_64 && mkdir build_prover_macos_x86_64 && cd build_prover_macos_x86_64 && \
cmake .. -DTARGET_PLATFORM=macos_x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../package_macos_x86_64 && \
make -j$(nproc) -vvv && make install
clean:
rm -rf build_prover \
build_prover_macos_arm64 \
build_prover_macos_x86_64 \
build_prover_android \
build_prover_android_x86_64 \
build_prover_ios \
build_prover_ios_simulator \
package \
package_macos_arm64 \
package_macos_x86_64 \
package_android \
package_android_x86_64 \
package_ios \
package_ios_simulator \
depends/gmp/package \
depends/gmp/package_macos_arm64 \
depends/gmp/package_macos_x86_64 \
depends/gmp/package_android_arm64 \
depends/gmp/package_android_x86_64 \
depends/gmp/package_ios_arm64 \
depends/gmp/package_iphone_simulator

423
.github/resources/prover/windows.build_gmp.sh vendored Executable file
View File

@ -0,0 +1,423 @@
#!/usr/bin/env bash
set -e
NPROC=8
fetch_cmd=$( (type wget > /dev/null 2>&1 && echo "wget") || echo "curl -O" )
usage()
{
echo "USAGE: $0 <target>"
echo "where target is one of:"
echo " ios: build for iOS arm64"
echo " ios_simulator: build for iPhone Simulator for arm64/x86_64 (fat binary)"
echo " macos: build for macOS for arm64/x86_64 (fat binary)"
echo " macos_arm64: build for macOS arm64"
echo " macos_x86_64: build for macOS x86_64"
echo " android: build for Android arm64"
echo " android_x86_64: build for Android x86_64"
echo " host: build for this host"
echo " host_noasm: build for this host without asm optimizations (e.g. needed for macOS)"
echo " aarch64: build for Linux aarch64"
exit 1
}
get_gmp()
{
GMP_NAME=gmp-6.2.1
GMP_ARCHIVE=${GMP_NAME}.tar.xz
GMP_URL=https://ftp.gnu.org/gnu/gmp/${GMP_ARCHIVE}
if [ ! -f ${GMP_ARCHIVE} ]; then
$fetch_cmd ${GMP_URL}
fi
if [ ! -d gmp ]; then
tar -xvf ${GMP_ARCHIVE}
mv ${GMP_NAME} gmp
fi
}
build_aarch64()
{
PACKAGE_DIR="$GMP_DIR/package_aarch64"
BUILD_DIR=build_aarch64
if [ -d "$PACKAGE_DIR" ]; then
echo "aarch64 package is built already. See $PACKAGE_DIR"
return 1
fi
export TARGET=aarch64-linux-gnu
echo $TARGET
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --host $TARGET --prefix="$PACKAGE_DIR" --with-pic --disable-fft &&
make -j${NPROC} &&
make install
cd ..
}
build_host()
{
PACKAGE_DIR="$GMP_DIR/package"
BUILD_DIR=build
if [ -d "$PACKAGE_DIR" ]; then
echo "Host package is built already. See $PACKAGE_DIR"
return 1
fi
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
# The following line is a workaround for the Windows build due to an existing incompatibility with current compiler
# when building GMP.
CFLAGS="-O2 -std=gnu17" CXXFLAGS="-O2 -std=gnu++17" \
../configure --prefix="$PACKAGE_DIR" --with-pic --disable-fft &&
make -j${NPROC} &&
make install
cd ..
}
build_host_noasm()
{
PACKAGE_DIR="$GMP_DIR/package"
BUILD_DIR=build
if [ -d "$PACKAGE_DIR" ]; then
echo "Host package is built already. See $PACKAGE_DIR"
return 1
fi
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --prefix="$PACKAGE_DIR" --with-pic --disable-fft --disable-assembly &&
make -j${NPROC} &&
make install
cd ..
}
build_android()
{
PACKAGE_DIR="$GMP_DIR/package_android_arm64"
BUILD_DIR=build_android_arm64
if [ -d "$PACKAGE_DIR" ]; then
echo "Android package is built already. See $PACKAGE_DIR"
return 1
fi
if [ -z "$ANDROID_NDK" ]; then
echo "ERROR: ANDROID_NDK environment variable is not set."
echo " It must be an absolute path to the root directory of Android NDK."
echo " For instance /home/test/Android/Sdk/ndk/23.1.7779620"
return 1
fi
if [ "$(uname)" == "Darwin" ]; then
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64
else
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64
fi
export TARGET=aarch64-linux-android
export API=21
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export AS=$CC
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
echo "$TOOLCHAIN"
echo "$TARGET"
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --host $TARGET --prefix="$PACKAGE_DIR" --with-pic --disable-fft &&
make -j${NPROC} &&
make install
cd ..
}
build_android_x86_64()
{
PACKAGE_DIR="$GMP_DIR/package_android_x86_64"
BUILD_DIR=build_android_x86_64
if [ -d "$PACKAGE_DIR" ]; then
echo "Android package is built already. See $PACKAGE_DIR"
return 1
fi
if [ -z "$ANDROID_NDK" ]; then
echo "ERROR: ANDROID_NDK environment variable is not set."
echo " It must be an absolute path to the root directory of Android NDK."
echo " For instance /home/test/Android/Sdk/ndk/23.1.7779620"
return 1
fi
if [ "$(uname)" == "Darwin" ]; then
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64
else
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64
fi
export TARGET=x86_64-linux-android
export API=21
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export AS=$CC
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
echo "$TOOLCHAIN"
echo $TARGET
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --host $TARGET --prefix="$PACKAGE_DIR" --with-pic --disable-fft &&
make -j${NPROC} &&
make install
cd ..
}
build_ios()
{
PACKAGE_DIR="$GMP_DIR/package_ios_arm64"
BUILD_DIR=build_ios_arm64
if [ -d "$PACKAGE_DIR" ]; then
echo "iOS package is built already. See $PACKAGE_DIR"
return 1
fi
export SDK="iphoneos"
export TARGET=arm64-apple-darwin
export MIN_IOS_VERSION=8.0
export ARCH_FLAGS="-arch arm64 -arch arm64e"
export OPT_FLAGS="-O3 -g3 -fembed-bitcode"
HOST_FLAGS="${ARCH_FLAGS} -miphoneos-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CC=$(xcrun --find --sdk "${SDK}" clang)
export CC
CXX=$(xcrun --find --sdk "${SDK}" clang++)
export CXX
CPP=$(xcrun --find --sdk "${SDK}" cpp)
export CPP
export CFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
export CXXFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
export LDFLAGS="${HOST_FLAGS}"
echo $TARGET
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --host $TARGET --prefix="$PACKAGE_DIR" --with-pic --disable-fft --disable-assembly &&
make -j${NPROC} &&
make install
cd ..
}
build_ios_simulator()
{
libs=()
for ARCH in "arm64" "x86_64"; do
case "$ARCH" in
"arm64" )
echo "Building for iPhone Simulator arm64"
ARCH_FLAGS="-arch arm64 -arch arm64e"
;;
"x86_64" )
echo "Building for iPhone Simulator x86_64"
ARCH_FLAGS="-arch x86_64"
;;
* )
echo "Incorrect iPhone Simulator arch"
exit 1
esac
BUILD_DIR="build_iphone_simulator_${ARCH}"
PACKAGE_DIR="$GMP_DIR/package_iphone_simulator_${ARCH}"
libs+=("${PACKAGE_DIR}/lib/libgmp.a")
if [ -d "$PACKAGE_DIR" ]; then
echo "iPhone Simulator ${ARCH} package is built already. See $PACKAGE_DIR. Skip building this ARCH."
continue
fi
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --prefix="${PACKAGE_DIR}" \
CC="$(xcrun --sdk iphonesimulator --find clang)" \
CFLAGS="-O3 -isysroot $(xcrun --sdk iphonesimulator --show-sdk-path) ${ARCH_FLAGS} -fvisibility=hidden -mios-simulator-version-min=8.0" \
LDFLAGS="" \
--host ${ARCH}-apple-darwin --disable-assembly --enable-static --disable-shared --with-pic &&
make -j${NPROC} &&
make install
cd ..
done
mkdir -p "${GMP_DIR}/package_iphone_simulator/lib"
lipo "${libs[@]}" -create -output "${GMP_DIR}/package_iphone_simulator/lib/libgmp.a"
echo "Wrote universal fat library for iPhone Simulator arm64/x86_64 to ${GMP_DIR}/package_iphone_simulator/lib/libgmp.a"
}
build_macos_arch()
{
ARCH="$1"
case "$ARCH" in
"arm64" )
ARCH_FLAGS="-arch arm64 -arch arm64e"
;;
"x86_64" )
ARCH_FLAGS="-arch x86_64"
;;
* )
echo "Incorrect arch"
exit 1
esac
BUILD_DIR="build_macos_${ARCH}"
PACKAGE_DIR="$GMP_DIR/package_macos_${ARCH}"
if [ -d "$PACKAGE_DIR" ]; then
echo "macOS ${ARCH} package is built already. See $PACKAGE_DIR. Skip building this ARCH."
return
fi
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
../configure --prefix="${PACKAGE_DIR}" \
CC="$(xcrun --sdk macosx --find clang)" \
CFLAGS="-O3 -isysroot $(xcrun --sdk macosx --show-sdk-path) ${ARCH_FLAGS} -fvisibility=hidden -mmacos-version-min=14.0" \
LDFLAGS="" \
--host "${ARCH}-apple-darwin" --disable-assembly --enable-static --disable-shared --with-pic &&
make -j${NPROC} &&
make install
cd ..
}
build_macos_fat()
{
echo "Building for macOS arm64"
build_macos_arch "arm64"
echo "Building for macOS x86_64"
build_macos_arch "x86_64"
gmp_lib_arm64="$GMP_DIR/package_macos_arm64/lib/libgmp.a"
gmp_lib_x86_64="$GMP_DIR/package_macos_x86_64/lib/libgmp.a"
gmp_lib_fat="$GMP_DIR/package_macos/lib/libgmp.a"
mkdir -p "${GMP_DIR}/package_macos/lib"
lipo "${gmp_lib_arm64}" "${gmp_lib_x86_64}" -create -output "${gmp_lib_fat}"
mkdir -p "${GMP_DIR}/package_macos/include"
cp "${GMP_DIR}/package_macos_arm64/include/gmp.h" "${GMP_DIR}/package_macos/include/"
echo "Wrote universal fat library for macOS arm64/x86_64 to ${GMP_DIR}/package_macos/lib/libgmp.a"
}
if [ $# -ne 1 ]; then
usage
fi
TARGET_PLATFORM=$(echo "$1" | tr "[:upper:]" "[:lower:]")
cd depends
get_gmp
cd gmp
GMP_DIR=$PWD
case "$TARGET_PLATFORM" in
"ios" )
echo "Building for ios"
build_ios
;;
"ios_simulator" )
echo "Building for iPhone Simulator"
build_ios_simulator
;;
"macos" )
echo "Building fat library for macOS"
build_macos_fat
;;
"macos_arm64" )
echo "Building library for macOS arm64"
build_macos_arch "arm64"
;;
"macos_x86_64" )
echo "Building library for macOS x86_64"
build_macos_arch "x86_64"
;;
"android" )
echo "Building for android"
build_android
;;
"android_x86_64" )
echo "Building for android x86_64"
build_android_x86_64
;;
"host" )
echo "Building for this host"
build_host
;;
"host_noasm" )
echo "Building for this host without asm optimizations (e.g. needed for macOS)"
build_host_noasm
;;
"aarch64" )
echo "Building for linux aarch64"
build_aarch64
;;
* )
usage
esac

View File

@ -0,0 +1,7 @@
// Workaround for Windows where some mman functions are not defined
#ifdef _WIN32
#include <cstddef>
inline int madvise(void*, size_t, int) { return 0; }
#define MADV_SEQUENTIAL 0
#endif

View File

@ -0,0 +1,181 @@
# Workaround for CMake on Windows to
# Added explicit linking (e.g., mman), which are otherwise resolved transitively on Linux and macOS.
find_library(MMAN_LIB mman PATHS /lib NO_DEFAULT_PATH REQUIRED)
link_libraries(${GMP_LIB})
add_definitions(${GMP_DEFINIONS})
if(USE_ASM)
if(ARCH MATCHES "arm64")
add_definitions(-DUSE_ASM -DARCH_ARM64)
elseif(ARCH MATCHES "x86_64")
add_definitions(-DUSE_ASM -DARCH_X86_64)
endif()
endif()
if(DEFINED BITS_PER_CHUNK)
add_definitions(-DMSM_BITS_PER_CHUNK=${BITS_PER_CHUNK})
endif()
if(USE_ASM AND ARCH MATCHES "x86_64")
if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin" AND NOT TARGET_PLATFORM MATCHES "^android(_x86_64)?")
set(NASM_FLAGS -fmacho64 --prefix _)
else()
set(NASM_FLAGS -felf64 -DPIC)
endif()
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/build/fq_asm.o
COMMAND nasm ${NASM_FLAGS} fq.asm -o fq_asm.o
DEPENDS ${CMAKE_SOURCE_DIR}/build/fq.asm
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/build/fr_asm.o
COMMAND nasm ${NASM_FLAGS} fr.asm -o fr_asm.o
DEPENDS ${CMAKE_SOURCE_DIR}/build/fr.asm
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
endif()
set(FR_SOURCES
../build/fr.hpp
../build/fr.cpp
)
if(USE_ASM)
if(ARCH MATCHES "arm64")
set(FR_SOURCES ${FR_SOURCES} ../build/fr_raw_arm64.s ../build/fr_raw_generic.cpp ../build/fr_generic.cpp)
elseif(ARCH MATCHES "x86_64")
set(FR_SOURCES ${FR_SOURCES} ../build/fr_asm.o)
endif()
else()
set(FR_SOURCES ${FR_SOURCES} ../build/fr_generic.cpp ../build/fr_raw_generic.cpp)
endif()
add_library(fr STATIC ${FR_SOURCES})
set_target_properties(fr PROPERTIES POSITION_INDEPENDENT_CODE ON)
link_libraries(fr)
set(FQ_SOURCES
../build/fq.hpp
../build/fq.cpp
)
if(USE_ASM)
if(ARCH MATCHES "arm64")
set(FQ_SOURCES ${FQ_SOURCES} ../build/fq_raw_arm64.s ../build/fq_raw_generic.cpp ../build/fq_generic.cpp)
elseif(ARCH MATCHES "x86_64")
set(FQ_SOURCES ${FQ_SOURCES} ../build/fq_asm.o)
endif()
else()
set(FQ_SOURCES ${FQ_SOURCES} ../build/fq_raw_generic.cpp ../build/fq_generic.cpp)
endif()
add_library(fq STATIC ${FQ_SOURCES})
set_target_properties(fq PROPERTIES POSITION_INDEPENDENT_CODE ON)
link_libraries(fq)
if(OpenMP_CXX_FOUND)
add_definitions(-DUSE_OPENMP)
add_compile_options(${OpenMP_CXX_FLAGS})
endif()
set(LIB_SOURCES
binfile_utils.hpp
binfile_utils.cpp
zkey_utils.hpp
zkey_utils.cpp
wtns_utils.hpp
wtns_utils.cpp
logger.hpp
logger.cpp
fileloader.cpp
fileloader.hpp
prover.cpp
prover.h
verifier.cpp
verifier.h
../depends/ffiasm/c/misc.cpp
../depends/ffiasm/c/naf.cpp
../depends/ffiasm/c/splitparstr.cpp
../depends/ffiasm/c/alt_bn128.cpp
)
if(USE_LOGGER)
set(LIB_SOURCES ${LIB_SOURCES} logger.cpp)
add_definitions(-DUSE_LOGGER)
endif()
include_directories(
../src
../build
../depends/ffiasm/c
../depends/json/single_include
/include
)
add_library(rapidsnarkStatic STATIC ${LIB_SOURCES})
set_target_properties(rapidsnarkStatic PROPERTIES OUTPUT_NAME rapidsnark)
add_library(rapidsnarkStaticFrFq STATIC ${LIB_SOURCES} ${FQ_SOURCES} ${FR_SOURCES})
set_target_properties(rapidsnarkStaticFrFq PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(rapidsnarkStaticFrFq PROPERTIES OUTPUT_NAME rapidsnark-fr-fq)
add_executable(prover main_prover.cpp)
target_link_libraries(
prover rapidsnarkStatic
fr fq ${MMAN_LIB} # Explicit linking
)
add_executable(verifier main_verifier.cpp)
target_link_libraries(
verifier rapidsnarkStatic
fr fq ${MMAN_LIB} # Explicit linking
)
add_library(rapidsnark SHARED ${LIB_SOURCES})
target_link_libraries(rapidsnark ${MMAN_LIB}) # Explicit linking
if((USE_LOGGER OR NOT USE_OPENMP) AND NOT TARGET_PLATFORM MATCHES "android")
target_link_libraries(prover pthread)
target_link_libraries(verifier pthread)
endif()
if(USE_SODIUM)
target_link_libraries(prover sodium)
endif()
option(BUILD_TESTS "Build the tests" ON)
if(BUILD_TESTS)
enable_testing()
add_executable(test_public_size test_public_size.c)
target_link_libraries(
test_public_size rapidsnarkStaticFrFq pthread
${MMAN_LIB} # Explicit linking
)
add_test(NAME test_public_size COMMAND test_public_size circuit_final.zkey 86
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdata)
endif()
if(OpenMP_CXX_FOUND)
if(TARGET_PLATFORM MATCHES "android")
target_link_libraries(prover -static-openmp -fopenmp)
target_link_libraries(verifier -static-openmp -fopenmp)
target_link_libraries(rapidsnark -static-openmp -fopenmp)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(prover OpenMP::OpenMP_CXX)
target_link_libraries(verifier OpenMP::OpenMP_CXX)
target_link_libraries(test_public_size OpenMP::OpenMP_CXX)
endif()
endif()
add_executable(test_prover test_prover.cpp)

19
.github/resources/prover/windows.uio.h vendored Normal file
View File

@ -0,0 +1,19 @@
// Workaround for Windows UIO header, as it's not available by default
#ifndef SYS_UIO_H
#define SYS_UIO_H
#include <inttypes.h>
#include <unistd.h>
struct iovec
{
void *iov_base;
size_t iov_len;
};
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);
#endif

View File

@ -0,0 +1,43 @@
.PHONY: linux macos windows build clean
# ---- Arguments ----
ifndef PROJECT
$(error PROJECT is not set. Usage: make PROJECT=<name>. E.g.: make PROJECT=pol linux)
endif
# ---- Common ----
CXX := g++
CXXFLAGS_COMMON := -std=c++11 -O3 -I. -Wno-address-of-packed-member
SRCS := main.cpp calcwit.cpp fr.cpp $(PROJECT).cpp
OBJS := $(SRCS:.cpp=.o)
DEPS_HPP := circom.hpp calcwit.hpp fr.hpp
BIN := $(PROJECT)
# ---- Linux ----
linux: CXXFLAGS=$(CXXFLAGS_COMMON)
linux: LDFLAGS=-static
linux: LDLIBS=-lgmp
linux: $(BIN)
# ---- macOS ----
macos: CXXFLAGS=$(CXXFLAGS_COMMON) -I/opt/homebrew/include -include gmp_patch.hpp
macos: LDFLAGS=-Wl,-search_paths_first -Wl,-dead_strip
macos: LDLIBS=/opt/homebrew/lib/libgmp.a
macos: $(BIN)
# ---- Windows (MinGW) ----
windows: CXXFLAGS=$(CXXFLAGS_COMMON) -I/include -Duint="unsigned int"
windows: LDFLAGS=-static
windows: LDLIBS=-L/lib -lgmp -lmman
windows: $(BIN)
# ---- Rules ----
$(BIN): $(OBJS)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
%.o: %.cpp $(DEPS_HPP)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(BIN)

View File

@ -0,0 +1,150 @@
// Workaround for a known macOS issue where certain GMP functions fail to compile
// due to strict type checking, despite uint64_t and mp_limb_t being the same size.
// These wrappers explicitly cast uint64_t parameters to mp_limb_t to resolve the mismatch.
#ifndef GMP_PATCH_CAST_HPP
#define GMP_PATCH_CAST_HPP
#include <gmp.h>
#include <cstdint>
// Arithmetic Wrappers
inline mp_limb_t gmp_add_n_cast(uint64_t* rp, const uint64_t* up, const uint64_t* vp, mp_size_t n) {
return mpn_add_n(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
inline mp_limb_t gmp_sub_n_cast(uint64_t* rp, const uint64_t* up, const uint64_t* vp, mp_size_t n) {
return mpn_sub_n(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
inline mp_limb_t gmp_add_1_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, mp_limb_t b) {
return mpn_add_1(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, b);
}
inline mp_limb_t gmp_sub_1_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, mp_limb_t b) {
return mpn_sub_1(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, b);
}
inline int gmp_cmp_cast(const uint64_t* up, const uint64_t* vp, mp_size_t n) {
return mpn_cmp(reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
inline void gmp_copyi_cast(uint64_t* dst, const uint64_t* src, mp_size_t n) {
mpn_copyi(reinterpret_cast<mp_limb_t*>(dst),
reinterpret_cast<const mp_limb_t*>(src),
n);
}
inline mp_limb_t gmp_mul_1_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, mp_limb_t b) {
return mpn_mul_1(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, b);
}
inline mp_limb_t gmp_addmul_1_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, mp_limb_t b) {
return mpn_addmul_1(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, b);
}
inline mp_limb_t gmp_add_cast(uint64_t* rp, const uint64_t* up, mp_size_t un, const uint64_t* vp, mp_size_t vn) {
return mpn_add(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up), un,
reinterpret_cast<const mp_limb_t*>(vp), vn);
}
// Logic/Bitwise Wrappers
inline int gmp_zero_p_cast(const uint64_t* up, mp_size_t n) {
return mpn_zero_p(reinterpret_cast<const mp_limb_t*>(up), n);
}
inline void gmp_and_n_cast(uint64_t* rp, const uint64_t* up, const uint64_t* vp, mp_size_t n) {
mpn_and_n(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
inline void gmp_com_cast(uint64_t* rp, const uint64_t* up, mp_size_t n) {
mpn_com(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n);
}
inline void gmp_ior_n_cast(uint64_t* rp, const uint64_t* up, const uint64_t* vp, mp_size_t n) {
mpn_ior_n(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
inline void gmp_xor_n_cast(uint64_t* rp, const uint64_t* up, const uint64_t* vp, mp_size_t n) {
mpn_xor_n(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
reinterpret_cast<const mp_limb_t*>(vp),
n);
}
// Shift Wrappers
inline mp_limb_t gmp_lshift_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, unsigned int cnt) {
return mpn_lshift(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, cnt);
}
inline mp_limb_t gmp_rshift_cast(uint64_t* rp, const uint64_t* up, mp_size_t n, unsigned int cnt) {
return mpn_rshift(reinterpret_cast<mp_limb_t*>(rp),
reinterpret_cast<const mp_limb_t*>(up),
n, cnt);
}
// Undefine existing GMP macros
#undef mpn_add_n
#undef mpn_sub_n
#undef mpn_add_1
#undef mpn_sub_1
#undef mpn_cmp
#undef mpn_copyi
#undef mpn_mul_1
#undef mpn_addmul_1
#undef mpn_add
#undef mpn_zero_p
#undef mpn_and_n
#undef mpn_com
#undef mpn_ior_n
#undef mpn_xor_n
#undef mpn_lshift
#undef mpn_rshift
// Redefine GMP macros to wrappers
#define mpn_add_n gmp_add_n_cast
#define mpn_sub_n gmp_sub_n_cast
#define mpn_add_1 gmp_add_1_cast
#define mpn_sub_1 gmp_sub_1_cast
#define mpn_cmp gmp_cmp_cast
#define mpn_copyi gmp_copyi_cast
#define mpn_mul_1 gmp_mul_1_cast
#define mpn_addmul_1 gmp_addmul_1_cast
#define mpn_add gmp_add_cast
#define mpn_zero_p gmp_zero_p_cast
#define mpn_and_n gmp_and_n_cast
#define mpn_com gmp_com_cast
#define mpn_ior_n gmp_ior_n_cast
#define mpn_xor_n gmp_xor_n_cast
#define mpn_lshift gmp_lshift_cast
#define mpn_rshift gmp_rshift_cast
#endif // GMP_PATCH_CAST_HPP

537
.github/workflows/build-circuits.yml vendored Normal file
View File

@ -0,0 +1,537 @@
name: Build Circuits
on:
push:
tags:
- "circom_circuits-v*.*.*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release. Must follow the format of 'circom_circuits-vX.Y.Z'."
required: true
jobs:
setup:
name: Configure Environment
runs-on: ubuntu-latest
outputs:
version: ${{ steps.define-version.outputs.version }}
tag: ${{ steps.define-version.outputs.tag }}
steps:
- name: Define version
id: define-version
env:
# Use the tag name if it is available, otherwise use the input version.
# If neither is available, default to the commit hash.
TAG: ${{ (github.ref_type == 'tag' && github.ref_name) || inputs.tag || '' }}
run: |
if [ -z "$TAG" ]; then
echo "Could not determine tag."
exit 1
elif [[ ! "$TAG" =~ ^circom_circuits-v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "TAG must follow the format of 'circom_circuits-vX.Y.Z'. Value: '$VERSION'."
exit 2
fi
# Parse Version: Take only the vX.Y.Z part.
VERSION=$(echo $TAG | cut -d'-' -f2)
# Export the tag and version.
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
build-linux:
name: Build Linux Binaries (Native)
runs-on: ubuntu-latest
needs:
- setup
env:
VERSION: ${{ needs.setup.outputs.version }}
OS: linux
ARCH: x86_64
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Setup Dependencies
working-directory: circom_circuits/rapidsnark
run: sudo apt update -y
- name: Install Dependencies [Prover]
run: sudo apt install -y build-essential cmake libgmp-dev libsodium-dev nasm curl m4
- name: Install Dependencies [Witness Generator]
run: sudo apt install nlohmann-json3-dev
- name: Replace Prover Makefile # TODO: Make a fork generate the appropriate Linux Makefile
run: cp .github/resources/prover/Makefile circom_circuits/rapidsnark/Makefile
- name: Compile Prover and Verifier
working-directory: circom_circuits/rapidsnark
run: |
./build_gmp.sh host
make host_linux_x86_64_static
- name: Bundle Rapidsnark Prover
env:
BINARY_NAME: prover
BUNDLE_NAME: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}"/bin/${BINARY_NAME} "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Bundle Rapidsnark Verifier
env:
BINARY_NAME: verifier
BUNDLE_NAME: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}"/bin/${BINARY_NAME} "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Upload Rapidsnark Prover
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Upload Rapidsnark Verifier
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Compile and Bundle PoL
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoL"
circuit-name-binary: "pol"
circuit-path: "circom_circuits/Mantle/pol.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoQ
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoQ"
circuit-name-binary: "poq"
circuit-path: "circom_circuits/Blend/poq.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle ZKSign
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-path: "circom_circuits/Mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoC
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoC"
circuit-name-binary: "poc"
circuit-path: "circom_circuits/Mantle/poc.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
build-windows:
name: Build Windows Binaries (Native)
runs-on: windows-latest
needs:
- setup
env:
VERSION: ${{ needs.setup.outputs.version }}
OS: windows
ARCH: x86_64
steps:
- name: Setup MSYS2
uses: msys2/setup-msys2@v2.28.0 # FIXME: Ideally use the hash to avoid supply chain attacks. Currently unable to.
with:
update: true
install: >-
base-devel
mingw-w64-x86_64-toolchain
make
git
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
$env:RUSTFLAGS="-A dead_code"; cargo build --release
$env:RUSTFLAGS="-A dead_code"; cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Dependencies [Witness Generator]
shell: msys2 {0}
run: |
# nlohmann/json
mkdir -p /include/nlohmann/
wget -O /include/nlohmann/json.hpp https://github.com/nlohmann/json/releases/download/v3.12.0/json.hpp
# mman-win32
git clone https://github.com/alitrack/mman-win32.git
pushd mman-win32
./configure --prefix= # Path: /
make
make install
popd
- name: Install Dependencies [Prover]
shell: msys2 {0}
run: pacman --noconfirm -Sy --needed cmake nasm mingw-w64-ucrt-x86_64-libsodium
- name: Replace Prover Makefile # TODO: Make a fork generate the appropriate Windows Makefile
shell: msys2 {0}
run: cp .github/resources/prover/Makefile circom_circuits/rapidsnark/Makefile
- name: Replace Prover CMakeLists
shell: msys2 {0}
run: cp .github/resources/prover/${{ env.OS }}.src-CMakeLists.txt circom_circuits/rapidsnark/src/CMakeLists.txt
- name: Patch Windows mman
shell: msys2 {0}
run: cp .github/resources/prover/${{ env.OS }}.mman_patch.hpp /include/mman_patch.hpp
- name: Add uio.h headers
shell: msys2 {0}
run: cp .github/resources/prover/${{ env.OS }}.uio.h /include/sys/uio.h
- name: Replace build_gmp
shell: msys2 {0}
run: cp .github/resources/prover/${{ env.OS }}.build_gmp.sh circom_circuits/rapidsnark/build_gmp.sh
- name: Compile Prover and Verifier
shell: msys2 {0}
working-directory: circom_circuits/rapidsnark
run: |
./build_gmp.sh host
make host_windows_x86_64_static
- name: Bundle Rapidsnark Prover
shell: msys2 {0}
env:
BINARY_NAME: prover
BUNDLE_NAME: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}"/bin/${BINARY_NAME}.exe "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Bundle Rapidsnark Verifier
shell: msys2 {0}
env:
BINARY_NAME: verifier
BUNDLE_NAME: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}"/bin/${BINARY_NAME}.exe "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Upload Rapidsnark Prover
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Upload Rapidsnark Verifier
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Compile and Bundle PoL
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoL"
circuit-name-binary: "pol"
circuit-path: "circom_circuits/Mantle/pol.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoQ
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoQ"
circuit-name-binary: "poq"
circuit-path: "circom_circuits/Blend/poq.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle ZKSign
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-path: "circom_circuits/Mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoC
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoC"
circuit-name-binary: "poc"
circuit-path: "circom_circuits/Mantle/poc.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
build-macos:
name: Build MacOS Binaries (Native)
runs-on: macos-latest
needs:
- setup
env:
VERSION: ${{ needs.setup.outputs.version }}
ARCH: aarch64
OS: macos
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Setup Dependencies
run: mkdir include
- name: Install Dependencies [Witness Generator]
run: brew install nlohmann-json
- name: Install Dependencies [Prover]
run: brew install nasm m4
- name: Compile Prover and Verifier
working-directory: circom_circuits/rapidsnark
run: |
./build_gmp.sh macos_arm64
make macos_arm64
- name: Bundle Rapidsnark Prover
env:
BINARY_NAME: prover
BUNDLE_NAME: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package_macos_arm64
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}/bin/${BINARY_NAME}" "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Bundle Rapidsnark Verifier
env:
BINARY_NAME: verifier
BUNDLE_NAME: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
RAPIDSNARK_DIR: circom_circuits/rapidsnark/package_macos_arm64
run: |
BUNDLE_DIR="${BUNDLE_NAME}/${BINARY_NAME}"
mkdir -p "$BUNDLE_DIR"
mv "${RAPIDSNARK_DIR}/bin/${BINARY_NAME}" "$BUNDLE_DIR/"
tar -czf "${BUNDLE_NAME}.tar.gz" "${BUNDLE_NAME}"
- name: Upload Rapidsnark Prover
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Upload Rapidsnark Verifier
uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8
with:
name: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
- name: Compile and Bundle PoL
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoL"
circuit-name-binary: "pol"
circuit-path: "circom_circuits/Mantle/pol.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoQ
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoQ"
circuit-name-binary: "poq"
circuit-path: "circom_circuits/Blend/poq.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle ZKSign
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-path: "circom_circuits/Mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile and Bundle PoC
uses: ./.github/actions/compile-and-bundle
with:
circuit-name-display: "PoC"
circuit-name-binary: "poc"
circuit-path: "circom_circuits/Mantle/poc.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
publish-release:
name: Create Release
runs-on: ubuntu-latest
needs:
- setup
- build-linux
- build-windows
- build-macos
env:
TAG: ${{ needs.setup.outputs.tag }}
VERSION: ${{ needs.setup.outputs.version }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create Release
uses: actions/create-release@4c11c9fe1dcd9636620a16455165783b20fc7ea0
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.TAG }}
release_name: Circom Circuits ${{ env.VERSION }}
body: |
This is a release of Circom Circuits ${{ env.VERSION }}.
## Changelog
- feature(X): new feature
- fix(Y): bug description
- feature: performance improvement on Z
## Checklist
Before publishing please ensure:
- [ ] Description is complete
- [ ] Changelog is correct
- [ ] Assets for all platforms exist
- [ ] Pre-release is checked if necessary
- [ ] Remove this checklist before publishing the release.
draft: true
prerelease: true
upload-artifacts:
name: Upload Artifacts to Release
runs-on: ubuntu-latest
needs:
- setup
- publish-release
strategy:
fail-fast: false
matrix:
platform:
- os: linux
arch: x86_64
- os: macos
arch: aarch64
- os: windows
arch: x86_64
artifact:
- prover
- verifier
- pol
- poq
- zksign
- poc
env:
UPLOAD_URL: ${{ needs.publish-release.outputs.upload_url }}
ARTIFACT_NAME: ${{ matrix.artifact }}-${{ needs.setup.outputs.version }}-${{ matrix.platform.os }}-${{ matrix.platform.arch }}.tar.gz
steps:
- name: Download Artifacts
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: ${{ env.ARTIFACT_NAME }}
- name: Upload Artifacts to Release
uses: actions/upload-release-asset@ef2adfe8cb8ebfa540930c452c576b3819990faa
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ env.UPLOAD_URL }}
asset_path: ${{ env.ARTIFACT_NAME }}
asset_name: ${{ env.ARTIFACT_NAME }}
asset_content_type: application/octet-stream

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "circomlib"]
path = circom_circuits/circomlib
url = https://github.com/iden3/circomlib.git
[submodule "rapidsnark"]
path = circom_circuits/rapidsnark
url = https://github.com/iden3/rapidsnark

38
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,38 @@
# Contributor's Guide
## Triggering a New Release for Circom Circuits
To trigger a release build:
1. Create and push a tag in the format `circom-circuits_vX.Y.Z`.
2. This will automatically trigger the `.github/workflows/build_circuits.yml` workflow.
3. Once the workflow finishes, the generated artifacts will be attached to a new release.
> Currently, releases published this way are marked as **Draft** and **Pre-Release** to ensure that the changelog and pre-release steps are manually reviewed first.
### Example
```bash
git tag circom_circuits-v1.2.3 -m "Release for Circom Circuits v1.2.3"
git push --tags
```
## Publishing the Release
After triggering the release, it will appear as a **Draft** and **Pre-Release**.
Before making it public, make sure to:
1. **Review the changelog**
Ensure that all relevant changes are clearly listed and properly formatted.
2. **Confirm the pre-release checklist**
Verify that all required steps have been completed, then remove the checklist from the release notes.
Once everything looks good:
3. **Mark the release as published**
- Uncheck **“This is a pre-release.”**
- Publish the release (removing the Draft state).
> ⚡ **Important:** Nomos builds will only pick up the new circuits once the release is published as **Latest** (i.e. not marked as draft or pre-release).

View File

@ -0,0 +1,331 @@
from sage.all import *
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
F = FiniteField(p)
def poseidon2_hash(data):
return PoseidonSponge(data,2,1)[0]
def Poseidon2_sponge_hash_rate_1(data, n):
return PoseidonSponge(data,3,2,n,1)
def Poseidon2_sponge_hash_rate_2(data, n):
return PoseidonSponge(data,3,1,n,1)
def SBox(inp):
return inp**5
def InternalRound(inp, i):
round_consts = [
0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9,
]
sb = SBox(inp[0] + round_consts[i])
out = [F(0) for i in range(3)]
out[0] = 2*sb + inp[1] + inp[2];
out[1] = sb + 2*inp[1] + inp[2];
out[2] = sb + inp[1] + 3*inp[2];
return out
def ExternalRound(inp, i):
out = [F(0) for j in range(3)]
round_consts = [ [F(0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816)
, F(0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610)
, F(0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1)
]
, [ F(0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5)
, F(0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28)
, F(0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735)
]
, [ F(0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a)
, F(0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d)
, F(0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce)
]
, [ F(0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4)
, F(0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e)
, F(0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e)
]
, [ F(0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0)
, F(0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5)
, F(0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893)
]
, [ F(0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d)
, F(0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e)
, F(0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc)
]
, [ F(0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1)
, F(0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe)
, F(0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166)
]
, [ F(0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce)
, F(0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57)
, F(0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6)
]]
sb = [F(0) for j in range(3)]
for j in range(3):
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
out = [F(0) for j in range(3)]
out[0] = 2*sb[0] + sb[1] + sb[2]
out[1] = sb[0] + 2*sb[1] + sb[2]
out[2] = sb[0]+ sb[1] + 2*sb[2]
return out
def LinearLayer(inp):
out = [F(0) for i in range(3)]
out[0] = 2*inp[0] + inp[1] + inp[2]
out[1] = inp[0] + 2*inp[1] + inp[2]
out[2] = inp[0] + inp[1] + 2*inp[2]
return out
def Permutation(inp):
out = [F(0) for i in range(3)]
state = LinearLayer(inp)
for k in range(4):
state = ExternalRound(state, k)
for k in range(56):
state = InternalRound(state, k)
for k in range(4):
state = ExternalRound(state, k+4)
return state
def Compression(inp):
return Permutation([inp[0],inp[1],F(0)])
def PoseidonSponge(data, capacity, output_len):
rate = 3 - capacity;
output = [F(0) for i in range(output_len)]
assert( capacity > 0 )
assert( rate > 0 )
assert( capacity < 3 )
assert( rate < 3 )
# round up to rate the input + 1 field element ("10*" padding)
nblocks = ((len(data) + 1) + (rate-1)) // rate;
nout = (output_len + (rate-1)) // rate;
padded_len = nblocks * rate;
padded = []
for i in range(len(data)):
padded.append(F(data[i]))
padded.append(F(1))
for i in range(len(data)+1,padded_len):
padded.append(F(0))
civ = F(0)
state = [F(0),F(0),F(civ)]
sorbed = [F(0) for j in range(rate)]
for m in range(nblocks):
for i in range(rate):
a = state[i]
b = padded[m*rate+i]
sorbed[i] = a + b
state = Permutation(sorbed[0:rate] + state[rate:3])
q = min(rate, output_len)
for i in range(q):
output[i] = state[i]
out_ptr = rate
for n in range(1,nout):
state[nblocks+n] = Permutation(state[nblocks+n-1])
q = min(rate, output_len-out_ptr)
for i in range(q):
output[out_ptr+i] = state[nblocks+n][i]
out_ptr += rate
return output
# ———————————————————————
# Main
# ———————————————————————
if len(sys.argv) != 5:
print("Usage: python3 generate_inputs_for_poq.py <session> <core_quota> <leader_quota> <core (0) or leader (1)>")
sys.exit(1)
session = int(sys.argv[1])
Qc = int(sys.argv[2])
Ql = int(sys.argv[3])
core_or_leader = int(sys.argv[4])
if not core_or_leader in [0,1]:
print("core or leader must be 0 or 1")
sys.exit(1)
# 1) Corenode registry Merkleproof
# pick a random core_sk and derive its public key
core_sk = F(randrange(0,p,1))
pk_core = poseidon2_hash([ F(1296193216988918402894), core_sk ])
core_selectors = randrange(0,2**20,1)
core_selectors = format(int(core_selectors),'020b')
core_nodes = [F(randrange(0,p,1)) for i in range(20)]
core_root = pk_core
for i in range(20):
if int(core_selectors[19-i]) == 0:
core_root = poseidon2_hash([core_root,core_nodes[i]])
else:
core_root = poseidon2_hash([core_nodes[i],core_root])
#pk_root, core_path, core_selectors = merkle_root_and_path(pk_core, 20)
# 2) PoL inputs (broadened from pol script)
epoch_nonce = F(randrange(0, p,1))
slot_number = F(randrange(0, 2**32,1))
total_stake = int(5000)
t0_constant = F(0x1a3fb997fd58374772808c13d1c2ddacb5ab3ea77413f86fd6e0d3d978e5438)
t1_constant = F(0x71e790b41991052e30c93934b5612412e7958837bac8b1c524c24d84cc7d0)
t0 = F(int(t0_constant) // total_stake)
t1 = F(p- (int(t1_constant) // total_stake**2))
value = F(total_stake / 100)
threshold = (t0 + t1 * value) * value
starting_slot = randrange(max(0,slot_number-2**25+1),slot_number,1)
slot_secret = F(randrange(0,p,1))
slot_secret_indexes = format(int(slot_number - starting_slot),'025b')
tx_hash = F(randrange(0,p,1))
output_number = F(randrange(0,50,1))
slot_secret_path = [F(randrange(0,p,1)) for i in range(25)]
secret_root = slot_secret
for i in range(25):
if int(slot_secret_indexes[24-i]) == 0:
secret_root = poseidon2_hash([secret_root,slot_secret_path[i]])
else:
secret_root = poseidon2_hash([slot_secret_path[i],secret_root])
sk = poseidon2_hash([F(256174383281726064679014503048630094),starting_slot,secret_root])
pk = poseidon2_hash([F(1296193216988918402894),sk])
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
while(ticket > threshold):
output_number += 1
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
aged_nodes = [F(randrange(0,p,1)) for i in range(32)]
aged_selectors = randrange(0,2**32,1)
aged_selectors = format(aged_selectors,'032b')
aged_root = note_id
for i in range(32):
if int(aged_selectors[31-i]) == 0:
aged_root = poseidon2_hash([aged_root,aged_nodes[i]])
else:
aged_root = poseidon2_hash([aged_nodes[i],aged_root])
# 3) Choose branch & index
index = randrange(0, Ql if core_or_leader else Qc,1)
# 4) Onetime key
K_one = F(123456)
K_two = F(654321)
# 5) Assemble JSON
inp = {
"session": str(session),
"core_quota": str(Qc),
"leader_quota": str(Ql),
"core_root": str(core_root),
"pol_ledger_aged": str(aged_root),
"K_part_one": str(K_one),
"K_part_two": str(K_two),
"selector": str(core_or_leader),
"index": str(index),
"core_sk": str(core_sk),
"core_path": [str(x) for x in core_nodes],
"core_path_selectors": [str(x) for x in core_selectors],
"pol_sl": str(slot_number),
"pol_epoch_nonce": str(epoch_nonce),
"pol_t0": str(t0),
"pol_t1": str(t1),
"pol_slot_secret": str(slot_secret),
"pol_slot_secret_path": [str(x) for x in slot_secret_path],
"pol_noteid_path": [str(x) for x in aged_nodes],
"pol_noteid_path_selectors": [str(x) for x in aged_selectors],
"pol_note_tx_hash": str(tx_hash),
"pol_note_output_number": str(output_number),
"pol_sk_starting_slot": str(starting_slot),
"pol_note_value": str(value)
}
if core_or_leader == 0:
inp["pol_ledger_aged"] = str(randrange(0,p,1))
else:
inp["core_root"] = str(randrange(0,p,1))
import json
with open("input.json","w") as f:
json.dump(inp, f, indent=2)
print("Wrote input_poq.json")

137
blend/poq.circom Normal file
View File

@ -0,0 +1,137 @@
// PoQ.circom
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../misc/constants.circom"; // defines NOMOS_KDF, SELECTION_RANDOMNESS, PROOF_NULLIFIER
include "../misc/comparator.circom";
include "../circomlib/circuits/bitify.circom";
include "../Mantle/pol_lib.circom"; // defines proof_of_leadership
include "../ledger/notes.circom";
/**
* ProofOfQuota(nLevelsPK, nLevelsPol)
*
* - nLevelsPK : depth of the core-node public-key registry Merkle tree
* - nLevelsPol : depth of the slot-secret tree used in PoL (25)
* - bitsQuota : bit-width for the index comparator
*/
template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) {
// Public Inputs
signal input session; // session s
signal input core_quota;
signal input leader_quota;
signal input core_root;
signal input pol_ledger_aged; // PoL: aged notes root
signal input K_part_one; // Blend: one-time signature public key
signal input K_part_two; // Blend: one-time signature public key
// dummy constraints to avoid unused public input to be erased after compilation optimisation
signal dummy_one;
dummy_one <== K_part_one * K_part_one;
signal dummy_two;
dummy_two <== K_part_two * K_part_two;
signal output key_nullifier; //key_nullifier
// Private Inputs
signal input selector; // 0 = core, 1 = leader
signal input index; // nullifier index
// Core-nodes inputs
signal input core_sk; // core node secret key
signal input core_path[nLevelsPK]; // Merkle path for core PK
signal input core_path_selectors[nLevelsPK]; // path selectors (bits)
// PoL branch inputs (all the PoL private data)
signal input pol_sl;
signal input pol_epoch_nonce;
signal input pol_t0;
signal input pol_t1;
signal input pol_slot_secret;
signal input pol_slot_secret_path[nLevelsPol];
signal input pol_noteid_path[32];
signal input pol_noteid_path_selectors[32];
signal input pol_note_tx_hash;
signal input pol_note_output_number;
signal input pol_sk_starting_slot;
signal input pol_note_value;
// Constraint the selector to be a bit
selector * (1 - selector) === 0;
// Quota check: index < core_quota if core, index < leader_quota if leader
component cmp = SafeLessThan(bitsQuota);
cmp.in[0] <== index;
cmp.in[1] <== selector * (leader_quota - core_quota) + core_quota;
cmp.out === 1;
// derive zk_id
component zk_id = derive_public_key();
zk_id.secret_key <== core_sk;
// Merkleverify zk_id in core_root
component is_registered = proof_of_membership(nLevelsPK);
for (var i = 0; i < nLevelsPK; i++) {
//check that the selectors are indeed bits
core_path_selectors[i] * (1 - core_path_selectors[i]) === 0;
//call the merkle proof checker
is_registered.nodes[i] <== core_path[i];
is_registered.selector[i] <== core_path_selectors[i];
}
is_registered.root <== core_root;
is_registered.leaf <== zk_id.out;
// enforce potential PoL (without verification that the note is unspent)
// (All constraints inside pol ensure LeadershipVerify)
component would_win = would_win_leadership(nLevelsPol);
would_win.slot <== pol_sl;
would_win.epoch_nonce <== pol_epoch_nonce;
would_win.t0 <== pol_t0;
would_win.t1 <== pol_t1;
would_win.slot_secret <== pol_slot_secret;
for (var i = 0; i < nLevelsPol; i++) {
would_win.slot_secret_path[i] <== pol_slot_secret_path[i];
}
for (var i = 0; i < 32; i++) {
would_win.aged_nodes[i] <== pol_noteid_path[i];
would_win.aged_selectors[i] <== pol_noteid_path_selectors[i];
}
would_win.aged_root <== pol_ledger_aged;
would_win.transaction_hash <== pol_note_tx_hash;
would_win.output_number <== pol_note_output_number;
would_win.starting_slot <== pol_sk_starting_slot;
would_win.value <== pol_note_value;
// Enforce the selected role is correct
selector * (would_win.out - is_registered.out) + is_registered.out === 1;
// Derive selection_randomness
component selection_randomness = Poseidon2_hash(4);
component dstSel = SELECTION_RANDOMNESS_V1();
selection_randomness.inp[0] <== dstSel.out;
// choose core_sk or pol.secret_key:
selection_randomness.inp[1] <== selector * (would_win.secret_key - core_sk ) + core_sk;
selection_randomness.inp[2] <== index;
selection_randomness.inp[3] <== session;
// Derive key_nullifier
component nf = Poseidon2_hash(2);
component dstNF = KEY_NULLIFIER_V1();
nf.inp[0] <== dstNF.out;
nf.inp[1] <== selection_randomness.out;
key_nullifier <== nf.out;
}
// Instantiate with chosen depths: 20 for core PK tree, 25 for PoL secret slot tree
component main { public [ session, core_quota, leader_quota, core_root, K_part_one, K_part_two, pol_epoch_nonce, pol_t0, pol_t1, pol_ledger_aged ] }
= ProofOfQuota(20, 25, 20);

55
hash_bn/merkle.circom Normal file
View File

@ -0,0 +1,55 @@
//test
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../circomlib/circuits/comparators.circom";
// compute a merkle root of depth n
// /!\ To call this function, it's important to check that each selector is a bit before!!!
template compute_merkle_root(n) {
signal input nodes[n]; // The Merkle path
signal input selector[n]; // it's the leaf's indice in big endian bits indicating if complementary nodes are left or right
signal input leaf;
signal output root;
component compression_hash[n];
compression_hash[0] = Poseidon2_hash(2);
compression_hash[0].inp[0] <== leaf - selector[n-1] * (leaf - nodes[0]);
compression_hash[0].inp[1] <== nodes[0] - selector[n-1] * (nodes[0] - leaf);
for(var i=1; i<n; i++){
compression_hash[i] = Poseidon2_hash(2);
compression_hash[i].inp[0] <== compression_hash[i-1].out - selector[n-1-i] * (compression_hash[i-1].out - nodes[i]);
compression_hash[i].inp[1] <== nodes[i] - selector[n-1-i] * (nodes[i] - compression_hash[i-1].out);
}
root <== compression_hash[n-1].out;
}
// Verify a Merkle proof of depth n
// /!\ To call this function, it's important to check that each selector is a bit before!!!
template proof_of_membership(n) {
signal input nodes[n]; // The Merkle path
signal input selector[n]; // it's the leaf's indice in big endian bits indicating if complementary nodes are left or right
signal input root;
signal input leaf;
signal output out;
component root_calculator = compute_merkle_root(n);
for(var i=0; i<n; i++){
root_calculator.nodes[i] <== nodes[i];
root_calculator.selector[i] <== selector[i];
}
root_calculator.leaf <== leaf;
component eq = IsEqual();
eq.in[0] <== root_calculator.root;
eq.in[1] <== root;
out <== eq.out;
}

View File

@ -0,0 +1,19 @@
//
pragma circom 2.1.9;
include "poseidon2_sponge.circom";
//------------------------------------------------------------------------------
// Hash `n` field elements into 1, with approximately 254 bits of preimage security (?)
// (assuming bn128 scalar field. We use capacity=2, rate=1, t=3).
template Poseidon2_hash(n) {
signal input inp[n];
signal output out;
component sponge = PoseidonSponge(3,2,n,1);
sponge.inp <== inp;
sponge.out[0] ==> out;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,217 @@
//
pragma circom 2.1.9;
//
// The Poseidon2 permutation for bn128 and t=3
//
//------------------------------------------------------------------------------
// The S-box
template SBox() {
signal input inp;
signal output out;
signal x2 <== inp*inp;
signal x4 <== x2*x2;
out <== inp*x4;
}
//------------------------------------------------------------------------------
// partial or internal round
template InternalRound(i) {
signal input inp[3];
signal output out[3];
var round_consts[56] =
[0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9];
component sb = SBox();
sb.inp <== inp[0] + round_consts[i];
out[0] <== 2*sb.out + inp[1] + inp[2];
out[1] <== sb.out + 2*inp[1] + inp[2];
out[2] <== sb.out + inp[1] + 3*inp[2];
}
//------------------------------------------------------------------------------
// external rounds
template ExternalRound(i) {
signal input inp[3];
signal output out[3];
var round_consts[8][3] =
[ [ 0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816
, 0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610
, 0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1
]
, [ 0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5
, 0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28
, 0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735
]
, [ 0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a
, 0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d
, 0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce
]
, [ 0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4
, 0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e
, 0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e
]
, [ 0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0
, 0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5
, 0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893
]
, [ 0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d
, 0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e
, 0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc
]
, [ 0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1
, 0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe
, 0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166
]
, [ 0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce
, 0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57
, 0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6
]
];
component sb[3];
for(var j=0; j<3; j++) {
sb[j] = SBox();
sb[j].inp <== inp[j] + round_consts[i][j];
}
out[0] <== 2*sb[0].out + sb[1].out + sb[2].out;
out[1] <== sb[0].out + 2*sb[1].out + sb[2].out;
out[2] <== sb[0].out + sb[1].out + 2*sb[2].out;
}
//------------------------------------------------------------------------------
// the initial linear layer
template LinearLayer() {
signal input inp[3];
signal output out[3];
out[0] <== 2*inp[0] + inp[1] + inp[2];
out[1] <== inp[0] + 2*inp[1] + inp[2];
out[2] <== inp[0] + inp[1] + 2*inp[2];
}
//------------------------------------------------------------------------------
// the Poseidon2 permutation for t=3
template Permutation() {
signal input inp[3];
signal output out[3];
signal aux[65][3];
component ll = LinearLayer();
for(var j=0; j<3; j++) { ll.inp[j] <== inp[j]; }
for(var j=0; j<3; j++) { ll.out[j] ==> aux[0][j]; }
component ext[8];
for(var k=0; k<8; k++) { ext[k] = ExternalRound(k); }
component int[56];
for(var k=0; k<56; k++) { int[k] = InternalRound(k); }
// first 4 external rounds
for(var k=0; k<4; k++) {
for(var j=0; j<3; j++) { ext[k].inp[j] <== aux[k ][j]; }
for(var j=0; j<3; j++) { ext[k].out[j] ==> aux[k+1][j]; }
}
// the 56 internal rounds
for(var k=0; k<56; k++) {
for(var j=0; j<3; j++) { int[k].inp[j] <== aux[k+4][j]; }
for(var j=0; j<3; j++) { int[k].out[j] ==> aux[k+5][j]; }
}
// last 4 external rounds
for(var k=0; k<4; k++) {
for(var j=0; j<3; j++) { ext[k+4].inp[j] <== aux[k+60][j]; }
for(var j=0; j<3; j++) { ext[k+4].out[j] ==> aux[k+61][j]; }
}
for(var j=0; j<3; j++) { out[j] <== aux[64][j]; }
}
//------------------------------------------------------------------------------
// the "compression function" takes 2 field elements as input and produces
// 1 field element as output. It is a trivial application of the permutation.
template Compression() {
signal input inp[2];
signal output out;
component perm = Permutation();
perm.inp[0] <== inp[0];
perm.inp[1] <== inp[1];
perm.inp[2] <== 0;
perm.out[0] ==> out;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,123 @@
//
pragma circom 2.1.9;
include "poseidon2_perm.circom";
//------------------------------------------------------------------------------
function min(a,b) {
return (a <= b) ? a : b;
}
//------------------------------------------------------------------------------
//
// Poseidon sponge construction
//
// t = size of state (currently fixed to 3)
// c = capacity (1 or 2)
// r = rate = t - c
//
// everything is measured in number of field elements
//
// we use the padding `10*` from the original Poseidon paper,
// and initial state constant zero. Note that this is different
// from the "SAFE padding" recommended in the Poseidon2 paper
// (which uses `0*` padding and a nontrivial initial state)
//
template PoseidonSponge(t, capacity, input_len, output_len) {
var rate = t - capacity;
assert( t == 3);
assert( capacity > 0 );
assert( rate > 0 );
assert( capacity < t );
assert( rate < t );
signal input inp[ input_len];
signal output out[output_len];
// round up to rate the input + 1 field element ("10*" padding)
var nblocks = ((input_len + 1) + (rate-1)) \ rate;
var nout = (output_len + (rate-1)) \ rate;
var padded_len = nblocks * rate;
signal padded[padded_len];
for(var i=0; i<input_len; i++) { padded[i] <== inp[i]; }
padded[input_len ] <== 1;
for(var i=input_len+1; i<padded_len; i++) { padded[i] <== 0; }
signal state [nblocks+nout][t ];
signal sorbed[nblocks ][rate];
// initialize state
for(var i=0; i<t; i++) { state[0][i] <== 0; }
component absorb [nblocks];
component squeeze[nout-1];
for(var m=0; m<nblocks; m++) {
for(var i=0; i<rate; i++) {
var a = state [m][i];
var b = padded[m*rate+i];
sorbed[m][i] <== a + b;
}
absorb[m] = Permutation();
for(var j=0 ; j<rate; j++) { absorb[m].inp[j] <== sorbed[m][j]; }
for(var j=rate; j<t ; j++) { absorb[m].inp[j] <== state [m][j]; }
absorb[m].out ==> state[m+1];
}
var q = min(rate, output_len);
for(var i=0; i<q; i++) {
state[nblocks][i] ==> out[i];
}
var out_ptr = rate;
for(var n=1; n<nout; n++) {
squeeze[n-1] = Permutation();
squeeze[n-1].inp <== state[nblocks+n-1];
squeeze[n-1].out ==> state[nblocks+n ];
var q = min(rate, output_len-out_ptr);
for(var i=0; i<q; i++) {
state[nblocks+n][i] ==> out[out_ptr+i];
}
out_ptr += rate;
}
}
//------------------------------------------------------------------------------
//
// sponge hash with rate=1
//
template Poseidon2_sponge_hash_rate_1(n) {
signal input inp[n];
signal output out;
component sponge = PoseidonSponge(3, 2, n, 1);
sponge.inp <== inp;
sponge.out[0] ==> out;
}
//
// sponge hash with rate=2
//
template Poseidon2_sponge_hash_rate_2(n) {
signal input inp[n];
signal output out;
component sponge = PoseidonSponge(3, 1, n, 1);
sponge.inp <== inp;
sponge.out[0] ==> out;
}
//------------------------------------------------------------------------------

30
ledger/notes.circom Normal file
View File

@ -0,0 +1,30 @@
//test
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../misc/constants.circom";
template derive_secret_key(){
signal input starting_slot;
signal input secrets_root;
signal output out;
component hash = Poseidon2_hash(3);
component dst = NOMOS_POL_SK_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== starting_slot;
hash.inp[2] <== secrets_root;
out <== hash.out;
}
template derive_public_key(){
signal input secret_key;
signal output out;
component hash = Poseidon2_hash(2);
component dst = NOMOS_KDF();
hash.inp[0] <== dst.out;
hash.inp[1] <== secret_key;
out <== hash.out;
}

308
mantle/generate_inputs_for_pol.py Executable file
View File

@ -0,0 +1,308 @@
#!/usr/bin/sage
# -*- mode: python ; -*-
from sage.all import *
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
F = FiniteField(p)
def poseidon2_hash(data):
return PoseidonSponge(data,2,1)[0]
def Poseidon2_sponge_hash_rate_1(data, n):
return PoseidonSponge(data,3,2,n,1)
def Poseidon2_sponge_hash_rate_2(data, n):
return PoseidonSponge(data,3,1,n,1)
def SBox(inp):
return inp**5
def InternalRound(inp, i):
round_consts = [
0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9,
]
sb = SBox(inp[0] + round_consts[i])
out = [F(0) for i in range(3)]
out[0] = 2*sb + inp[1] + inp[2];
out[1] = sb + 2*inp[1] + inp[2];
out[2] = sb + inp[1] + 3*inp[2];
return out
def ExternalRound(inp, i):
out = [F(0) for j in range(3)]
round_consts = [ [F(0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816)
, F(0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610)
, F(0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1)
]
, [ F(0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5)
, F(0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28)
, F(0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735)
]
, [ F(0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a)
, F(0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d)
, F(0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce)
]
, [ F(0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4)
, F(0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e)
, F(0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e)
]
, [ F(0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0)
, F(0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5)
, F(0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893)
]
, [ F(0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d)
, F(0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e)
, F(0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc)
]
, [ F(0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1)
, F(0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe)
, F(0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166)
]
, [ F(0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce)
, F(0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57)
, F(0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6)
]]
sb = [F(0) for j in range(3)]
for j in range(3):
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
out = [F(0) for j in range(3)]
out[0] = 2*sb[0] + sb[1] + sb[2]
out[1] = sb[0] + 2*sb[1] + sb[2]
out[2] = sb[0]+ sb[1] + 2*sb[2]
return out
def LinearLayer(inp):
out = [F(0) for i in range(3)]
out[0] = 2*inp[0] + inp[1] + inp[2]
out[1] = inp[0] + 2*inp[1] + inp[2]
out[2] = inp[0] + inp[1] + 2*inp[2]
return out
def Permutation(inp):
out = [F(0) for i in range(3)]
state = LinearLayer(inp)
for k in range(4):
state = ExternalRound(state, k)
for k in range(56):
state = InternalRound(state, k)
for k in range(4):
state = ExternalRound(state, k+4)
return state
def Compression(inp):
return Permutation([inp[0],inp[1],F(0)])
def PoseidonSponge(data, capacity, output_len):
rate = 3 - capacity;
output = [F(0) for i in range(output_len)]
assert( capacity > 0 )
assert( rate > 0 )
assert( capacity < 3 )
assert( rate < 3 )
# round up to rate the input + 1 field element ("10*" padding)
nblocks = ((len(data) + 1) + (rate-1)) // rate;
nout = (output_len + (rate-1)) // rate;
padded_len = nblocks * rate;
padded = []
for i in range(len(data)):
padded.append(F(data[i]))
padded.append(F(1))
for i in range(len(data)+1,padded_len):
padded.append(F(0))
civ = F(0)
state = [F(0),F(0),F(civ)]
sorbed = [F(0) for j in range(rate)]
for m in range(nblocks):
for i in range(rate):
a = state[i]
b = padded[m*rate+i]
sorbed[i] = a + b
state = Permutation(sorbed[0:rate] + state[rate:3])
q = min(rate, output_len)
for i in range(q):
output[i] = state[i]
out_ptr = rate
for n in range(1,nout):
state[nblocks+n] = Permutation(state[nblocks+n-1])
q = min(rate, output_len-out_ptr)
for i in range(q):
output[out_ptr+i] = state[nblocks+n][i]
out_ptr += rate
return output
if len(sys.argv) != Integer(4):
print("Usage: <script> <epoch_nonce> <slot_number> <total_stake>")
exit()
epoch_nonce = int(sys.argv[Integer(1)])
slot_number = int(sys.argv[Integer(2)])
total_stake = int(sys.argv[Integer(3)])
if epoch_nonce >= p:
print("epoch nonce must be less than p")
exit()
if total_stake >= p:
print("total stake must be less than p")
exit()
t0_constant = F(0x1a3fb997fd58374772808c13d1c2ddacb5ab3ea77413f86fd6e0d3d978e5438)
t1_constant = F(0x71e790b41991052e30c93934b5612412e7958837bac8b1c524c24d84cc7d0)
t0 = F(int(t0_constant) // total_stake)
t1 = F(p- (int(t1_constant) // total_stake**2))
value = F(total_stake / 100)
threshold = (t0 + t1 * value) * value
starting_slot = randrange(max(0,slot_number-2**25+1),slot_number,1)
slot_secret = F(randrange(0,p,1))
slot_secret_indexes = format(int(slot_number - starting_slot),'025b')
tx_hash = F(randrange(0,p,1))
output_number = F(randrange(0,50,1))
slot_secret_path = [F(randrange(0,p,1)) for i in range(25)]
secret_root = slot_secret
for i in range(25):
if int(slot_secret_indexes[24-i]) == 0:
secret_root = poseidon2_hash([secret_root,slot_secret_path[i]])
else:
secret_root = poseidon2_hash([slot_secret_path[i],secret_root])
sk = poseidon2_hash([F(256174383281726064679014503048630094),starting_slot,secret_root])
pk = poseidon2_hash([F(1296193216988918402894),sk])
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
while(ticket > threshold):
output_number += 1
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
aged_nodes = [F(randrange(0,p,1)) for i in range(32)]
aged_selectors = randrange(0,2**32,1)
aged_selectors = format(aged_selectors,'032b')
aged_root = note_id
for i in range(32):
if int(aged_selectors[31-i]) == 0:
aged_root = poseidon2_hash([aged_root,aged_nodes[i]])
else:
aged_root = poseidon2_hash([aged_nodes[i],aged_root])
unspent_nodes = [F(randrange(0,p,1)) for i in range(32)]
unspent_selectors = randrange(0,2**32,1)
unspent_selectors = format(unspent_selectors,'032b')
latest_root = note_id
for i in range(32):
if int(unspent_selectors[31-i]) == 0:
latest_root = poseidon2_hash([latest_root,unspent_nodes[i]])
else:
latest_root = poseidon2_hash([unspent_nodes[i],latest_root])
# 5) Assemble JSON
inp = {
"sl": str(slot_number),
"epoch_nonce": str(epoch_nonce),
"t0": str(t0),
"t1": str(t1),
"slot_secret": str(slot_secret),
"P_lead_part_one": str(F(123456)),
"P_lead_part_two": str(F(654321)),
"slot_secret_path": [str(x) for x in slot_secret_path],
"noteid_aged_path": [str(x) for x in aged_nodes],
"noteid_aged_selectors": [str(x) for x in aged_selectors],
"ledger_aged": str(aged_root),
"note_tx_hash": str(tx_hash),
"note_output_number": str(output_number),
"noteid_latest_path": [str(x) for x in unspent_nodes],
"noteid_latest_selectors": [str(x) for x in unspent_selectors],
"ledger_latest": str(latest_root),
"starting_slot": str(starting_slot),
"v": str(value)
}
import json
with open("input.json","w") as f:
json.dump(inp, f, indent=2)
print("Wrote input of pol")

View File

@ -0,0 +1,238 @@
#!/usr/bin/sage
# -*- mode: python ; -*-
from sage.all import *
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
F = FiniteField(p)
def poseidon2_hash(data):
return PoseidonSponge(data,2,1)[0]
def Poseidon2_sponge_hash_rate_1(data, n):
return PoseidonSponge(data,3,2,n,1)
def Poseidon2_sponge_hash_rate_2(data, n):
return PoseidonSponge(data,3,1,n,1)
def SBox(inp):
return inp**5
def InternalRound(inp, i):
round_consts = [
0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9,
]
sb = SBox(inp[0] + round_consts[i])
out = [F(0) for i in range(3)]
out[0] = 2*sb + inp[1] + inp[2];
out[1] = sb + 2*inp[1] + inp[2];
out[2] = sb + inp[1] + 3*inp[2];
return out
def ExternalRound(inp, i):
out = [F(0) for j in range(3)]
round_consts = [ [F(0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816)
, F(0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610)
, F(0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1)
]
, [ F(0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5)
, F(0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28)
, F(0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735)
]
, [ F(0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a)
, F(0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d)
, F(0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce)
]
, [ F(0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4)
, F(0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e)
, F(0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e)
]
, [ F(0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0)
, F(0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5)
, F(0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893)
]
, [ F(0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d)
, F(0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e)
, F(0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc)
]
, [ F(0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1)
, F(0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe)
, F(0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166)
]
, [ F(0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce)
, F(0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57)
, F(0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6)
]]
sb = [F(0) for j in range(3)]
for j in range(3):
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
out = [F(0) for j in range(3)]
out[0] = 2*sb[0] + sb[1] + sb[2]
out[1] = sb[0] + 2*sb[1] + sb[2]
out[2] = sb[0]+ sb[1] + 2*sb[2]
return out
def LinearLayer(inp):
out = [F(0) for i in range(3)]
out[0] = 2*inp[0] + inp[1] + inp[2]
out[1] = inp[0] + 2*inp[1] + inp[2]
out[2] = inp[0] + inp[1] + 2*inp[2]
return out
def Permutation(inp):
out = [F(0) for i in range(3)]
state = LinearLayer(inp)
for k in range(4):
state = ExternalRound(state, k)
for k in range(56):
state = InternalRound(state, k)
for k in range(4):
state = ExternalRound(state, k+4)
return state
def Compression(inp):
return Permutation([inp[0],inp[1],F(0)])
def PoseidonSponge(data, capacity, output_len):
rate = 3 - capacity;
output = [F(0) for i in range(output_len)]
assert( capacity > 0 )
assert( rate > 0 )
assert( capacity < 3 )
assert( rate < 3 )
# round up to rate the input + 1 field element ("10*" padding)
nblocks = ((len(data) + 1) + (rate-1)) // rate;
nout = (output_len + (rate-1)) // rate;
padded_len = nblocks * rate;
padded = []
for i in range(len(data)):
padded.append(F(data[i]))
padded.append(F(1))
for i in range(len(data)+1,padded_len):
padded.append(F(0))
civ = F(0)
state = [F(0),F(0),F(civ)]
sorbed = [F(0) for j in range(rate)]
for m in range(nblocks):
for i in range(rate):
a = state[i]
b = padded[m*rate+i]
sorbed[i] = a + b
state = Permutation(sorbed[0:rate] + state[rate:3])
q = min(rate, output_len)
for i in range(q):
output[i] = state[i]
out_ptr = rate
for n in range(1,nout):
state[nblocks+n] = Permutation(state[nblocks+n-1])
q = min(rate, output_len-out_ptr)
for i in range(q):
output[out_ptr+i] = state[nblocks+n][i]
out_ptr += rate
return output
secret_voucher = F(randrange(0,p,1))
reward_voucher = poseidon2_hash([F(1668646695034522932676805048878418),secret_voucher])
merkle_nodes = [F(randrange(0,p,1)) for i in range(32)]
selectors = randrange(0,2**32,1)
selectors = format(selectors,'032b')
voucher_root = reward_voucher
for i in range(32):
if int(selectors[31-i]) == 0:
voucher_root = poseidon2_hash([voucher_root,merkle_nodes[i]])
else:
voucher_root = poseidon2_hash([merkle_nodes[i],voucher_root])
data_msg = F(randrange(0,p,1))
inp = {
"secret_voucher": str(secret_voucher),
"voucher_merkle_path":[str(x) for x in merkle_nodes],
"voucher_merkle_path_selectors": [str(x) for x in selectors],
"mantle_tx_hash": str(data_msg),
"voucher_root": str(voucher_root)
}
import json
with open("input.json","w") as f:
json.dump(inp, f, indent=2)
print("Wrote input of proof of claim")

View File

@ -0,0 +1,232 @@
#!/usr/bin/sage
# -*- mode: python ; -*-
from sage.all import *
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
F = FiniteField(p)
def poseidon2_hash(data):
return PoseidonSponge(data,2,1)[0]
def Poseidon2_sponge_hash_rate_1(data, n):
return PoseidonSponge(data,3,2,n,1)
def Poseidon2_sponge_hash_rate_2(data, n):
return PoseidonSponge(data,3,1,n,1)
def SBox(inp):
return inp**5
def InternalRound(inp, i):
round_consts = [
0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9,
]
sb = SBox(inp[0] + round_consts[i])
out = [F(0) for i in range(3)]
out[0] = 2*sb + inp[1] + inp[2];
out[1] = sb + 2*inp[1] + inp[2];
out[2] = sb + inp[1] + 3*inp[2];
return out
def ExternalRound(inp, i):
out = [F(0) for j in range(3)]
round_consts = [ [F(0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816)
, F(0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610)
, F(0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1)
]
, [ F(0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5)
, F(0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28)
, F(0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735)
]
, [ F(0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a)
, F(0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d)
, F(0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce)
]
, [ F(0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4)
, F(0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e)
, F(0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e)
]
, [ F(0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0)
, F(0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5)
, F(0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893)
]
, [ F(0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d)
, F(0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e)
, F(0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc)
]
, [ F(0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1)
, F(0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe)
, F(0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166)
]
, [ F(0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce)
, F(0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57)
, F(0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6)
]]
sb = [F(0) for j in range(3)]
for j in range(3):
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
out = [F(0) for j in range(3)]
out[0] = 2*sb[0] + sb[1] + sb[2]
out[1] = sb[0] + 2*sb[1] + sb[2]
out[2] = sb[0]+ sb[1] + 2*sb[2]
return out
def LinearLayer(inp):
out = [F(0) for i in range(3)]
out[0] = 2*inp[0] + inp[1] + inp[2]
out[1] = inp[0] + 2*inp[1] + inp[2]
out[2] = inp[0] + inp[1] + 2*inp[2]
return out
def Permutation(inp):
out = [F(0) for i in range(3)]
state = LinearLayer(inp)
for k in range(4):
state = ExternalRound(state, k)
for k in range(56):
state = InternalRound(state, k)
for k in range(4):
state = ExternalRound(state, k+4)
return state
def Compression(inp):
return Permutation([inp[0],inp[1],F(0)])
def PoseidonSponge(data, capacity, output_len):
rate = 3 - capacity;
output = [F(0) for i in range(output_len)]
assert( capacity > 0 )
assert( rate > 0 )
assert( capacity < 3 )
assert( rate < 3 )
# round up to rate the input + 1 field element ("10*" padding)
nblocks = ((len(data) + 1) + (rate-1)) // rate;
nout = (output_len + (rate-1)) // rate;
padded_len = nblocks * rate;
padded = []
for i in range(len(data)):
padded.append(F(data[i]))
padded.append(F(1))
for i in range(len(data)+1,padded_len):
padded.append(F(0))
civ = F(0)
state = [F(0),F(0),F(civ)]
sorbed = [F(0) for j in range(rate)]
for m in range(nblocks):
for i in range(rate):
a = state[i]
b = padded[m*rate+i]
sorbed[i] = a + b
state = Permutation(sorbed[0:rate] + state[rate:3])
q = min(rate, output_len)
for i in range(q):
output[i] = state[i]
out_ptr = rate
for n in range(1,nout):
state[nblocks+n] = Permutation(state[nblocks+n-1])
q = min(rate, output_len-out_ptr)
for i in range(q):
output[out_ptr+i] = state[nblocks+n][i]
out_ptr += rate
return output
if len(sys.argv) != Integer(2):
print("Usage: <script> <number of input>")
exit()
nInput = int(sys.argv[Integer(1)])
sk = [F(randrange(0,p,1)) for i in range(nInput)]
data_msg = F(randrange(0,p,1))
if nInput == 1:
inp = {
"secret_keys": str(sk[0]),
"msg": str(data_msg)
}
else:
inp = {
"secret_keys": [str(x) for x in sk],
"msg": str(data_msg)
}
import json
with open("input.json","w") as f:
json.dump(inp, f, indent=2)
print("Wrote input of ZkSignature")

74
mantle/poc.circom Normal file
View File

@ -0,0 +1,74 @@
//test
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../hash_bn/merkle.circom";
include "../misc/constants.circom";
template derive_voucher_nullifier(){
signal input secret_voucher;
signal output out;
component hash = Poseidon2_hash(2);
component dst = VOUCHER_NF();
hash.inp[0] <== dst.out;
hash.inp[1] <== secret_voucher;
out <== hash.out;
}
template derive_reward_voucher(){
signal input secret_voucher;
signal output out;
component hash = Poseidon2_hash(2);
component dst = REWARD_VOUCHER();
hash.inp[0] <== dst.out;
hash.inp[1] <== secret_voucher;
out <== hash.out;
}
template proof_of_claim(){
signal input secret_voucher;
signal input voucher_merkle_path[32];
signal input voucher_merkle_path_selectors[32];
signal input mantle_tx_hash;
signal input voucher_root;
signal output voucher_nullifier;
//derive the reward voucher
component reward_voucher = derive_reward_voucher();
reward_voucher.secret_voucher <== secret_voucher;
//Check reward voucher membership
//First check selectors are indeed bits
for(var i = 0; i < 32; i++){
voucher_merkle_path_selectors[i] * (1 - voucher_merkle_path_selectors[i]) === 0;
}
//Then check the proof of membership
component reward_membership = proof_of_membership(32);
for(var i = 0; i < 32; i++){
reward_membership.nodes[i] <== voucher_merkle_path[i];
reward_membership.selector[i] <== voucher_merkle_path_selectors[i];
}
reward_membership.root <== voucher_root;
reward_membership.leaf <== reward_voucher.out;
reward_membership.out === 1;
//derive the reward nullifier
component reward_nullifier = derive_voucher_nullifier();
reward_nullifier.secret_voucher <== secret_voucher;
voucher_nullifier <== reward_nullifier.out;
// dummy constraint to avoid unused public input to be erased after compilation optimisation
signal dummy;
dummy <== mantle_tx_hash * mantle_tx_hash;
}
component main {public [voucher_root,mantle_tx_hash]}= proof_of_claim();

5
mantle/pol.circom Normal file
View File

@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "pol_lib.circom";
component main {public [sl,epoch_nonce,t0,t1,ledger_aged,ledger_latest,P_lead_part_one,P_lead_part_two]}= proof_of_leadership(25);

248
mantle/pol_lib.circom Normal file
View File

@ -0,0 +1,248 @@
//test
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../ledger/notes.circom";
include "../hash_bn/merkle.circom";
include "../misc/comparator.circom";
include "../circomlib/circuits/bitify.circom";
include "../misc/constants.circom";
template ticket_calculator(){
signal input epoch_nonce;
signal input slot;
signal input note_id;
signal input secret_key;
signal output out;
component hash = Poseidon2_hash(5);
component dst = LEAD_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== epoch_nonce;
hash.inp[2] <== slot;
hash.inp[3] <== note_id;
hash.inp[4] <== secret_key;
out <== hash.out;
}
template derive_entropy(){
signal input slot;
signal input note_id;
signal input secret_key;
signal output out;
component hash = Poseidon2_hash(4);
component dst = NOMOS_NONCE_CONTRIB_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== slot;
hash.inp[2] <== note_id;
hash.inp[3] <== secret_key;
out <== hash.out;
}
template would_win_leadership(secret_depth){
signal input slot;
signal input epoch_nonce;
signal input t0;
signal input t1;
signal input slot_secret;
signal input slot_secret_path[secret_depth];
//Part of the note id proof of membership to prove aged
signal input aged_nodes[32];
signal input aged_selectors[32]; // must be bits
signal input aged_root;
//Used to derive the note identifier
signal input transaction_hash;
signal input output_number;
//Part of the secret key
signal input starting_slot;
// The winning note value
signal input value;
signal output out;
signal output note_identifier;
signal output secret_key;
// Derivation of the secrets root from the slot secret at position slot - starting_slot
// Verify that the substraction wont underflow (starting_slot < slot)
component checker = SafeFullLessThan();
checker.a <== starting_slot;
checker.b <== slot;
// Compute the positions related to slot - starting_slot and make sure slot - starting_slot is a 25 bits number
component bits = Num2Bits(secret_depth);
bits.in <== slot - starting_slot;
// Derive the secrets root
component secrets_root = compute_merkle_root(secret_depth);
for(var i=0; i<secret_depth; i++){
secrets_root.nodes[i] <== slot_secret_path[i];
secrets_root.selector[i] <== bits.out[secret_depth-1-i];
}
secrets_root.leaf <== slot_secret;
// Derive the secret key
component sk = derive_secret_key();
sk.starting_slot <== starting_slot;
sk.secrets_root <== secrets_root.root;
// Derive the public key from the secret key
component pk = derive_public_key();
pk.secret_key <== sk.out;
// Derive the note id
component note_id = Poseidon2_hash(5);
component dst_note_id = NOMOS_NOTE_ID_V1();
note_id.inp[0] <== dst_note_id.out;
note_id.inp[1] <== transaction_hash;
note_id.inp[2] <== output_number;
note_id.inp[3] <== value;
note_id.inp[4] <== pk.out;
// Check the note ID is aged enough
//First check selectors are indeed bits
for(var i = 0; i < 32; i++){
aged_selectors[i] * (1 - aged_selectors[i]) === 0;
}
//Then check the proof of membership
component aged_membership = proof_of_membership(32);
for(var i = 0; i < 32; i++){
aged_membership.nodes[i] <== aged_nodes[i];
aged_membership.selector[i] <== aged_selectors[i];
}
aged_membership.root <== aged_root;
aged_membership.leaf <== note_id.out;
// Compute the lottery ticket
component ticket = ticket_calculator();
ticket.epoch_nonce <== epoch_nonce;
ticket.slot <== slot;
ticket.note_id <== note_id.out;
ticket.secret_key <== sk.out;
// Compute the lottery threshold
signal intermediate;
signal threshold;
intermediate <== t1 * value;
threshold <== value * (t0 + intermediate);
// Check that the ticket is winning
component winning = SafeFullLessThan();
winning.a <== ticket.out;
winning.b <== threshold;
// Check that every constraint holds
signal intermediate_out;
intermediate_out <== aged_membership.out * winning.out;
out <== intermediate_out * checker.out;
note_identifier <== note_id.out;
secret_key <== sk.out;
}
template proof_of_leadership(secret_depth){
signal input sl;
signal input epoch_nonce; // the epoch nonce eta
signal input t0;
signal input t1;
signal input slot_secret; // This is r_sl
signal input slot_secret_path[secret_depth];
//Part of the note id proof of membership to prove aged
signal input noteid_aged_path[32];
signal input noteid_aged_selectors[32]; // must be bits
signal input ledger_aged;
//Used to derive the note identifier
signal input note_tx_hash;
signal input note_output_number;
//Part of the note id proof of membership to prove it's unspent
signal input noteid_latest_path[32];
signal input noteid_latest_selectors[32]; // must be bits
signal input ledger_latest;
//Part of the secret key
signal input starting_slot;
// The winning note. The unit is supposed to be NMO and the ZoneID is MANTLE
signal input v; // value of the note
// Verify the note is winning the lottery
component lottery_checker = would_win_leadership(secret_depth);
lottery_checker.slot <== sl;
lottery_checker.epoch_nonce <== epoch_nonce;
lottery_checker.t0 <== t0;
lottery_checker.t1 <== t1;
lottery_checker.slot_secret <== slot_secret;
for(var i = 0; i < secret_depth; i++){
lottery_checker.slot_secret_path[i] <== slot_secret_path[i];
}
for(var i = 0; i < 32; i++){
lottery_checker.aged_nodes[i] <== noteid_aged_path[i];
lottery_checker.aged_selectors[i] <== noteid_aged_selectors[i];
}
lottery_checker.aged_root <== ledger_aged;
lottery_checker.transaction_hash <== note_tx_hash;
lottery_checker.output_number <== note_output_number;
lottery_checker.starting_slot <== starting_slot;
lottery_checker.value <== v;
// One time signing key used to sign the block proposal and the block
signal input P_lead_part_one;
signal input P_lead_part_two;
//Avoid the circom optimisation that removes unused public input
signal dummy_one;
signal dummy_two;
dummy_one <== P_lead_part_one * P_lead_part_one;
dummy_two <== P_lead_part_two * P_lead_part_two;
signal output entropy_contribution; // This is rho_lead
// Check that the note is unspent
//First check selectors are indeed bits
for(var i = 0; i < 32; i++){
noteid_latest_selectors[i] * (1 - noteid_latest_selectors[i]) === 0;
}
//Then check the note id is in the latest ledger state
component unspent_membership = proof_of_membership(32);
for(var i = 0; i < 32; i++){
unspent_membership.nodes[i] <== noteid_latest_path[i];
unspent_membership.selector[i] <== noteid_latest_selectors[i];
}
unspent_membership.root <== ledger_latest;
unspent_membership.leaf <== lottery_checker.note_identifier;
lottery_checker.out * unspent_membership.out === 1;
// Compute the entropy contribution
component entropy = derive_entropy();
entropy.slot <== sl;
entropy.note_id <== lottery_checker.note_identifier;
entropy.secret_key <== lottery_checker.secret_key;
entropy_contribution <== entropy.out;
}

24
mantle/signature.circom Normal file
View File

@ -0,0 +1,24 @@
//test
pragma circom 2.1.9;
include "../ledger/notes.circom";
include "../misc/constants.circom";
template zkSignature(maxInput){
signal input secret_keys[maxInput];
signal input msg;
signal output public_keys[maxInput];
component pk[maxInput];
for(var i =0; i<maxInput; i++){
pk[i] = derive_public_key();
pk[i].secret_key <== secret_keys[i];
public_keys[i] <== pk[i].out;
}
// dummy constraint to avoid unused public input to be erased after compilation optimisation
signal dummy;
dummy <== msg * msg;
}
component main {public [msg]}= zkSignature(32);

80
misc/comparator.circom Normal file
View File

@ -0,0 +1,80 @@
//test
pragma circom 2.1.9;
include "../circomlib/circuits/bitify.circom";
include "../circomlib/circuits/comparators.circom";
// If a or b isn't guaranteed to be less than p use SafeFullComparator
// See https://www.notion.so/nomos-tech/Comparisons-1fd261aa09df81feae1ff3e6612b92a0
template SafeFullLessThan() {
signal input a;
signal input b;
signal output out;
component bitifier_a = Num2Bits_strict();
component bitifier_b = Num2Bits_strict();
bitifier_a.in <== a;
bitifier_b.in <== b;
component numifier_a = Bits2Num(252);
component numifier_b = Bits2Num(252);
for(var i =0; i<252; i++){
numifier_a.in[i] <== bitifier_a.out[i+2];
numifier_b.in[i] <== bitifier_b.out[i+2];
}
component A = LessThan(252);
A.in[0] <== numifier_b.out;
A.in[1] <== numifier_a.out;
component B = IsEqual();
B.in[0] <== numifier_a.out;
B.in[1] <== numifier_b.out;
component C = IsEqual();
C.in[0] <== bitifier_a.out[1];
C.in[1] <== bitifier_b.out[1];
component D = IsEqual();
D.in[0] <== bitifier_a.out[1];
D.in[1] <== 1;
component E = IsEqual();
E.in[0] <== bitifier_a.out[0];
E.in[1] <== bitifier_b.out[0];
component F = IsEqual();
F.in[0] <== bitifier_a.out[0];
F.in[1] <== 1;
signal intermediate_results[4];
intermediate_results[0] <== (1 - C.out) * (1-D.out);
intermediate_results[1] <== (1 - C.out) * (1-E.out);
intermediate_results[2] <== intermediate_results[1] * (1- F.out);
intermediate_results[3] <== B.out * (intermediate_results[0] + intermediate_results[2]);
out <== (1 - A.out) * ((1 - B.out) + intermediate_results[3]);
}
// Safely compare two n-bit numbers
// Performs range checks on the inputs to avoid overflow. Range is n <= 252
template SafeLessThan(n) {
assert(n <= 252);
signal input in[2];
signal output out;
component aInRange = Num2Bits(n);
aInRange.in <== in[0];
component bInRange = Num2Bits(n);
bInRange.in <== in[1];
component lt = LessThan(n);
lt.in[0] <== in[0];
lt.in[1] <== in[1];
out <== lt.out;
}

67
misc/constants.circom Normal file
View File

@ -0,0 +1,67 @@
//test
pragma circom 2.1.9;
include "../circomlib/circuits/bitify.circom";
include "../circomlib/circuits/comparators.circom";
// int.from_bytes(b"LEAD_V1", byteorder="little") = 13887241025832268
template LEAD_V1(){
signal output out;
out <== 13887241025832268;
}
// int.from_bytes(b"NOMOS_POL_SK_V1", byteorder="little") = 256174383281726064679014503048630094
template NOMOS_POL_SK_V1(){
signal output out;
out <== 256174383281726064679014503048630094;
}
// int.from_bytes(b"NOMOS_NONCE_CONTRIB_V1", byteorder="little") = 18459309511848927313552932915476467038165525790019406
template NOMOS_NONCE_CONTRIB_V1(){
signal output out;
out <== 18459309511848927313552932915476467038165525790019406;
}
// int.from_bytes(b"NOMOS_KDF", byteorder="little") = 1296193216988918402894
template NOMOS_KDF(){
signal output out;
out <== 1296193216988918402894;
}
// int.from_bytes(b"NOMOS_NOTE_ID_V1", byteorder="little") = 65580641562429851895355409762135920462
template NOMOS_NOTE_ID_V1(){
signal output out;
out <== 65580641562429851895355409762135920462;
}
// int.from_bytes(b"SELECTION_RANDOMNESS_V1", byteorder="little") = 4725583332308041445519605499429790922252397838206780755
template SELECTION_RANDOMNESS_V1(){
signal output out;
out <== 4725583332308041445519605499429790922252397838206780755;
}
// int.from_bytes(b"KEY_NULLIFIER_V1", byteorder="little") = 65580642670359595206974785265459610955
template KEY_NULLIFIER_V1(){
signal output out;
out <== 65580642670359595206974785265459610955;
}
// int.from_bytes(b"REWARD_VOUCHER", byteorder="little") = 1668646695034522932676805048878418
template REWARD_VOUCHER(){
signal output out;
out <== 1668646695034522932676805048878418;
}
// int.from_bytes(b"VOUCHER_NF", byteorder="little") = 332011368467182873038678
template VOUCHER_NF(){
signal output out;
out <== 332011368467182873038678;
}