From bb6e1e1d760b4d4cd22033f2201ed96fc5016e9e Mon Sep 17 00:00:00 2001 From: mratsim Date: Wed, 10 Oct 2018 16:26:21 +0200 Subject: [PATCH] modExp precompiles: fix padding and static evaluation --- nimbus/vm/interpreter/utils/utils_numeric.nim | 16 ++++++++++ nimbus/vm/precompiles.nim | 31 ++++++++++++------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/nimbus/vm/interpreter/utils/utils_numeric.nim b/nimbus/vm/interpreter/utils/utils_numeric.nim index 673fcdd9b..4274bf861 100644 --- a/nimbus/vm/interpreter/utils/utils_numeric.nim +++ b/nimbus/vm/interpreter/utils/utils_numeric.nim @@ -54,3 +54,19 @@ func cleanMemRef*(x: UInt256): int {.inline.} = if x > upperBound: return high(int32) shr 2 return x.toInt + +proc rangeToPaddedUint256*(x: seq[byte], first, last: int): Uint256 = + ## Convert take a slice of a sequence of bytes interpret it as the big endian + ## representation of an Uint256. Use padding for sequence shorter than 32 bytes + ## including 0-length sequences. + + let lo = max(0, first) + let hi = min(x.high, last) + + if not(lo < hi): + return # 0 + + result = UInt256.fromBytesBE( + x.toOpenArray(lo, hi), + allowPadding = true + ) diff --git a/nimbus/vm/precompiles.nim b/nimbus/vm/precompiles.nim index 0885aedd7..09720bb94 100644 --- a/nimbus/vm/precompiles.nim +++ b/nimbus/vm/precompiles.nim @@ -91,21 +91,21 @@ proc identity*(computation: var BaseComputation) = debug "Identity precompile", output = computation.rawOutput proc modExp(computation: var BaseComputation) = - + ## Modular exponentiation precompiled contract # Parsing the data template rawMsg: untyped {.dirty.} = computation.msg.data let - base_len = toInt Uint256.fromBytesBE(rawMsg.toOpenArray(0, 31), allowPadding = true) - exp_len = toInt Uint256.fromBytesBE(rawMsg.toOpenArray(32, 63), allowPadding = true) - mod_len = toInt Uint256.fromBytesBE(rawMsg.toOpenArray(64, 96), allowPadding = true) + base_len = rawMsg.rangeToPaddedUint256(0, 31).truncate(int) + exp_len = rawMsg.rangeToPaddedUint256(32, 63).truncate(int) + mod_len = rawMsg.rangeToPaddedUint256(64, 95).truncate(int) start_exp = 96 + base_len start_mod = start_exp + exp_len - base = Uint256.fromBytesBE(rawMsg.toOpenArray(96, start_exp - 1), allowPadding = true) - exp = Uint256.fromBytesBE(rawMsg.toOpenArray(start_exp, start_mod - 1), allowPadding = true) - modulo = Uint256.fromBytesBE(rawMsg.toOpenArray(start_mod, start_mod + mod_len - 1), allowPadding = true) + base = rawMsg.rangeToPaddedUint256(96, start_exp - 1) + exp = rawMsg.rangeToPaddedUint256(start_exp, start_mod - 1) + modulo = rawMsg.rangeToPaddedUint256(start_mod, start_mod + mod_len - 1) block: # Gas cost func gasModExp_f(x: Natural): int = @@ -122,7 +122,7 @@ proc modExp(computation: var BaseComputation) = if exp.isZero(): 0 else: log2(exp) else: - let extra = Uint256.fromBytesBE(rawMsg.toOpenArray(96 + base_len, 127 + base_len), allowPadding = true) + let extra = rawMsg.rangeToPaddedUint256(96 + base_len, 127 + base_len) if not extra.isZero: 8 * (exp_len - 32) + extra.log2 else: @@ -136,16 +136,25 @@ proc modExp(computation: var BaseComputation) = block: # Processing # Start with EVM special cases + + # Force static evaluation + func zero256(): static array[32, byte] = discard + func one256(): static array[32, byte] = + when cpuEndian == bigEndian: + result[^1] = 1 + else: + result[0] = 1 + if modulo <= 1: # If m == 0: EVM returns 0. # If m == 1: we can shortcut that to 0 as well - computation.rawOutput = @(static(zero(UInt256).toByteArrayBE)) + computation.rawOutput = @(zero256()) elif exp.isZero(): # If 0^0: EVM returns 1 # For all x != 0, x^0 == 1 as well - computation.rawOutput = @(static(one(UInt256).toByteArrayBE)) + computation.rawOutput = @(one256()) else: - computation.rawOutput = @powmod(base, exp, modulo).toByteArrayBE + computation.rawOutput = @(powmod(base, exp, modulo).toByteArrayBE) proc bn256ecAdd*(computation: var BaseComputation) = var