Skip to content

Commit

Permalink
update bench for modexp (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
mratsim authored Sep 6, 2023
1 parent c85ffb0 commit b645d68
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 11 deletions.
215 changes: 205 additions & 10 deletions benchmarks/bench_evm_modexp_dos.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import
../constantine/ethereum_evm_precompiles,
./platforms, ./bench_blueprint,

../constantine/serialization/codecs
../constantine/math/arithmetic,
../constantine/math/io/io_bigints,
../constantine/platforms/abstractions,
./platforms, ./bench_blueprint

proc report(op: string, elapsedNs: int64, elapsedCycles: int64, iters: int) =
let ns = elapsedNs div iters
Expand All @@ -24,11 +25,88 @@ template bench(fnCall: untyped, ticks, ns: var int64): untyped =
ticks += stopClock - startClock
ns += inNanoseconds(stopTime-startTime)

proc main() =
func computeGasFee(inputs: openArray[byte]): tuple[eip128, eip2565: int] =
## Note: This is an approximation
# For Denial-of-Service fuzzing we want to ensure that
# 30M gas blocks are processed in a few milliseconds at most
# https://eips.ethereum.org/EIPS/eip-198
# https://eips.ethereum.org/EIPS/eip-2565
func mulComplexityEIP198(x: int): int =
## Estimates the difficulty of Karatsuba multiplication
if x <= 64: x * x
elif x <= 1024: (x * x) div 4 + 96*x - 3072
else: (x * x) div 16 + 480*x - 199680

func mulComplexityEIP2565(x: int): int =
result = (x+7) div 8
result *= result

func getMSB_bigEndian(a: openArray[byte]): int =
## Returns the position of the most significant bit
## of `a`.
## Returns 0 if a == 0
##
## a is stored in bigEndian representation
result = 0
for i in 0 ..< a.len:
if a[i] != byte(0):
return int(log2_vartime(uint32 a[i])) + 8*(a.len-1-i)

func iterCount(exponent: openArray[byte]): int =
let msbFirst32Bits = exponent.toOpenArray(0, min(exponent.len, 32) - 1).getMSB_bigEndian()

if exponent.len <= 32:
result = msbFirst32Bits
else:
result = (8*(exponent.len-32)) + msbFirst32Bits
if result < 1:
result = 1

func gasCostEIP198(baseLen, modLen: int, exponent: openArray[byte]): int =
const Gquaddivisor = 20
let mulComplexity = mulComplexityEIP198(max(baseLen, modLen))
let adjExpLen = iterCount(exponent)

return (mulComplexity * max(adjExpLen, 1)) div Gquaddivisor

func gasCostEIP2565(baseLen, modLen: int, exponent: openArray[byte]): int =
const Gquaddivisor = 3
let mulComplexity = mulComplexityEIP2565(max(baseLen, modLen))
let iterCount = iterCount(exponent)

return max(200, (mulComplexity * iterCount) div Gquaddivisor)

# Input parse sizes
# -----------------
let
bL = BigInt[256].unmarshal(inputs.toOpenArray(0, 31), bigEndian)
eL = BigInt[256].unmarshal(inputs.toOpenArray(32, 63), bigEndian)
mL = BigInt[256].unmarshal(inputs.toOpenArray(64, 95), bigEndian)

baseByteLen = cast[int](bL.limbs[0])
exponentByteLen = cast[int](eL.limbs[0])
modulusByteLen = cast[int](mL.limbs[0])

baseStart = 96
baseStop = baseStart+baseByteLen-1
expStart = baseStop+1
expStop = expStart+exponentByteLen-1
modStart = expStop+1
modStop = modStart+modulusByteLen-1

template exponent(): untyped =
inputs.toOpenArray(expStart, expStop)

let gasFeeEIP198 = gasCostEIP198(baseByteLen, modulusByteLen, exponent)
let gasFeeEIP2565 = gasCostEIP2565(baseByteLen, modulusByteLen, exponent)

return (gasFeeEIP198, gasFeeEIP2565)

proc dos1() =

let input = [
# Length of base (32)
(uint8)0x00,
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,

Expand Down Expand Up @@ -56,14 +134,131 @@ proc main() =
var r = newSeq[byte](32)
var ticks, nanoseconds: int64

const Iters = 22058
let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< Iters:
for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp", nanoseconds, ticks, Iters)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms"
report("EVM Modexp - 32,32,32", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos2() =

let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,

# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,

# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,

# Base
0x33,

# Exponent
0x10,

# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]

var r = newSeq[byte](121)
var ticks, nanoseconds: int64

let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos3() =

let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,

# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,

# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,

# Base
0x33,

# Exponent
0x07,

# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]

var r = newSeq[byte](121)
var ticks, nanoseconds: int64

let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

main()
dos1()
echo "\n"
dos2()
echo "\n"
dos3()
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func getMSB_vartime*[T](a: openArray[T]): int =
## Returns -1 if a == 0
result = -1
for i in countdown(a.len-1, 0):
if bool a[i] != T(0):
if bool(a[i] != T(0)):
return int(log2_vartime(uint64 a[i])) + 8*sizeof(T)*i

func getBits_vartime*[T](a: openArray[T]): int {.inline.} =
Expand Down

0 comments on commit b645d68

Please sign in to comment.