Skip to content
5 changes: 3 additions & 2 deletions PrecompileTests.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ PrecompileTests
===
## PrecompileTests
```diff
+ P256Verify.json OK
+ blake2F.json OK
+ blsG1Add.json OK
+ blsG1MultiExp.json OK
Expand All @@ -24,7 +25,7 @@ PrecompileTests
+ ripemd160.json OK
+ sha256.json OK
```
OK: 21/21 Fail: 0/21 Skip: 0/21
OK: 22/22 Fail: 0/22 Skip: 0/22
## eest
```diff
+ add_G1_bls.json OK
Expand All @@ -49,4 +50,4 @@ OK: 21/21 Fail: 0/21 Skip: 0/21
OK: 18/18 Fail: 0/18 Skip: 0/18

---TOTAL---
OK: 39/39 Fail: 0/39 Skip: 0/39
OK: 40/40 Fail: 0/40 Skip: 0/40
1 change: 1 addition & 0 deletions execution_chain/evm/interpreter/gas_costs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -858,3 +858,4 @@ const
Bls12381PairingPerPairGas* = GasInt 32600
Bls12381MapG1Gas* = GasInt 5500
Bls12381MapG2Gas* = GasInt 23800
GasP256VerifyGas* = GasInt 3450
172 changes: 132 additions & 40 deletions execution_chain/evm/precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,118 @@ import
chronicles,
nimcrypto/[ripemd, sha2, utils],
bncurve/[fields, groups],
stew/assign2,
stew/[assign2, endians2, byteutils],
libp2p/crypto/ecnist,
../common/evmforks,
../core/eip4844,
./modexp,
./evm_errors,
./computation,
./secp256r1verify,
eth/common/[base, addresses]

type
PrecompileAddresses* = enum
Precompiles* = enum
# Frontier to Spurious Dragron
paEcRecover = 0x01
paSha256 = 0x02
paRipeMd160 = 0x03
paIdentity = 0x04
paEcRecover
paSha256
paRipeMd160
paIdentity
# Byzantium and Constantinople
paModExp = 0x05
paEcAdd = 0x06
paEcMul = 0x07
paPairing = 0x08
paModExp
paEcAdd
paEcMul
paPairing
# Istanbul
paBlake2bf = 0x09
paBlake2bf
# Cancun
paPointEvaluation = 0x0a
paPointEvaluation
# Prague (EIP-2537)
paBlsG1Add = 0x0b
paBlsG1MultiExp = 0x0c
paBlsG2Add = 0x0d
paBlsG2MultiExp = 0x0e
paBlsPairing = 0x0f
paBlsMapG1 = 0x10
paBlsMapG2 = 0x11
paBlsG1Add
paBlsG1MultiExp
paBlsG2Add
paBlsG2MultiExp
paBlsPairing
paBlsMapG1
paBlsMapG2
# Osaka
paP256Verify

SigRes = object
msgHash: array[32, byte]
sig: Signature

const
# Frontier to Spurious Dragron
paEcRecoverAddress = Address.fromHex("0x0000000000000000000000000000000000000001")
paSha256Address = Address.fromHex("0x0000000000000000000000000000000000000002")
paRipeMd160Address = Address.fromHex("0x0000000000000000000000000000000000000003")
paIdentityAddress = Address.fromHex("0x0000000000000000000000000000000000000004")
# Byzantium and Constantinople
paModExpAddress = Address.fromHex("0x0000000000000000000000000000000000000005")
paEcAddAddress = Address.fromHex("0x0000000000000000000000000000000000000006")
paEcMulAddress = Address.fromHex("0x0000000000000000000000000000000000000007")
paPairingAddress = Address.fromHex("0x0000000000000000000000000000000000000008")
# Istanbul
paBlake2bfAddress = Address.fromHex("0x0000000000000000000000000000000000000009")
# Cancun
paPointEvaluationAddress = Address.fromHex("0x000000000000000000000000000000000000000a")
# Prague (EIP-2537)
paBlsG1AddAddress = Address.fromHex("0x000000000000000000000000000000000000000b")
paBlsG1MultiExpAddress = Address.fromHex("0x000000000000000000000000000000000000000c")
paBlsG2AddAddress = Address.fromHex("0x000000000000000000000000000000000000000d")
paBlsG2MultiExpAddress = Address.fromHex("0x000000000000000000000000000000000000000e")
paBlsPairingAddress = Address.fromHex("0x000000000000000000000000000000000000000f")
paBlsMapG1Address = Address.fromHex("0x0000000000000000000000000000000000000010")
paBlsMapG2Address = Address.fromHex("0x0000000000000000000000000000000000000011")
# Osaka
paP256VerifyAddress = Address.fromHex("0x0000000000000000000000000000000000000100")

precompileAddrs*: array[Precompiles, Address] = [
# Frontier to Spurious Dragon
paEcRecoverAddress, # paEcRecover
paSha256Address, # paSha256
paRipeMd160Address, # paRipeMd160
paIdentityAddress, # paIdentity

# Byzantium and Constantinople
paModExpAddress, # paModExp
paEcAddAddress, # paEcAdd
paEcMulAddress, # paEcMul
paPairingAddress, # paPairing

# Istanbul
paBlake2bfAddress, # paBlake2bf

# Cancun
paPointEvaluationAddress, # paPointEvaluation

# Prague (EIP-2537)
paBlsG1AddAddress, # paBlsG1Add
paBlsG1MultiExpAddress, # paBlsG1MultiExp
paBlsG2AddAddress, # paBlsG2Add
paBlsG2MultiExpAddress, # paBlsG2MultiExp
paBlsPairingAddress, # paBlsPairing
paBlsMapG1Address, # paBlsMapG1
paBlsMapG2Address, # paBlsMapG2
paP256VerifyAddress # paP256Verify
]


# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------

func getMaxPrecompileAddr(fork: EVMFork): PrecompileAddresses =
func getMaxPrecompile(fork: EVMFork): Precompiles =
if fork < FkByzantium: paIdentity
elif fork < FkIstanbul: paPairing
elif fork < FkCancun: paBlake2bf
elif fork < FkPrague: paPointEvaluation
else: PrecompileAddresses.high
elif fork < FkOsaka: paBlsMapG2
else: Precompiles.high

func validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
(addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)
# func validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
# (addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)

func getSignature(c: Computation): EvmResult[SigRes] =
# input is Hash, V, R, S
Expand Down Expand Up @@ -694,36 +756,65 @@ proc pointEvaluation(c: Computation): EvmResultVoid =
c.output = @PointEvaluationResult
ok()

proc p256verify(c: Computation): EvmResultVoid =
? c.gasMeter.consumeGas(GasP256VerifyGas, reason="P256VERIFY Precompile")

let input = c.msg.data
if input.len != 160:
return err(prcErr(PrcInvalidParam))

var
h = input[0 ..< 32]
r = input[32 ..< 64]
s = input[64 ..< 96]
qx = input[96 ..< 128]
qy = input[128 ..< 160]

if qx.isInfinityByte() and qy.isInfinityByte():
# If the public key is infinity, the signature is invalid
return err(prcErr(PrcInvalidParam))

# Check scalar and field bounds (r, s ∈ (0, n), qx, qy ∈ [0, p))
var sig: EcSignature
if not sig.initRaw(@(r & s)):
return err(prcErr(PrcInvalidParam))

var pubkey: EcPublicKey
if not pubkey.initRaw(@(0x04'u8 & qx & qy)):
return err(prcErr(PrcInvalidParam))

let isValid = sig.verifyRaw(h, pubkey)

if isValid:
c.output.setLen(32)
c.output[^1] = 1.byte # return 0x...01
else:
c.output.setLen(0)

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
let maxPrecompile = getMaxPrecompile(fork)
for c in Precompiles.low..maxPrecompile:
yield precompileAddrs[c]

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[Precompiles] =
for precompile, addr in precompileAddrs:
if addr == codeAddress:
return Opt.some(precompile)

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])
Opt.none(Precompiles)

proc execPrecompile*(c: Computation, precompile: PrecompileAddresses) =
proc execPrecompile*(c: Computation, precompile: Precompiles) =
let fork = c.fork
let res = case precompile
of paEcRecover: ecRecover(c)
Expand All @@ -743,6 +834,7 @@ proc execPrecompile*(c: Computation, precompile: PrecompileAddresses) =
of paBlsPairing: blsPairing(c)
of paBlsMapG1: blsMapG1(c)
of paBlsMapG2: blsMapG2(c)
of paP256Verify: p256verify(c)

if res.isErr:
if res.error.code == EvmErrorCode.OutOfGas:
Expand Down
44 changes: 44 additions & 0 deletions execution_chain/evm/secp256r1verify.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Nimbus
# Copyright (c) 2018-2025 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
libp2p/crypto/ecnist,
bearssl/[ec, hash]

proc verifyRaw*[T: byte | char](
sig: EcSignature, message: openArray[T], pubkey: ecnist.EcPublicKey
): bool {.inline.} =
## Verify ECDSA signature ``sig`` using public key ``pubkey`` and data
## ``message``.
##
## Return ``true`` if message verification succeeded, ``false`` if
## verification failed.
doAssert((not isNil(sig)) and (not isNil(pubkey)))
var hc: HashCompatContext
var hash: array[32, byte]
var impl = ecGetDefault()
if pubkey.key.curve in EcSupportedCurvesCint:
let res = ecdsaI31VrfyRaw(
impl,
addr message[0],
uint(len(message)),
unsafeAddr pubkey.key,
addr sig.buffer[0],
uint(len(sig.buffer)),
)
# Clear context with initial value
result = (res == 1)

proc isInfinityByte*(data: openArray[byte]): bool =
## Check if all values in ``data`` are zero.
for b in data:
if b != 0:
return false
return true
Loading
Loading