modExp precompiles: fix padding and static evaluation

This commit is contained in:
mratsim 2018-10-10 16:26:21 +02:00
parent 0494e2a877
commit bb6e1e1d76
2 changed files with 36 additions and 11 deletions

View File

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

View File

@ -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