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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 3 additions & 20 deletions .github/workflows/nimbus_verified_proxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ on:
- 'Makefile'
- 'nimbus.nimble'

workflow_dispatch:

concurrency: # Cancel stale PR builds (but not push builds)
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
Expand Down Expand Up @@ -67,28 +69,9 @@ jobs:
rocksdb-cache: true
eest-cache: false

- name: Run verified proxy tests (Windows)
if: runner.os == 'Windows'
- name: Run verified proxy tests
run: |
gcc --version
DEFAULT_MAKE_FLAGS="-j${ncpu}"
mingw32-make ${DEFAULT_MAKE_FLAGS} nimbus_verified_proxy libverifproxy
build/nimbus_verified_proxy.exe --help
mingw32-make ${DEFAULT_MAKE_FLAGS} nimbus-verified-proxy-test

- name: Run verified proxy tests (Linux)
if: runner.os == 'Linux'
run: |
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib"
DEFAULT_MAKE_FLAGS="-j${ncpu}"
env CC=gcc make ${DEFAULT_MAKE_FLAGS} nimbus_verified_proxy libverifproxy
build/nimbus_verified_proxy --help
# CC is needed to select correct compiler 32/64 bit
env CC=gcc CXX=g++ make ${DEFAULT_MAKE_FLAGS} nimbus-verified-proxy-test

- name: Run verified proxy tests (Macos)
if: runner.os == 'Macos'
run: |
DEFAULT_MAKE_FLAGS="-j${ncpu}"
make ${DEFAULT_MAKE_FLAGS} nimbus_verified_proxy libverifproxy
build/nimbus_verified_proxy --help
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,7 @@
path = vendor/nim-minilru
url = https://github.com/status-im/nim-minilru.git
branch = master
[submodule "vendor/nim-mcl"]
path = vendor/nim-mcl
url = https://github.com/status-im/nim-mcl
branch = main
4 changes: 4 additions & 0 deletions config.nims
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ elif defined(riscv64):
# and seems to be the minimum extensions needed to build.
switch("passC", "-march=rv64gc")
switch("passL", "-march=rv64gc")
elif defined(linux) and defined(arm64):
# clang can't handle "-march=native"
switch("passC", "-march=armv8-a")
switch("passL", "-march=armv8-a")
else:
switch("passC", "-march=native")
switch("passL", "-march=native")
Expand Down
4 changes: 4 additions & 0 deletions execution_chain/compile_info.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@
const chronicles_line_numbers {.strdefine.} = "0"
when chronicles_line_numbers notin ["0", "off"]:
{.hint: "*** Compiling with logger line numbers enabled".}

const enable_mcl_lib* {.booldefine.} = true
when enable_mcl_lib:
{.hint: "*** Compiling with mcl library".}
174 changes: 174 additions & 0 deletions execution_chain/evm/bncurve_mcl.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# nimbus-execution-client
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms

{.push raises: [].}

import
results,
stew/assign2,
bncurve/arith,
mcl/bn_abi,
./evm_errors,
./types

# one time initialization
doAssert(mclBn_init(MCL_BN_SNARK1, MCLBN_COMPILED_TIME_VAR) == 0.cint)

const
ioMode = MCLBN_IO_SERIALIZE or MCLBN_IO_BIG_ENDIAN

func isAllZero(data: openArray[byte]): bool =
for c in data:
if c != 0: return false
true

# deserialize Fp from 32 byte big-endian number.
func deserialize(x: var BnFp, buf: openArray[byte]): bool =
mclBnFp_setStr(x.addr, cast[ptr char](buf[0].addr), 32, ioMode) == 0.cint

func deserialize(x: var BnFp2, buf: openArray[byte]): bool =
deserialize(x.d[1], buf) and deserialize(x.d[0], buf.toOpenArray(32, buf.len-1))

func deserialize(x: var BnFr, buf: openArray[byte]): bool =
mclBnFr_setBigEndianMod(x.addr, buf[0].addr, 32.mclSize) == 0

func deserialize(P: var BnG1, buf: openArray[byte]): bool =
if buf.isAllZero:
mclBnG1_clear(P.addr)
return true

