nimbus-eth1/nimbus/evm/precompiles.nim
Jacek Sieka b3cb51e89e
Speed up evm stack (#2881)
The EVM stack is a hot spot in EVM execution and we end up paying a nim
seq tax in several ways, adding up to ~5% of execution time:

* on initial allocation, all bytes get zeroed - this means we have to
choose between allocating a full stack or just a partial one and then
growing it
* pushing and popping introduce additional zeroing
* reallocations on growth copy + zero - expensive again!
* redundant range checking on every operation reducing inlining etc

Here a custom stack using C memory is instroduced:

* no zeroing on allocation
* full stack allocated on EVM startup -> no reallocation during
execution
* fast push/pop - no zeroing again
* 32-byte alignment - this makes it easier for the compiler to use
vector instructions
* no stack allocated for precompiles (these never use it anyway)

Of course, this change also means we have to manage memory manually -
for the EVM, this turns out to be not too bad because we already manage
database transactions the same way (they have to be freed "manually") so
we can simply latch on to this mechanism.

While we're at it, this PR also skips database lookup for known
precompiles by resolving such addresses earlier.
2024-11-30 10:07:10 +01:00

763 lines
22 KiB
Nim

# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
import
std/[macros],
results,
"."/[types, blake2b_f, blscurve],
./interpreter/[gas_meter, gas_costs, utils/utils_numeric],
eth/common/keys,
chronicles,
nimcrypto/[ripemd, sha2, utils],
bncurve/[fields, groups],
stew/assign2,
../common/evmforks,
../core/eip4844,
./modexp,
./evm_errors,
./computation,
eth/common/[base, addresses]
type
PrecompileAddresses* = enum
# Frontier to Spurious Dragron
paEcRecover = 0x01,
paSha256 = 0x02,
paRipeMd160 = 0x03,
paIdentity = 0x04,
# Byzantium and Constantinople
paModExp = 0x05,
paEcAdd = 0x06,
paEcMul = 0x07,
paPairing = 0x08,
# Istanbul
paBlake2bf = 0x09,
# Cancun
paPointEvaluation = 0x0A,
# Prague (EIP-2537)
paBlsG1Add = 0x0b,
paBlsG1Mul = 0x0c,
paBlsG1MultiExp = 0x0d,
paBlsG2Add = 0x0e,
paBlsG2Mul = 0x0f,
paBlsG2MultiExp = 0x10,
paBlsPairing = 0x11,
paBlsMapG1 = 0x12,
paBlsMapG2 = 0x13
SigRes = object
msgHash: array[32, byte]
sig: Signature
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
func getMaxPrecompileAddr(fork: EVMFork): PrecompileAddresses =
if fork < FkByzantium: paIdentity
elif fork < FkIstanbul: paPairing
elif fork < FkCancun: paBlake2bf
elif fork < FkPrague: paPointEvaluation
else: PrecompileAddresses.high
func validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
(addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)
func getSignature(c: Computation): EvmResult[SigRes] =
# input is Hash, V, R, S
template data: untyped = c.msg.data
var bytes: array[65, byte] # will hold R[32], S[32], V[1], in that order
let maxPos = min(data.high, 127)
# if we don't have at minimum 64 bytes, there can be no valid V
if maxPos < 63:
return err(prcErr(PrcInvalidSig))
let v = data[63]
# check if V[32] is 27 or 28
if not (v.int in 27..28):
return err(prcErr(PrcInvalidSig))
for x in 32..<63:
if data[x] != 0:
return err(prcErr(PrcInvalidSig))
bytes[64] = v - 27
# if there is more data for R and S, copy it. Else, defaulted zeroes are
# used for R and S
if maxPos >= 64:
# Copy message data to buffer
assign(bytes.toOpenArray(0, (maxPos-64)), data.toOpenArray(64, maxPos))
let sig = Signature.fromRaw(bytes).valueOr:
return err(prcErr(PrcInvalidSig))
var res = SigRes(sig: sig)
# extract message hash, only need to copy when there is a valid signature
assign(res.msgHash, data.toOpenArray(0, 31))
ok(res)
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)
# ------------------------------------------------------------------------------
# Precompiles functions
# ------------------------------------------------------------------------------
func ecRecover(c: Computation): EvmResultVoid =
? c.gasMeter.consumeGas(
GasECRecover,
reason="ECRecover Precompile")
let
sig = ? c.getSignature()
pubkey = recover(sig.sig, SkMessage(sig.msgHash)).valueOr:
return err(prcErr(PrcInvalidSig))
c.output.setLen(32)
assign(c.output.toOpenArray(12, 31), pubkey.toCanonicalAddress().data)
ok()
func sha256(c: Computation): EvmResultVoid =
let
wordCount = wordCount(c.msg.data.len)
gasFee = GasSHA256 + wordCount.GasInt * GasSHA256Word
? c.gasMeter.consumeGas(gasFee, reason="SHA256 Precompile")
assign(c.output, sha2.sha256.digest(c.msg.data).data)
ok()
func ripemd160(c: Computation): EvmResultVoid =
let
wordCount = wordCount(c.msg.data.len)
gasFee = GasRIPEMD160 + wordCount.GasInt * GasRIPEMD160Word
? c.gasMeter.consumeGas(gasFee, reason="RIPEMD160 Precompile")
c.output.setLen(32)
assign(c.output.toOpenArray(12, 31), ripemd.ripemd160.digest(c.msg.data).data)
ok()
func identity(c: Computation): EvmResultVoid =
let
wordCount = wordCount(c.msg.data.len)
gasFee = GasIdentity + wordCount.GasInt * GasIdentityWord
? c.gasMeter.consumeGas(gasFee, reason="Identity Precompile")
assign(c.output, c.msg.data)
ok()
func modExpFee(c: Computation,
baseLen, expLen, modLen: UInt256,
fork: EVMFork): EvmResult[GasInt] =
template data: untyped {.dirty.} =
c.msg.data
func mulComplexity(x: UInt256): UInt256 =
## Estimates the difficulty of Karatsuba multiplication
if x <= 64.u256: x * x
elif x <= 1024.u256: x * x div 4.u256 + 96.u256 * x - 3072.u256
else: x * x div 16.u256 + 480.u256 * x - 199680.u256
func mulComplexityEIP2565(x: UInt256): UInt256 =
# gas = ceil(x div 8) ^ 2
result = x + 7
result = result div 8
result = result * result
let adjExpLen = block:
let
baseL = baseLen.safeInt
expL = expLen.safeInt
first32 = if baseL.uint64 + expL.uint64 < high(int32).uint64 and baseL < data.len:
data.rangeToPadded[:UInt256](96 + baseL, 95 + baseL + expL, min(expL, 32))
else:
0.u256
if expLen <= 32:
if first32.isZero(): 0.u256
else: first32.log2.u256 # highest-bit in exponent
else:
if not first32.isZero:
8.u256 * (expLen - 32.u256) + first32.log2.u256
else:
8.u256 * (expLen - 32.u256)
template gasCalc(comp, divisor: untyped): untyped =
(
max(modLen, baseLen).comp *
max(adjExpLen, 1.u256)
) div divisor
# EIP2565: modExp gas cost
let gasFee = if fork >= FkBerlin: gasCalc(mulComplexityEIP2565, GasQuadDivisorEIP2565)
else: gasCalc(mulComplexity, GasQuadDivisor)
if gasFee > high(GasInt).u256:
return err(gasErr(OutOfGas))
var res = gasFee.truncate(GasInt)
# EIP2565: modExp gas cost
if fork >= FkBerlin and res < 200.GasInt:
res = 200.GasInt
ok(res)
func modExp(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
## Modular exponentiation precompiled contract
## Yellow Paper Appendix E
## EIP-198 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md
# Parsing the data
template data: untyped {.dirty.} =
c.msg.data
let # lengths Base, Exponent, Modulus
baseL = data.rangeToPadded[:UInt256](0, 31, 32)
expL = data.rangeToPadded[:UInt256](32, 63, 32)
modL = data.rangeToPadded[:UInt256](64, 95, 32)
baseLen = baseL.safeInt
expLen = expL.safeInt
modLen = modL.safeInt
let gasFee = ? modExpFee(c, baseL, expL, modL, fork)
? c.gasMeter.consumeGas(gasFee, reason="ModExp Precompile")
if baseLen == 0 and modLen == 0:
# This is a special case where expLength can be very big.
c.output = @[]
return ok()
const maxSize = int32.high.u256
if baseL > maxSize or expL > maxSize or modL > maxSize:
return err(prcErr(PrcInvalidParam))
# TODO:
# add EVM special case:
# - modulo <= 1: return zero
# - exp == zero: return one
let output = modExp(
data.rangeToPadded(96, baseLen),
data.rangeToPadded(96 + baseLen, expLen),
data.rangeToPadded(96 + baseLen + expLen, modLen)
)
# maximum output len is the same as modLen
# if it less than modLen, it will be zero padded at left
if output.len >= modLen:
assign(c.output, output.toOpenArray(output.len-modLen, output.len-1))
else:
c.output = newSeq[byte](modLen)
assign(c.output.toOpenArray(c.output.len-output.len, c.output.len-1), output)
ok()
func bn256ecAdd(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul
? c.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile")
var
input: array[128, byte]
# Padding data
let len = min(c.msg.data.len, 128) - 1
input[0..len] = c.msg.data[0..len]
var p1 = ? G1.getPoint(input.toOpenArray(0, 63))
var p2 = ? G1.getPoint(input.toOpenArray(64, 127))
var 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 bn256ecMul(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul
? c.gasMeter.consumeGas(gasFee, reason="ecMul Precompile")
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))
var p1 = ? G1.getPoint(input.toOpenArray(0, 63))
var fr = ? getFR(input.toOpenArray(64, 95))
var 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 bn256ecPairing(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
let msglen = c.msg.data.len
if msglen mod 192 != 0:
return err(prcErr(PrcInvalidParam))
let numPoints = GasInt msglen div 192
let gasFee = if fork < FkIstanbul:
GasECPairingBase + numPoints * GasECPairingPerPoint
else:
GasECPairingBaseIstanbul + numPoints * GasECPairingPerPointIstanbul
? c.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
c.output.setLen(32)
if msglen == 0:
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytes(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]
var p1 = ? G1.getPoint(c.msg.data.toOpenArray(s, s + 63))
# Loading AffinePoint[G2], bytes from [64..191]
var p2 = ? G2.getPoint(c.msg.data.toOpenArray(s + 64, s + 191))
# Accumulate pairing result
acc = acc * pairing(p1, p2)
if acc == FQ12.one():
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytes(c.output)
ok()
func blake2bf(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if len(input) == blake2FInputLength:
let gasFee = GasInt(beLoad32(input, 0))
? c.gasMeter.consumeGas(gasFee, reason="blake2bf Precompile")
c.output.setLen(64)
if not blake2b_F(input, c.output):
return err(prcErr(PrcInvalidParam))
ok()
func blsG1Add(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 256:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381G1AddGas, reason="blsG1Add Precompile")
var a, b: BLS_G1
if not a.decodePoint(input.toOpenArray(0, 127)):
return err(prcErr(PrcInvalidPoint))
if not b.decodePoint(input.toOpenArray(128, 255)):
return err(prcErr(PrcInvalidPoint))
a.add b
c.output.setLen(128)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsG1Mul(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 160:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381G1MulGas, reason="blsG1Mul Precompile")
var a: BLS_G1
if not a.decodePoint(input.toOpenArray(0, 127)):
return err(prcErr(PrcInvalidPoint))
var scalar: BLS_SCALAR
if not scalar.fromBytes(input.toOpenArray(128, 159)):
return err(prcErr(PrcInvalidParam))
a.mul(scalar)
c.output.setLen(128)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
const
Bls12381MultiExpDiscountTable = [
1200, 888, 764, 641, 594, 547, 500, 453, 438, 423,
408, 394, 379, 364, 349, 334, 330, 326, 322, 318,
314, 310, 306, 302, 298, 294, 289, 285, 281, 277,
273, 269, 268, 266, 265, 263, 262, 260, 259, 257,
256, 254, 253, 251, 250, 248, 247, 245, 244, 242,
241, 239, 238, 236, 235, 233, 232, 231, 229, 228,
226, 225, 223, 222, 221, 220, 219, 219, 218, 217,
216, 216, 215, 214, 213, 213, 212, 211, 211, 210,
209, 208, 208, 207, 206, 205, 205, 204, 203, 202,
202, 201, 200, 199, 199, 198, 197, 196, 196, 195,
194, 193, 193, 192, 191, 191, 190, 189, 188, 188,
187, 186, 185, 185, 184, 183, 182, 182, 181, 180,
179, 179, 178, 177, 176, 176, 175, 174
]
func calcBlsMultiExpGas(K: GasInt, gasCost: GasInt): GasInt =
# Calculate G1 point, scalar value pair length
if K == 0:
# Return 0 gas for small input length
return 0.GasInt
const dLen = Bls12381MultiExpDiscountTable.len
# Lookup discount value for G1 point, scalar value pair length
let discount = if K < dLen: GasInt Bls12381MultiExpDiscountTable[K-1]
else: GasInt Bls12381MultiExpDiscountTable[dLen-1]
# Calculate gas and return the result
result = (K * gasCost * discount) div 1000
func blsG1MultiExp(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
const L = 160
if (input.len == 0) or ((input.len mod L) != 0):
return err(prcErr(PrcInvalidParam))
let
K = input.len div L
gas = calcBlsMultiExpGas(GasInt K, Bls12381G1MulGas)
? c.gasMeter.consumeGas(gas, reason="blsG1MultiExp Precompile")
var
p: BLS_G1
s: BLS_SCALAR
acc: BLS_G1
# Decode point scalar pairs
for i in 0..<K:
let off = L * i
# Decode G1 point
if not p.decodePoint(input.toOpenArray(off, off+127)):
return err(prcErr(PrcInvalidPoint))
# Decode scalar value
if not s.fromBytes(input.toOpenArray(off+128, off+159)):
return err(prcErr(PrcInvalidParam))
p.mul(s)
if i == 0:
acc = p
else:
acc.add(p)
c.output.setLen(128)
if not encodePoint(acc, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsG2Add(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 512:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381G2AddGas, reason="blsG2Add Precompile")
var a, b: BLS_G2
if not a.decodePoint(input.toOpenArray(0, 255)):
return err(prcErr(PrcInvalidPoint))
if not b.decodePoint(input.toOpenArray(256, 511)):
return err(prcErr(PrcInvalidPoint))
a.add b
c.output.setLen(256)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsG2Mul(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 288:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381G2MulGas, reason="blsG2Mul Precompile")
var a: BLS_G2
if not a.decodePoint(input.toOpenArray(0, 255)):
return err(prcErr(PrcInvalidPoint))
var scalar: BLS_SCALAR
if not scalar.fromBytes(input.toOpenArray(256, 287)):
return err(prcErr(PrcInvalidParam))
a.mul(scalar)
c.output.setLen(256)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsG2MultiExp(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
const L = 288
if (input.len == 0) or ((input.len mod L) != 0):
return err(prcErr(PrcInvalidParam))
let
K = input.len div L
gas = calcBlsMultiExpGas(GasInt K, Bls12381G2MulGas)
? c.gasMeter.consumeGas(gas, reason="blsG2MultiExp Precompile")
var
p: BLS_G2
s: BLS_SCALAR
acc: BLS_G2
# Decode point scalar pairs
for i in 0..<K:
let off = L * i
# Decode G1 point
if not p.decodePoint(input.toOpenArray(off, off+255)):
return err(prcErr(PrcInvalidPoint))
# Decode scalar value
if not s.fromBytes(input.toOpenArray(off+256, off+287)):
return err(prcErr(PrcInvalidParam))
p.mul(s)
if i == 0:
acc = p
else:
acc.add(p)
c.output.setLen(256)
if not encodePoint(acc, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsPairing(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
const L = 384
if (input.len == 0) or ((input.len mod L) != 0):
return err(prcErr(PrcInvalidParam))
let
K = input.len div L
gas = Bls12381PairingBaseGas + K.GasInt * Bls12381PairingPerPairGas
? c.gasMeter.consumeGas(gas, reason="blsG2Pairing Precompile")
var
g1: BLS_G1P
g2: BLS_G2P
acc: BLS_ACC
# Decode pairs
for i in 0..<K:
let off = L * i
# Decode G1 point
if not g1.decodePoint(input.toOpenArray(off, off+127)):
return err(prcErr(PrcInvalidPoint))
# Decode G2 point
if not g2.decodePoint(input.toOpenArray(off+128, off+383)):
return err(prcErr(PrcInvalidPoint))
# 'point is on curve' check already done,
# Here we need to apply subgroup checks.
if not g1.subgroupCheck:
return err(prcErr(PrcInvalidPoint))
if not g2.subgroupCheck:
return err(prcErr(PrcInvalidPoint))
# Update pairing engine with G1 and G2 points
if i == 0:
acc = millerLoop(g1, g2)
else:
acc.mul(millerLoop(g1, g2))
c.output.setLen(32)
if acc.check():
c.output[^1] = 1.byte
ok()
func blsMapG1(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 64:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381MapG1Gas, reason="blsMapG1 Precompile")
var fe: BLS_FE
if not fe.decodeFE(input):
return err(prcErr(PrcInvalidPoint))
let p = fe.mapFPToG1()
c.output.setLen(128)
if not encodePoint(p, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
func blsMapG2(c: Computation): EvmResultVoid =
template input: untyped =
c.msg.data
if input.len != 128:
return err(prcErr(PrcInvalidParam))
? c.gasMeter.consumeGas(Bls12381MapG2Gas, reason="blsMapG2 Precompile")
var fe: BLS_FE2
if not fe.decodeFE(input):
return err(prcErr(PrcInvalidPoint))
let p = fe.mapFPToG2()
c.output.setLen(256)
if not encodePoint(p, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
proc pointEvaluation(c: Computation): EvmResultVoid =
# Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof.
# Also verify that the provided commitment matches the provided versioned_hash.
# The data is encoded as follows: versioned_hash | z | y | commitment | proof |
template input: untyped =
c.msg.data
? c.gasMeter.consumeGas(POINT_EVALUATION_PRECOMPILE_GAS,
reason = "EIP-4844 Point Evaluation Precompile")
pointEvaluation(input).isOkOr:
return err(prcErr(PrcValidationError))
# return a constant
c.output = @PointEvaluationResult
ok()
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
iterator activePrecompiles*(fork: EVMFork): Address =
var res: Address
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
for c in PrecompileAddresses.low..maxPrecompileAddr:
if validPrecompileAddr(c.byte, maxPrecompileAddr.byte):
res.data[^1] = c.byte
yield res
func activePrecompilesList*(fork: EVMFork): seq[Address] =
for address in activePrecompiles(fork):
result.add address
proc getPrecompile*(fork: EVMFork, b: byte): Opt[PrecompileAddresses] =
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
if validPrecompileAddr(b, maxPrecompileAddr.byte):
Opt.some(PrecompileAddresses(b))
else:
Opt.none(PrecompileAddresses)
proc getPrecompile*(fork: EVMFork, codeAddress: Address): Opt[PrecompileAddresses] =
for i in 0..18:
if codeAddress.data[i] != 0:
return Opt.none(PrecompileAddresses)
getPrecompile(fork, codeAddress.data[19])
proc execPrecompile*(c: Computation, precompile: PrecompileAddresses) =
let fork = c.fork
let res = case precompile
of paEcRecover: ecRecover(c)
of paSha256: sha256(c)
of paRipeMd160: ripemd160(c)
of paIdentity: identity(c)
of paModExp: modExp(c, fork)
of paEcAdd: bn256ecAdd(c, fork)
of paEcMul: bn256ecMul(c, fork)
of paPairing: bn256ecPairing(c, fork)
of paBlake2bf: blake2bf(c)
of paPointEvaluation: pointEvaluation(c)
of paBlsG1Add: blsG1Add(c)
of paBlsG1Mul: blsG1Mul(c)
of paBlsG1MultiExp: blsG1MultiExp(c)
of paBlsG2Add: blsG2Add(c)
of paBlsG2Mul: blsG2Mul(c)
of paBlsG2MultiExp: blsG2MultiExp(c)
of paBlsPairing: blsPairing(c)
of paBlsMapG1: blsMapG1(c)
of paBlsMapG2: blsMapG2(c)
if res.isErr:
if res.error.code == EvmErrorCode.OutOfGas:
c.setError(EVMC_OUT_OF_GAS, $res.error.code, true)
else:
if fork >= FkByzantium and precompile > paIdentity:
c.setError(EVMC_PRECOMPILE_FAILURE, $res.error.code, true)
else:
# swallow any other precompiles errors
debug "execPrecompiles validation error", errCode = $res.error.code