Skip to content

testing branch for fusaka-devnet-2 #3388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 17 commits into from
Closed
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
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
4 changes: 4 additions & 0 deletions execution_chain/constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,8 @@ const
HISTORY_STORAGE_ADDRESS* = address"0x0000F90827F1C53a10cb7A02335B175320002935"
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS* = address"0x00000961Ef480Eb55e80D19ad83579A64c007002"
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS* = address"0x0000BBdDc7CE488642fb579F8B00f3a590007251"

MAX_BLOCK_SIZE* = 10_485_760 # 10 MiB
SAFETY_MARGIN* = 2_097_152 # 2 MiB
MAX_RLP_BLOCK_SIZE* = MAX_BLOCK_SIZE - SAFETY_MARGIN
# End
5 changes: 5 additions & 0 deletions execution_chain/core/executor/process_block.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import
../../common/common,
../../constants,
../../utils/utils,
../../constants,
../../db/ledger,
Expand Down Expand Up @@ -118,6 +119,10 @@ proc procBlkPreamble(
if blk.transactions.calcTxRoot != header.txRoot:
return err("Mismatched txRoot")

if com.isOsakaOrLater(header.timestamp):
if rlp.getEncodedLength(blk) > MAX_RLP_BLOCK_SIZE:
return err("Post-Osaka block exceeded MAX_RLP_BLOCK_SIZE")

if com.isPragueOrLater(header.timestamp):
if header.requestsHash.isNone:
return err("Post-Prague block header must have requestsHash")
Expand Down
4 changes: 4 additions & 0 deletions execution_chain/core/tx_pool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,13 @@ proc assembleBlock*(
blobsBundle = BlobsBundle(
wrapperVersion: getWrapperVersion(com, blk.header.timestamp)
)
currentRlpSize = rlp.getEncodedLength(blk.header) + rlp.getEncodedLength(blk.withdrawals)

for item in pst.packedTxs:
let tx = item.pooledTx
if currentRlpSize > MAX_RLP_BLOCK_SIZE - 10:
break
currentRlpSize = currentRlpSize + rlp.getEncodedLength(tx.tx)
blk.txs.add tx.tx
if tx.blobsBundle != nil:
doAssert(tx.blobsBundle.wrapperVersion == blobsBundle.wrapperVersion)
Expand Down
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
175 changes: 133 additions & 42 deletions execution_chain/evm/precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,115 @@ 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

func validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
(addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)
elif fork < FkOsaka: paBlsMapG2
else: Precompiles.high

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

proc p256verify(c: Computation): EvmResultVoid =

template failed() =
c.output.setLen(0)
return ok()

? c.gasMeter.consumeGas(GasP256VerifyGas, reason="P256VERIFY Precompile")

if c.msg.data.len != 160:
failed()

var inputPubKey: array[65, byte]

# Validations
if isInfinityByte(c.msg.data.toOpenArray(96, 159)):
failed()

# Check scalar and field bounds (r, s ∈ (0, n), qx, qy ∈ [0, p))
var sig: EcSignature
if not sig.initRaw(c.msg.data.toOpenArray(32, 95)):
failed()

var pubkey: EcPublicKey
inputPubKey[0] = 4.byte
assign(inputPubKey.toOpenArray(1, 64), c.msg.data.toOpenArray(96, 159))

if not pubkey.initRaw(inputPubKey):
failed()

let isValid = sig.verifyRaw(c.msg.data.toOpenArray(0, 31), 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] =
let maxPrecompile = getMaxPrecompile(fork)
for c in Precompiles.low..maxPrecompile:
if precompileAddrs[c] == codeAddress:
return Opt.some(c)

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 +833,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
87 changes: 87 additions & 0 deletions execution_chain/evm/secp256r1verify.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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 `-`(x: uint32): uint32 {.inline.} =
result = (0xFFFF_FFFF'u32 - x) + 1'u32

proc GT(x, y: uint32): uint32 {.inline.} =
var z = cast[uint32](y - x)
result = (z xor ((x xor y) and (x xor z))) shr 31

proc CMP(x, y: uint32): int32 {.inline.} =
cast[int32](GT(x, y)) or -(cast[int32](GT(y, x)))

proc EQ0(x: int32): uint32 {.inline.} =
var q = cast[uint32](x)
result = not (q or -q) shr 31

proc NEQ(x, y: uint32): uint32 {.inline.} =
var q = cast[uint32](x xor y)
result = ((q or -q) shr 31)

proc LT0(x: int32): uint32 {.inline.} =
result = cast[uint32](x) shr 31

proc checkScalar*(scalar: openArray[byte], curve: cint): uint32 =
## Return ``1`` if all of the following hold:
## - len(``scalar``) <= ``orderlen``
## - ``scalar`` != 0
## - ``scalar`` is lower than the curve ``order``.
##
## Otherwise, return ``0``.
var impl = ecGetDefault()
var orderlen: uint = 0
var order = cast[ptr UncheckedArray[byte]](impl.order(curve, orderlen))

var z = 0'u32
var c = 0'i32
for u in scalar:
z = z or u
if len(scalar) == int(orderlen):
for i in 0 ..< len(scalar):
c = c or (-(cast[int32](EQ0(c))) and CMP(scalar[i], order[i]))
else:
c = -1
result = NEQ(z, 0'u32) and LT0(c)

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

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)
Loading
Loading