2021-04-08 15:13:27 +00:00
|
|
|
# Nimbus
|
2024-03-21 11:24:32 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2021-04-08 15:13:27 +00:00
|
|
|
# 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.
|
|
|
|
|
2021-04-08 14:52:10 +00:00
|
|
|
import
|
2023-06-25 13:30:34 +00:00
|
|
|
std/[macros],
|
2024-05-30 12:54:03 +00:00
|
|
|
results,
|
2022-12-02 04:35:41 +00:00
|
|
|
"."/[types, blake2b_f, blscurve],
|
2021-06-01 11:27:05 +00:00
|
|
|
./interpreter/[gas_meter, gas_costs, utils/utils_numeric],
|
2022-12-02 04:35:41 +00:00
|
|
|
../errors, eth/[common, keys], chronicles,
|
|
|
|
nimcrypto/[ripemd, sha2, utils], bncurve/[fields, groups],
|
2023-05-10 15:40:48 +00:00
|
|
|
../common/evmforks,
|
2023-06-24 13:56:44 +00:00
|
|
|
../core/eip4844,
|
2023-08-28 12:10:31 +00:00
|
|
|
./modexp,
|
|
|
|
./computation
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-06-24 13:56:44 +00:00
|
|
|
|
2021-04-08 14:52:10 +00:00
|
|
|
type
|
|
|
|
PrecompileAddresses* = enum
|
|
|
|
# Frontier to Spurious Dragron
|
2023-06-24 13:56:44 +00:00
|
|
|
paEcRecover = 0x01,
|
|
|
|
paSha256 = 0x02,
|
|
|
|
paRipeMd160 = 0x03,
|
|
|
|
paIdentity = 0x04,
|
2021-04-08 14:52:10 +00:00
|
|
|
# Byzantium and Constantinople
|
2023-06-24 13:56:44 +00:00
|
|
|
paModExp = 0x05,
|
|
|
|
paEcAdd = 0x06,
|
|
|
|
paEcMul = 0x07,
|
|
|
|
paPairing = 0x08,
|
2021-04-08 14:52:10 +00:00
|
|
|
# Istanbul
|
2023-06-24 13:56:44 +00:00
|
|
|
paBlake2bf = 0x09,
|
2023-08-27 06:13:37 +00:00
|
|
|
paPointEvaluation = 0x0A
|
2021-04-08 14:52:10 +00:00
|
|
|
# Berlin
|
2021-05-16 12:44:24 +00:00
|
|
|
# EIP-2537: disabled
|
|
|
|
# reason: not included in berlin
|
|
|
|
# paBlsG1Add
|
|
|
|
# paBlsG1Mul
|
|
|
|
# paBlsG1MultiExp
|
|
|
|
# paBlsG2Add
|
|
|
|
# paBlsG2Mul
|
|
|
|
# paBlsG2MultiExp
|
|
|
|
# paBlsPairing
|
|
|
|
# paBlsMapG1
|
|
|
|
# paBlsMapG2
|
2023-06-24 13:56:44 +00:00
|
|
|
# Cancun
|
2023-08-28 12:10:31 +00:00
|
|
|
|
2023-06-24 13:56:44 +00:00
|
|
|
|
|
|
|
proc getMaxPrecompileAddr(fork: EVMFork): PrecompileAddresses =
|
|
|
|
if fork < FkByzantium: paIdentity
|
|
|
|
elif fork < FkIstanbul: paPairing
|
|
|
|
# EIP 2537: disabled
|
|
|
|
# reason: not included in berlin
|
|
|
|
# elif fork < FkBerlin: paBlake2bf
|
|
|
|
elif fork < FkCancun: paBlake2bf
|
|
|
|
else: PrecompileAddresses.high
|
|
|
|
|
|
|
|
proc validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
|
2023-08-24 05:11:19 +00:00
|
|
|
(addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-06-24 13:56:44 +00:00
|
|
|
proc validPrecompileAddr(addrByte: byte, fork: EVMFork): bool =
|
|
|
|
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
|
|
|
|
validPrecompileAddr(addrByte, maxPrecompileAddr.byte)
|
|
|
|
|
|
|
|
iterator activePrecompiles*(fork: EVMFork): EthAddress =
|
2021-04-08 14:52:10 +00:00
|
|
|
var res: EthAddress
|
2023-06-24 13:56:44 +00:00
|
|
|
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
|
|
|
|
for c in PrecompileAddresses.low..maxPrecompileAddr:
|
|
|
|
if validPrecompileAddr(c.byte, maxPrecompileAddr.byte):
|
|
|
|
res[^1] = c.byte
|
|
|
|
yield res
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2024-03-21 11:24:32 +00:00
|
|
|
func activePrecompilesList*(fork: EVMFork): seq[EthAddress] =
|
|
|
|
for address in activePrecompiles(fork):
|
|
|
|
result.add address
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc getSignature(c: Computation): (array[32, byte], Signature) =
|
2021-04-08 14:52:10 +00:00
|
|
|
# input is Hash, V, R, S
|
2023-08-28 12:10:31 +00:00
|
|
|
template data: untyped = c.msg.data
|
2021-04-08 14:52:10 +00:00
|
|
|
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:
|
|
|
|
let v = data[63]
|
|
|
|
# check if V[32] is 27 or 28
|
|
|
|
if not (v.int in 27..28):
|
|
|
|
raise newException(ValidationError, "Invalid V in getSignature")
|
|
|
|
for x in 32..<63:
|
|
|
|
if data[x] != 0:
|
|
|
|
raise newException(ValidationError, "Invalid V in getSignature")
|
|
|
|
|
|
|
|
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
|
|
|
|
bytes[0..(maxPos-64)] = data[64..maxPos]
|
|
|
|
|
|
|
|
let sig = Signature.fromRaw(bytes)
|
|
|
|
if sig.isErr:
|
2023-08-28 12:10:31 +00:00
|
|
|
raise newException(ValidationError, "Could not recover signature c")
|
2021-04-08 14:52:10 +00:00
|
|
|
result[1] = sig[]
|
|
|
|
|
|
|
|
# extract message hash, only need to copy when there is a valid signature
|
|
|
|
result[0][0..31] = data[0..31]
|
|
|
|
else:
|
|
|
|
raise newException(ValidationError, "Invalid V in getSignature")
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc simpleDecode*(dst: var FQ2, src: openArray[byte]): bool {.noinit.} =
|
2021-04-08 14:52:10 +00:00
|
|
|
# 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
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
template simpleDecode*(dst: var FQ, src: openArray[byte]): bool =
|
2021-04-08 14:52:10 +00:00
|
|
|
fromBytes(dst, src)
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc getPoint[T: G1|G2](t: typedesc[T], data: openArray[byte]): Point[T] =
|
2021-04-08 14:52:10 +00:00
|
|
|
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)):
|
|
|
|
raise newException(ValidationError, "Could not get point value")
|
|
|
|
if not py.simpleDecode(data.toOpenArray(nextOffset, nextOffset * 2 - 1)):
|
|
|
|
raise newException(ValidationError, "Could not get point value")
|
|
|
|
|
|
|
|
if px.isZero() and py.isZero():
|
|
|
|
result = T.zero()
|
|
|
|
else:
|
|
|
|
var ap: AffinePoint[T]
|
|
|
|
if not ap.init(px, py):
|
|
|
|
raise newException(ValidationError, "Point is not on curve")
|
|
|
|
result = ap.toJacobian()
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc getFR(data: openArray[byte]): FR =
|
2021-04-08 14:52:10 +00:00
|
|
|
if not result.fromBytes2(data):
|
|
|
|
raise newException(ValidationError, "Could not get FR value")
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc ecRecover*(c: Computation) =
|
|
|
|
c.gasMeter.consumeGas(
|
2021-04-08 14:52:10 +00:00
|
|
|
GasECRecover,
|
|
|
|
reason="ECRecover Precompile")
|
|
|
|
|
|
|
|
var
|
2023-08-28 12:10:31 +00:00
|
|
|
(msgHash, sig) = c.getSignature()
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
var pubkey = recover(sig, SkMessage(msgHash))
|
|
|
|
if pubkey.isErr:
|
2023-08-28 12:10:31 +00:00
|
|
|
raise newException(ValidationError, "Could not derive public key from c")
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.output.setLen(32)
|
|
|
|
c.output[12..31] = pubkey[].toCanonicalAddress()
|
Tracing: Remove some trace messages that occur a lot during sync
Disable some trace messages which appeared a lot in the output and probably
aren't so useful any more, when block processing is functioning well at high
speed.
Turning on the trace level globally is useful to get a feel for what's
happening, but only if each category is kept to a reasonable amount.
As well as overwhelming the output so that it's hard to see general activity,
some of these messages happen so much they severely slow down processing. Ones
called every time an EVM opcode uses some gas are particularly extreme.
These messages have all been chosen as things which are probably not useful any
more (the relevant functionality has been debugged and is tested plenty).
These have been commented out rather than removed. It may be that turning
trace topics on/off, or other selection, is a better longer term solution, but
that will require better command line options and good defaults for sure.
(I think higher levels `tracev` and `tracevv` levels (extra verbose) would be
more useful for this sort of deep tracing on request.)
For now, enabling `--log-level:TRACE` on the command line is quite useful as
long as we keep each category reasonable, and this patch tries to keep that
balance.
- Don't show "has transactions" on virtually every block imported.
- Don't show "Sender" and "txHash" lines on every transaction processed.
- Don't show "GAS CONSUMPTION" on every opcode executed", this is way too much.
- Don't show "GAS RETURNED" and "GAS REFUND" on each contract call.
- Don't show "op: Stop" on every Stop opcode, which means every transaction.
- Don't show "Insufficient funds" whenever a contract can't call another.
- Don't show "ECRecover", "SHA256 precompile", "RIPEMD160", "Identity"
or even "Call precompile" every time a precompile is called. These are
very well tested now.
- Don't show "executeOpcodes error" whenever a contract returns an error.
(This is changed to `trace` too, it's a normal event that is well tested.)
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-07-22 13:35:41 +00:00
|
|
|
#trace "ECRecover precompile", derivedKey = pubkey[].toCanonicalAddress()
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc sha256*(c: Computation) =
|
2021-04-08 14:52:10 +00:00
|
|
|
let
|
2023-08-28 12:10:31 +00:00
|
|
|
wordCount = wordCount(c.msg.data.len)
|
2021-04-08 14:52:10 +00:00
|
|
|
gasFee = GasSHA256 + wordCount * GasSHA256Word
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason="SHA256 Precompile")
|
|
|
|
c.output = @(sha2.sha256.digest(c.msg.data).data)
|
|
|
|
#trace "SHA256 precompile", output = c.output.toHex
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc ripemd160*(c: Computation) =
|
2021-04-08 14:52:10 +00:00
|
|
|
let
|
2023-08-28 12:10:31 +00:00
|
|
|
wordCount = wordCount(c.msg.data.len)
|
2021-04-08 14:52:10 +00:00
|
|
|
gasFee = GasRIPEMD160 + wordCount * GasRIPEMD160Word
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason="RIPEMD160 Precompile")
|
|
|
|
c.output.setLen(32)
|
|
|
|
c.output[12..31] = @(ripemd.ripemd160.digest(c.msg.data).data)
|
|
|
|
#trace "RIPEMD160 precompile", output = c.output.toHex
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc identity*(c: Computation) =
|
2021-04-08 14:52:10 +00:00
|
|
|
let
|
2023-08-28 12:10:31 +00:00
|
|
|
wordCount = wordCount(c.msg.data.len)
|
2021-04-08 14:52:10 +00:00
|
|
|
gasFee = GasIdentity + wordCount * GasIdentityWord
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason="Identity Precompile")
|
|
|
|
c.output = c.msg.data
|
|
|
|
#trace "Identity precompile", output = c.output.toHex
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc modExpFee(c: Computation, baseLen, expLen, modLen: UInt256, fork: EVMFork): GasInt =
|
2021-04-08 14:52:10 +00:00
|
|
|
template data: untyped {.dirty.} =
|
|
|
|
c.msg.data
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
func mulComplexity(x: UInt256): UInt256 =
|
2021-04-08 14:52:10 +00:00
|
|
|
## 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
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
func mulComplexityEIP2565(x: UInt256): UInt256 =
|
2021-04-08 14:52:10 +00:00
|
|
|
# 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:
|
2022-12-21 11:41:03 +00:00
|
|
|
data.rangeToPadded[:UInt256](96 + baseL, 95 + baseL + expL, min(expL, 32))
|
2021-04-08 14:52:10 +00:00
|
|
|
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
|
|
|
|
|
2021-05-16 12:22:01 +00:00
|
|
|
# EIP2565: modExp gas cost
|
|
|
|
let gasFee = if fork >= FkBerlin: gasCalc(mulComplexityEIP2565, GasQuadDivisorEIP2565)
|
|
|
|
else: gasCalc(mulComplexity, GasQuadDivisor)
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
if gasFee > high(GasInt).u256:
|
|
|
|
raise newException(OutOfGas, "modExp gas overflow")
|
|
|
|
|
|
|
|
result = gasFee.truncate(GasInt)
|
|
|
|
|
2021-05-16 12:22:01 +00:00
|
|
|
# EIP2565: modExp gas cost
|
|
|
|
if fork >= FkBerlin and result < 200.GasInt:
|
|
|
|
result = 200.GasInt
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc modExp*(c: Computation, fork: EVMFork = FkByzantium) =
|
2021-04-08 14:52:10 +00:00
|
|
|
## 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
|
2022-12-21 11:41:03 +00:00
|
|
|
baseL = data.rangeToPadded[:UInt256](0, 31, 32)
|
|
|
|
expL = data.rangeToPadded[:UInt256](32, 63, 32)
|
|
|
|
modL = data.rangeToPadded[:UInt256](64, 95, 32)
|
2021-04-08 14:52:10 +00:00
|
|
|
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
|
|
|
|
|
2023-05-10 15:40:48 +00:00
|
|
|
const maxSize = int32.high.u256
|
|
|
|
if baseL > maxSize or expL > maxSize or modL > maxSize:
|
|
|
|
raise newException(EVMError, "The Nimbus VM doesn't support oversized modExp operand")
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
c.output = @(output[^modLen..^1])
|
2021-04-08 14:52:10 +00:00
|
|
|
else:
|
2023-05-10 15:40:48 +00:00
|
|
|
c.output = newSeq[byte](modLen)
|
|
|
|
c.output[^output.len..^1] = output[0..^1]
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc bn256ecAdd*(c: Computation, fork: EVMFork = FkByzantium) =
|
2021-04-08 14:52:10 +00:00
|
|
|
let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile")
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
var
|
|
|
|
input: array[128, byte]
|
|
|
|
output: array[64, byte]
|
|
|
|
# Padding data
|
2023-08-28 12:10:31 +00:00
|
|
|
let len = min(c.msg.data.len, 128) - 1
|
|
|
|
input[0..len] = c.msg.data[0..len]
|
2021-04-08 14:52:10 +00:00
|
|
|
var p1 = G1.getPoint(input.toOpenArray(0, 63))
|
|
|
|
var p2 = G1.getPoint(input.toOpenArray(64, 127))
|
|
|
|
var apo = (p1 + p2).toAffine()
|
|
|
|
if isSome(apo):
|
|
|
|
# we can discard here because we supply proper buffer
|
|
|
|
discard apo.get().toBytes(output)
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.output = @output
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc bn256ecMul*(c: Computation, fork: EVMFork = FkByzantium) =
|
2021-04-08 14:52:10 +00:00
|
|
|
let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason="ecMul Precompile")
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
var
|
|
|
|
input: array[96, byte]
|
|
|
|
output: array[64, byte]
|
|
|
|
|
|
|
|
# Padding data
|
2023-08-28 12:10:31 +00:00
|
|
|
let len = min(c.msg.data.len, 96) - 1
|
|
|
|
input[0..len] = c.msg.data[0..len]
|
2021-04-08 14:52:10 +00:00
|
|
|
var p1 = G1.getPoint(input.toOpenArray(0, 63))
|
|
|
|
var fr = getFR(input.toOpenArray(64, 95))
|
|
|
|
var apo = (p1 * fr).toAffine()
|
|
|
|
if isSome(apo):
|
|
|
|
# we can discard here because we supply buffer of proper size
|
|
|
|
discard apo.get().toBytes(output)
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.output = @output
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc bn256ecPairing*(c: Computation, fork: EVMFork = FkByzantium) =
|
|
|
|
let msglen = len(c.msg.data)
|
2021-04-08 14:52:10 +00:00
|
|
|
if msglen mod 192 != 0:
|
|
|
|
raise newException(ValidationError, "Invalid input length")
|
|
|
|
|
|
|
|
let numPoints = msglen div 192
|
|
|
|
let gasFee = if fork < FkIstanbul:
|
|
|
|
GasECPairingBase + numPoints * GasECPairingPerPoint
|
|
|
|
else:
|
|
|
|
GasECPairingBaseIstanbul + numPoints * GasECPairingPerPointIstanbul
|
2023-08-28 12:10:31 +00:00
|
|
|
c.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
var output: array[32, byte]
|
|
|
|
if msglen == 0:
|
|
|
|
# we can discard here because we supply buffer of proper size
|
|
|
|
discard BNU256.one().toBytes(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]
|
2023-08-28 12:10:31 +00:00
|
|
|
var p1 = G1.getPoint(c.msg.data.toOpenArray(s, s + 63))
|
2021-04-08 14:52:10 +00:00
|
|
|
# Loading AffinePoint[G2], bytes from [64..191]
|
2023-08-28 12:10:31 +00:00
|
|
|
var p2 = G2.getPoint(c.msg.data.toOpenArray(s + 64, s + 191))
|
2021-04-08 14:52:10 +00:00
|
|
|
# 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(output)
|
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
c.output = @output
|
2021-04-08 14:52:10 +00:00
|
|
|
|
|
|
|
proc blake2bf*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if len(input) == blake2FInputLength:
|
|
|
|
let gasFee = GasInt(beLoad32(input, 0))
|
|
|
|
c.gasMeter.consumeGas(gasFee, reason="blake2bf Precompile")
|
|
|
|
|
|
|
|
var output: array[64, byte]
|
|
|
|
if not blake2b_F(input, output):
|
|
|
|
raise newException(ValidationError, "Blake2b F function invalid input")
|
|
|
|
else:
|
|
|
|
c.output = @output
|
|
|
|
|
|
|
|
proc blsG1Add*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 256:
|
|
|
|
raise newException(ValidationError, "blsG1Add invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381G1AddGas, reason="blsG1Add Precompile")
|
|
|
|
|
|
|
|
var a, b: BLS_G1
|
|
|
|
if not a.decodePoint(input.toOpenArray(0, 127)):
|
|
|
|
raise newException(ValidationError, "blsG1Add invalid input A")
|
|
|
|
|
|
|
|
if not b.decodePoint(input.toOpenArray(128, 255)):
|
|
|
|
raise newException(ValidationError, "blsG1Add invalid input B")
|
|
|
|
|
|
|
|
a.add b
|
|
|
|
|
|
|
|
c.output = newSeq[byte](128)
|
|
|
|
if not encodePoint(a, c.output):
|
|
|
|
raise newException(ValidationError, "blsG1Add encodePoint error")
|
|
|
|
|
|
|
|
proc blsG1Mul*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 160:
|
|
|
|
raise newException(ValidationError, "blsG1Mul invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381G1MulGas, reason="blsG1Mul Precompile")
|
|
|
|
|
|
|
|
var a: BLS_G1
|
|
|
|
if not a.decodePoint(input.toOpenArray(0, 127)):
|
|
|
|
raise newException(ValidationError, "blsG1Mul invalid input A")
|
|
|
|
|
|
|
|
var scalar: BLS_SCALAR
|
|
|
|
if not scalar.fromBytes(input.toOpenArray(128, 159)):
|
|
|
|
raise newException(ValidationError, "blsG1Mul invalid scalar")
|
|
|
|
|
|
|
|
a.mul(scalar)
|
|
|
|
|
|
|
|
c.output = newSeq[byte](128)
|
|
|
|
if not encodePoint(a, c.output):
|
|
|
|
raise newException(ValidationError, "blsG1Mul encodePoint error")
|
|
|
|
|
|
|
|
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: int, 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: Bls12381MultiExpDiscountTable[K-1]
|
|
|
|
else: Bls12381MultiExpDiscountTable[dLen-1]
|
|
|
|
|
|
|
|
# Calculate gas and return the result
|
|
|
|
result = (K * gasCost * discount) div 1000
|
|
|
|
|
|
|
|
proc blsG1MultiExp*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
const L = 160
|
|
|
|
if (input.len == 0) or ((input.len mod L) != 0):
|
|
|
|
raise newException(ValidationError, "blsG1MultiExp invalid input len")
|
|
|
|
|
|
|
|
let
|
|
|
|
K = input.len div L
|
|
|
|
gas = K.calcBlsMultiExpGas(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)):
|
|
|
|
raise newException(ValidationError, "blsG1MultiExp invalid input P")
|
|
|
|
|
|
|
|
# Decode scalar value
|
|
|
|
if not s.fromBytes(input.toOpenArray(off+128, off+159)):
|
|
|
|
raise newException(ValidationError, "blsG1MultiExp invalid scalar")
|
|
|
|
|
|
|
|
p.mul(s)
|
|
|
|
if i == 0:
|
|
|
|
acc = p
|
|
|
|
else:
|
|
|
|
acc.add(p)
|
|
|
|
|
|
|
|
c.output = newSeq[byte](128)
|
|
|
|
if not encodePoint(acc, c.output):
|
|
|
|
raise newException(ValidationError, "blsG1MuliExp encodePoint error")
|
|
|
|
|
|
|
|
proc blsG2Add*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 512:
|
|
|
|
raise newException(ValidationError, "blsG2Add invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381G2AddGas, reason="blsG2Add Precompile")
|
|
|
|
|
|
|
|
var a, b: BLS_G2
|
|
|
|
if not a.decodePoint(input.toOpenArray(0, 255)):
|
|
|
|
raise newException(ValidationError, "blsG2Add invalid input A")
|
|
|
|
|
|
|
|
if not b.decodePoint(input.toOpenArray(256, 511)):
|
|
|
|
raise newException(ValidationError, "blsG2Add invalid input B")
|
|
|
|
|
|
|
|
a.add b
|
|
|
|
|
|
|
|
c.output = newSeq[byte](256)
|
|
|
|
if not encodePoint(a, c.output):
|
|
|
|
raise newException(ValidationError, "blsG2Add encodePoint error")
|
|
|
|
|
|
|
|
proc blsG2Mul*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 288:
|
|
|
|
raise newException(ValidationError, "blsG2Mul invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381G2MulGas, reason="blsG2Mul Precompile")
|
|
|
|
|
|
|
|
var a: BLS_G2
|
|
|
|
if not a.decodePoint(input.toOpenArray(0, 255)):
|
|
|
|
raise newException(ValidationError, "blsG2Mul invalid input A")
|
|
|
|
|
|
|
|
var scalar: BLS_SCALAR
|
|
|
|
if not scalar.fromBytes(input.toOpenArray(256, 287)):
|
|
|
|
raise newException(ValidationError, "blsG2Mul invalid scalar")
|
|
|
|
|
|
|
|
a.mul(scalar)
|
|
|
|
|
|
|
|
c.output = newSeq[byte](256)
|
|
|
|
if not encodePoint(a, c.output):
|
|
|
|
raise newException(ValidationError, "blsG2Mul encodePoint error")
|
|
|
|
|
|
|
|
proc blsG2MultiExp*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
const L = 288
|
|
|
|
if (input.len == 0) or ((input.len mod L) != 0):
|
|
|
|
raise newException(ValidationError, "blsG2MultiExp invalid input len")
|
|
|
|
|
|
|
|
let
|
|
|
|
K = input.len div L
|
|
|
|
gas = K.calcBlsMultiExpGas(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)):
|
|
|
|
raise newException(ValidationError, "blsG2MultiExp invalid input P")
|
|
|
|
|
|
|
|
# Decode scalar value
|
|
|
|
if not s.fromBytes(input.toOpenArray(off+256, off+287)):
|
|
|
|
raise newException(ValidationError, "blsG2MultiExp invalid scalar")
|
|
|
|
|
|
|
|
p.mul(s)
|
|
|
|
if i == 0:
|
|
|
|
acc = p
|
|
|
|
else:
|
|
|
|
acc.add(p)
|
|
|
|
|
|
|
|
c.output = newSeq[byte](256)
|
|
|
|
if not encodePoint(acc, c.output):
|
|
|
|
raise newException(ValidationError, "blsG2MuliExp encodePoint error")
|
|
|
|
|
|
|
|
proc blsPairing*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
const L = 384
|
|
|
|
if (input.len == 0) or ((input.len mod L) != 0):
|
|
|
|
raise newException(ValidationError, "blsG2Pairing invalid input len")
|
|
|
|
|
|
|
|
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)):
|
|
|
|
raise newException(ValidationError, "blsG2Pairing invalid G1")
|
|
|
|
|
|
|
|
# Decode G2 point
|
|
|
|
if not g2.decodePoint(input.toOpenArray(off+128, off+383)):
|
|
|
|
raise newException(ValidationError, "blsG2Pairing invalid G2")
|
|
|
|
|
|
|
|
# 'point is on curve' check already done,
|
|
|
|
# Here we need to apply subgroup checks.
|
|
|
|
if not g1.subgroupCheck:
|
|
|
|
raise newException(ValidationError, "blsG2Pairing invalid G1 subgroup")
|
|
|
|
|
|
|
|
if not g2.subgroupCheck:
|
|
|
|
raise newException(ValidationError, "blsG2Pairing invalid G2 subgroup")
|
|
|
|
|
|
|
|
# Update pairing engine with G1 and G2 points
|
|
|
|
if i == 0:
|
|
|
|
acc = millerLoop(g1, g2)
|
|
|
|
else:
|
|
|
|
acc.mul(millerLoop(g1, g2))
|
|
|
|
|
|
|
|
c.output = newSeq[byte](32)
|
|
|
|
if acc.check():
|
|
|
|
c.output[^1] = 1.byte
|
|
|
|
|
|
|
|
proc blsMapG1*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 64:
|
|
|
|
raise newException(ValidationError, "blsMapG1 invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381MapG1Gas, reason="blsMapG1 Precompile")
|
|
|
|
|
|
|
|
var fe: BLS_FE
|
|
|
|
if not fe.decodeFE(input):
|
|
|
|
raise newException(ValidationError, "blsMapG1 invalid field element")
|
|
|
|
|
|
|
|
let p = fe.mapFPToG1()
|
|
|
|
|
|
|
|
c.output = newSeq[byte](128)
|
|
|
|
if not encodePoint(p, c.output):
|
|
|
|
raise newException(ValidationError, "blsMapG1 encodePoint error")
|
|
|
|
|
|
|
|
proc blsMapG2*(c: Computation) =
|
|
|
|
template input: untyped =
|
|
|
|
c.msg.data
|
|
|
|
|
|
|
|
if input.len != 128:
|
|
|
|
raise newException(ValidationError, "blsMapG2 invalid input len")
|
|
|
|
|
|
|
|
c.gasMeter.consumeGas(Bls12381MapG2Gas, reason="blsMapG2 Precompile")
|
|
|
|
|
|
|
|
var fe: BLS_FE2
|
|
|
|
if not fe.decodeFE(input):
|
|
|
|
raise newException(ValidationError, "blsMapG2 invalid field element")
|
|
|
|
|
|
|
|
let p = fe.mapFPToG2()
|
|
|
|
|
|
|
|
c.output = newSeq[byte](256)
|
|
|
|
if not encodePoint(p, c.output):
|
|
|
|
raise newException(ValidationError, "blsMapG2 encodePoint error")
|
|
|
|
|
2023-06-24 13:56:44 +00:00
|
|
|
proc pointEvaluation*(c: Computation) =
|
|
|
|
# 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")
|
|
|
|
|
|
|
|
let res = pointEvaluation(input)
|
|
|
|
if res.isErr:
|
|
|
|
raise newException(ValidationError, res.error)
|
|
|
|
|
|
|
|
# return a constant
|
|
|
|
c.output = @PointEvaluationResult
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
proc execPrecompiles*(c: Computation, fork: EVMFork): bool {.inline.} =
|
2021-04-08 14:52:10 +00:00
|
|
|
for i in 0..18:
|
2023-08-28 12:10:31 +00:00
|
|
|
if c.msg.codeAddress[i] != 0: return
|
2021-04-08 14:52:10 +00:00
|
|
|
|
2023-08-28 12:10:31 +00:00
|
|
|
let lb = c.msg.codeAddress[19]
|
|
|
|
if not validPrecompileAddr(lb, fork):
|
|
|
|
return
|
|
|
|
|
|
|
|
let precompile = PrecompileAddresses(lb)
|
|
|
|
try:
|
|
|
|
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)
|
|
|
|
#else: discard
|
|
|
|
# EIP 2537: disabled
|
|
|
|
# reason: not included in berlin
|
|
|
|
# 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)
|
|
|
|
except OutOfGas as e:
|
|
|
|
c.setError(EVMC_OUT_OF_GAS, e.msg, true)
|
|
|
|
except CatchableError as e:
|
|
|
|
if fork >= FkByzantium and precompile > paIdentity:
|
|
|
|
c.setError(EVMC_PRECOMPILE_FAILURE, e.msg, true)
|
|
|
|
else:
|
|
|
|
# swallow any other precompiles errors
|
|
|
|
debug "execPrecompiles validation error", msg=e.msg
|
|
|
|
true
|