if not deserialize(P.x, buf) or not deserialize(P.y, buf.toOpenArray(32, buf.len-1)):
return false

mclBnFp_setInt32(P.z.addr, 1.cint)
mclBnG1_isValid(P.addr) == 1.cint

func deserialize(P: var BnG2, buf: openArray[byte]): bool =
if buf.isAllZero:
mclBnG2_clear(P.addr)
return true

if not deserialize(P.x, buf) or not deserialize(P.y, buf.toOpenArray(64, buf.len-1)):
return false

mclBnFp_setInt32(P.z.d[0].addr, 1.cint)
mclBnFp_clear(P.z.d[1].addr)
mclBnG2_isValid(P.addr) == 1.cint

# serialize Fp as 32 byte big-endian number.
func serialize(buf: var openArray[byte], x: BnFp): bool =
# sigh, getStr output buf is zero terminated
var tmp: array[33, byte]
result = mclBnFp_getStr(cast[ptr char](tmp[0].addr), 32, x.addr, ioMode) == 32.mclSize
assign(buf.toOpenArray(0, 31), tmp.toOpenArray(0, 31))

# Serialize P.x|P.y.
# Set _buf to all zeros if P == 0.
func serialize(buf: var openArray[byte], P: BnG1): bool =
if mclBnG1_isZero(P.addr) == 1.cint:
zeroMem(buf[0].addr, 64)
return true

var Pn {.noinit.}: BnG1
mclBnG1_normalize(Pn.addr, P.addr)
serialize(buf, Pn.x) and serialize(buf.toOpenArray(32, buf.len-1), Pn.y)

func bn256ecAddImpl*(c: Computation): EvmResultVoid =
var
input: array[128, byte]
p1 {.noinit.}: BnG1
p2 {.noinit.}: BnG1
apo {.noinit.}: BnG1

# Padding data
let len = min(c.msg.data.len, 128) - 1
assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len))

if not p1.deserialize(input.toOpenArray(0, 63)):
return err(prcErr(PrcInvalidPoint))

if not p2.deserialize(input.toOpenArray(64, 127)):
return err(prcErr(PrcInvalidPoint))

mclBnG1_add(apo.addr, p1.addr, p2.addr)

c.output.setLen(64)
if not serialize(c.output, apo):
zeroMem(c.output[0].addr, 64)

ok()

func bn256ecMulImpl*(c: Computation): EvmResultVoid =
var
input: array[96, byte]
p1 {.noinit.}: BnG1
fr {.noinit.}: BnFr
apo {.noinit.}: BnG1

# Padding data
let len = min(c.msg.data.len, 96) - 1
assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len))

if not p1.deserialize(input.toOpenArray(0, 63)):
return err(prcErr(PrcInvalidPoint))

if not fr.deserialize(input.toOpenArray(64, 95)):
return err(prcErr(PrcInvalidPoint))

mclBnG1_mul(apo.addr, p1.addr, fr.addr)

c.output.setLen(64)
if not serialize(c.output, apo):
zeroMem(c.output[0].addr, 64)

ok()

func bn256ecPairingImpl*(c: Computation): EvmResultVoid =
let msglen = c.msg.data.len
if msglen == 0:
# we can discard here because we supply buffer of proper size
c.output.setLen(32)
discard BNU256.one().toBytesBE(c.output)
else:
# Calculate number of pairing pairs
let count = msglen div 192
# Pairing accumulator
var
acc {.noinit.}: BnGT
one {.noinit.}: BnGT
tmp {.noinit.}: BnGT

mclBnGT_setInt(acc.addr, 1.mclInt)
mclBnGT_setInt(one.addr, 1.mclInt)

var
p1 {.noinit.}: BnG1
p2 {.noinit.}: BnG2

for i in 0..<count:
let s = i * 192

# Loading AffinePoint[G1], bytes from [0..63]
if not p1.deserialize(c.msg.data.toOpenArray(s, s+63)):
return err(prcErr(PrcInvalidPoint))

