|
| 1 | +# nimbus-execution-client |
| 2 | +# Copyright (c) 2025 Status Research & Development GmbH |
| 3 | +# Licensed under either of |
| 4 | +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) |
| 5 | +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) |
| 6 | +# at your option. |
| 7 | +# This file may not be copied, modified, or distributed except according to |
| 8 | +# those terms |
| 9 | + |
| 10 | +{.push raises: [].} |
| 11 | + |
| 12 | +import |
| 13 | + results, |
| 14 | + stew/assign2, |
| 15 | + bncurve/arith, |
| 16 | + mcl/bn_abi, |
| 17 | + ./evm_errors, |
| 18 | + ./types |
| 19 | + |
| 20 | +# one time initialization |
| 21 | +doAssert(mclBn_init(MCL_BN_SNARK1, MCLBN_COMPILED_TIME_VAR) == 0.cint) |
| 22 | + |
| 23 | +const |
| 24 | + ioMode = MCLBN_IO_SERIALIZE or MCLBN_IO_BIG_ENDIAN |
| 25 | + |
| 26 | +func isAllZero(data: openArray[byte]): bool = |
| 27 | + for c in data: |
| 28 | + if c != 0: return false |
| 29 | + true |
| 30 | + |
| 31 | +# deserialize Fp from 32 byte big-endian number. |
| 32 | +func deserialize(x: var BnFp, buf: openArray[byte]): bool = |
| 33 | + mclBnFp_setStr(x.addr, cast[ptr char](buf[0].addr), 32, ioMode) == 0.cint |
| 34 | + |
| 35 | +func deserialize(x: var BnFp2, buf: openArray[byte]): bool = |
| 36 | + deserialize(x.d[1], buf) and deserialize(x.d[0], buf.toOpenArray(32, buf.len-1)) |
| 37 | + |
| 38 | +func deserialize(x: var BnFr, buf: openArray[byte]): bool = |
| 39 | + mclBnFr_setBigEndianMod(x.addr, buf[0].addr, 32.mclSize) == 0 |
| 40 | + |
| 41 | +func deserialize(P: var BnG1, buf: openArray[byte]): bool = |
| 42 | + if buf.isAllZero: |
| 43 | + mclBnG1_clear(P.addr) |
| 44 | + return true |
| 45 | + |
| 46 | + if not deserialize(P.x, buf) or not deserialize(P.y, buf.toOpenArray(32, buf.len-1)): |
| 47 | + return false |
| 48 | + |
| 49 | + mclBnFp_setInt32(P.z.addr, 1.cint) |
| 50 | + mclBnG1_isValid(P.addr) == 1.cint |
| 51 | + |
| 52 | +func deserialize(P: var BnG2, buf: openArray[byte]): bool = |
| 53 | + if buf.isAllZero: |
| 54 | + mclBnG2_clear(P.addr) |
| 55 | + return true |
| 56 | + |
| 57 | + if not deserialize(P.x, buf) or not deserialize(P.y, buf.toOpenArray(64, buf.len-1)): |
| 58 | + return false |
| 59 | + |
| 60 | + mclBnFp_setInt32(P.z.d[0].addr, 1.cint) |
| 61 | + mclBnFp_clear(P.z.d[1].addr) |
| 62 | + mclBnG2_isValid(P.addr) == 1.cint |
| 63 | + |
| 64 | +# serialize Fp as 32 byte big-endian number. |
| 65 | +func serialize(buf: var openArray[byte], x: BnFp): bool = |
| 66 | + # sigh, getStr output buf is zero terminated |
| 67 | + var tmp: array[33, byte] |
| 68 | + result = mclBnFp_getStr(cast[ptr char](tmp[0].addr), 32, x.addr, ioMode) == 32.mclSize |
| 69 | + assign(buf.toOpenArray(0, 31), tmp.toOpenArray(0, 31)) |
| 70 | + |
| 71 | +# Serialize P.x|P.y. |
| 72 | +# Set _buf to all zeros if P == 0. |
| 73 | +func serialize(buf: var openArray[byte], P: BnG1): bool = |
| 74 | + if mclBnG1_isZero(P.addr) == 1.cint: |
| 75 | + zeroMem(buf[0].addr, 64) |
| 76 | + return true |
| 77 | + |
| 78 | + var Pn {.noinit.}: BnG1 |
| 79 | + mclBnG1_normalize(Pn.addr, P.addr) |
| 80 | + serialize(buf, Pn.x) and serialize(buf.toOpenArray(32, buf.len-1), Pn.y) |
| 81 | + |
| 82 | +func bn256ecAddImpl*(c: Computation): EvmResultVoid = |
| 83 | + var |
| 84 | + input: array[128, byte] |
| 85 | + p1 {.noinit.}: BnG1 |
| 86 | + p2 {.noinit.}: BnG1 |
| 87 | + apo {.noinit.}: BnG1 |
| 88 | + |
| 89 | + # Padding data |
| 90 | + let len = min(c.msg.data.len, 128) - 1 |
| 91 | + assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len)) |
| 92 | + |
| 93 | + if not p1.deserialize(input.toOpenArray(0, 63)): |
| 94 | + return err(prcErr(PrcInvalidPoint)) |
| 95 | + |
| 96 | + if not p2.deserialize(input.toOpenArray(64, 127)): |
| 97 | + return err(prcErr(PrcInvalidPoint)) |
| 98 | + |
| 99 | + mclBnG1_add(apo.addr, p1.addr, p2.addr) |
| 100 | + |
| 101 | + c.output.setLen(64) |
| 102 | + if not serialize(c.output, apo): |
| 103 | + zeroMem(c.output[0].addr, 64) |
| 104 | + |
| 105 | + ok() |
| 106 | + |
| 107 | +func bn256ecMulImpl*(c: Computation): EvmResultVoid = |
| 108 | + var |
| 109 | + input: array[96, byte] |
| 110 | + p1 {.noinit.}: BnG1 |
| 111 | + fr {.noinit.}: BnFr |
| 112 | + apo {.noinit.}: BnG1 |
| 113 | + |
| 114 | + # Padding data |
| 115 | + let len = min(c.msg.data.len, 96) - 1 |
| 116 | + assign(input.toOpenArray(0, len), c.msg.data.toOpenArray(0, len)) |
| 117 | + |
| 118 | + if not p1.deserialize(input.toOpenArray(0, 63)): |
| 119 | + return err(prcErr(PrcInvalidPoint)) |
| 120 | + |
| 121 | + if not fr.deserialize(input.toOpenArray(64, 95)): |
| 122 | + return err(prcErr(PrcInvalidPoint)) |
| 123 | + |
| 124 | + mclBnG1_mul(apo.addr, p1.addr, fr.addr) |
| 125 | + |
| 126 | + c.output.setLen(64) |
| 127 | + if not serialize(c.output, apo): |
| 128 | + zeroMem(c.output[0].addr, 64) |
| 129 | + |
| 130 | + ok() |
| 131 | + |
| 132 | +func bn256ecPairingImpl*(c: Computation): EvmResultVoid = |
| 133 | + let msglen = c.msg.data.len |
| 134 | + if msglen == 0: |
| 135 | + # we can discard here because we supply buffer of proper size |
| 136 | + c.output.setLen(32) |
| 137 | + discard BNU256.one().toBytesBE(c.output) |
| 138 | + else: |
| 139 | + # Calculate number of pairing pairs |
| 140 | + let count = msglen div 192 |
| 141 | + # Pairing accumulator |
| 142 | + var |
| 143 | + acc {.noinit.}: BnGT |
| 144 | + one {.noinit.}: BnGT |
| 145 | + tmp {.noinit.}: BnGT |
| 146 | + |
| 147 | + mclBnGT_setInt(acc.addr, 1.mclInt) |
| 148 | + mclBnGT_setInt(one.addr, 1.mclInt) |
| 149 | + |
| 150 | + var |
| 151 | + p1 {.noinit.}: BnG1 |
| 152 | + p2 {.noinit.}: BnG2 |
| 153 | + |
| 154 | + for i in 0..<count: |
| 155 | + let s = i * 192 |
| 156 | + |
| 157 | + # Loading AffinePoint[G1], bytes from [0..63] |
| 158 | + if not p1.deserialize(c.msg.data.toOpenArray(s, s+63)): |
| 159 | + return err(prcErr(PrcInvalidPoint)) |
| 160 | + |
| 161 | + # Loading AffinePoint[G2], bytes from [64..191] |
| 162 | + if not p2.deserialize(c.msg.data.toOpenArray(s+64, s+191)): |
| 163 | + return err(prcErr(PrcInvalidPoint)) |
| 164 | + |
| 165 | + # Accumulate pairing result |
| 166 | + mclBn_pairing(tmp.addr, p1.addr, p2.addr) |
| 167 | + mclBnGT_mul(acc.addr, acc.addr, tmp.addr) |
| 168 | + |
| 169 | + c.output.setLen(32) |
| 170 | + if mclBnGT_isEqual(acc.addr, one.addr) == 1.cint: |
| 171 | + # we can discard here because we supply buffer of proper size |
| 172 | + discard BNU256.one().toBytesBE(c.output) |
| 173 | + |
| 174 | + ok() |
0 commit comments