From b645d68e1aad409a4c14f621caf2ad18870f854e Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Wed, 6 Sep 2023 17:18:07 +0200 Subject: [PATCH] update bench for modexp (#265) --- benchmarks/bench_evm_modexp_dos.nim | 215 +++++++++++++++++- .../arithmetic/limbs_views.nim | 2 +- 2 files changed, 206 insertions(+), 11 deletions(-) diff --git a/benchmarks/bench_evm_modexp_dos.nim b/benchmarks/bench_evm_modexp_dos.nim index a4bbb476..80dac717 100644 --- a/benchmarks/bench_evm_modexp_dos.nim +++ b/benchmarks/bench_evm_modexp_dos.nim @@ -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 @@ -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, @@ -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() \ No newline at end of file +dos1() +echo "\n" +dos2() +echo "\n" +dos3() \ No newline at end of file diff --git a/constantine/math_arbitrary_precision/arithmetic/limbs_views.nim b/constantine/math_arbitrary_precision/arithmetic/limbs_views.nim index bfe13075..f8c8f3a0 100644 --- a/constantine/math_arbitrary_precision/arithmetic/limbs_views.nim +++ b/constantine/math_arbitrary_precision/arithmetic/limbs_views.nim @@ -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.} =