# Loading AffinePoint[G2], bytes from [64..191]
if not p2.deserialize(c.msg.data.toOpenArray(s+64, s+191)):
return err(prcErr(PrcInvalidPoint))

# Accumulate pairing result
mclBn_pairing(tmp.addr, p1.addr, p2.addr)
mclBnGT_mul(acc.addr, acc.addr, tmp.addr)

c.output.setLen(32)
if mclBnGT_isEqual(acc.addr, one.addr) == 1.cint:
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytesBE(c.output)

ok()
121 changes: 121 additions & 0 deletions execution_chain/evm/bncurve_nim.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# nimbus-execution-client
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms

{.push raises: [].}

import
results,
stew/assign2,
bncurve/[fields, groups],
./evm_errors,
./types

func simpleDecode(dst: var FQ2, src: openArray[byte]): bool {.noinit.} =
# bypassing FQ2.fromBytes
# because we want to check `value > modulus`
result = false
if dst.c1.fromBytes(src.toOpenArray(0, 31)) and
dst.c0.fromBytes(src.toOpenArray(32, 63)):
result = true

template simpleDecode(dst: var FQ, src: openArray[byte]): bool =
fromBytes(dst, src)

func getPoint[T: G1|G2](_: typedesc[T], data: openArray[byte]): EvmResult[Point[T]] =
when T is G1:
const nextOffset = 32
var px, py: FQ
else:
const nextOffset = 64
var px, py: FQ2

if not px.simpleDecode(data.toOpenArray(0, nextOffset - 1)):
return err(prcErr(PrcInvalidPoint))
if not py.simpleDecode(data.toOpenArray(nextOffset, nextOffset * 2 - 1)):
return err(prcErr(PrcInvalidPoint))

if px.isZero() and py.isZero():
ok(T.zero())
else:
var ap: AffinePoint[T]
if not ap.init(px, py):
return err(prcErr(PrcInvalidPoint))
ok(ap.toJacobian())

func getFR(data: openArray[byte]): EvmResult[FR] =
var res: FR
if not res.fromBytes2(data):
return err(prcErr(PrcInvalidPoint))
ok(res)

func bn256ecAddImpl*(c: Computation): EvmResultVoid =
var
input: array[128, byte]
# Padding data
let len = min(c.msg.data.len, 128) - 1
assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len))
let
p1 = ? G1.getPoint(input.toOpenArray(0, 63))
p2 = ? G1.getPoint(input.toOpenArray(64, 127))
apo = (p1 + p2).toAffine()

c.output.setLen(64)
if isSome(apo):
# we can discard here because we supply proper buffer
discard apo.get().toBytes(c.output)

ok()

func bn256ecMulImpl*(c: Computation): EvmResultVoid =
var
input: array[96, byte]
# Padding data
let len = min(c.msg.data.len, 96) - 1
assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len))
let
p1 = ? G1.getPoint(input.toOpenArray(0, 63))
fr = ? getFR(input.toOpenArray(64, 95))
apo = (p1 * fr).toAffine()

c.output.setLen(64)
if isSome(apo):
# we can discard here because we supply buffer of proper size
discard apo.get().toBytes(c.output)

ok()

func bn256ecPairingImpl*(c: Computation): EvmResultVoid =
let msglen = c.msg.data.len
if msglen == 0:
# we can discard here because we supply buffer of proper size
c.output.setLen(32)
discard BNU256.one().toBytesBE(c.output)
else:
# Calculate number of pairing pairs
let count = msglen div 192
# Pairing accumulator
var acc = FQ12.one()

for i in 0..<count:
let
s = i * 192
# Loading AffinePoint[G1], bytes from [0..63]
p1 = ?G1.getPoint(c.msg.data.toOpenArray(s, s + 63))
# Loading AffinePoint[G2], bytes from [64..191]
p2 = ?G2.getPoint(c.msg.data.toOpenArray(s + 64, s + 191))

# Accumulate pairing result
acc = acc * pairing(p1, p2)

c.output.setLen(32)
if acc == FQ12.one():
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytesBE(c.output)

ok()
Loading