modExp, support up to uint4096
This commit is contained in:
parent
949c5a1dc6
commit
6d93bdffea
|
@ -12,9 +12,9 @@ import
|
||||||
|
|
||||||
# some methods based on py-evm utils/numeric
|
# some methods based on py-evm utils/numeric
|
||||||
|
|
||||||
func log2*(value: UInt256): Natural {.inline.}=
|
func log2*[bits: static int](value: StUint[bits]): Natural {.inline.}=
|
||||||
# TODO: do we use ln for log2 like Nim convention?
|
# TODO: do we use ln for log2 like Nim convention?
|
||||||
255 - value.countLeadingZeroBits
|
(bits - 1) - value.countLeadingZeroBits
|
||||||
|
|
||||||
func log256*(value: UInt256): Natural {.inline.}=
|
func log256*(value: UInt256): Natural {.inline.}=
|
||||||
value.log2 shr 3 # div 8 (= log2(256), Logb x = Loga x/Loga b)
|
value.log2 shr 3 # div 8 (= log2(256), Logb x = Loga x/Loga b)
|
||||||
|
@ -55,7 +55,7 @@ func cleanMemRef*(x: UInt256): int {.inline.} =
|
||||||
return high(int32) shr 2
|
return high(int32) shr 2
|
||||||
return x.toInt
|
return x.toInt
|
||||||
|
|
||||||
proc rangeToPaddedUint256*(x: seq[byte], first, last: int): Uint256 =
|
proc rangeToPadded*[T: StUint](x: openarray[byte], first, last: int): T =
|
||||||
## Convert take a slice of a sequence of bytes interpret it as the big endian
|
## 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
|
## representation of an Uint256. Use padding for sequence shorter than 32 bytes
|
||||||
## including 0-length sequences.
|
## including 0-length sequences.
|
||||||
|
@ -66,7 +66,7 @@ proc rangeToPaddedUint256*(x: seq[byte], first, last: int): Uint256 =
|
||||||
if not(lo <= hi):
|
if not(lo <= hi):
|
||||||
return # 0
|
return # 0
|
||||||
|
|
||||||
result = UInt256.fromBytesBE(
|
result = T.fromBytesBE(
|
||||||
x.toOpenArray(lo, hi),
|
x.toOpenArray(lo, hi),
|
||||||
allowPadding = true
|
allowPadding = true
|
||||||
)
|
)
|
||||||
|
|
|
@ -101,25 +101,18 @@ proc identity*(computation: var BaseComputation) =
|
||||||
computation.rawOutput = computation.msg.data
|
computation.rawOutput = computation.msg.data
|
||||||
debug "Identity precompile", output = computation.rawOutput.toHex
|
debug "Identity precompile", output = computation.rawOutput.toHex
|
||||||
|
|
||||||
proc modExp*(computation: var BaseComputation) =
|
proc modExpInternal(computation: var BaseComputation, base_len, exp_len, mod_len: int, T: type StUint) =
|
||||||
## Modular exponentiation precompiled contract
|
|
||||||
# Parsing the data
|
|
||||||
template rawMsg: untyped {.dirty.} =
|
template rawMsg: untyped {.dirty.} =
|
||||||
computation.msg.data
|
computation.msg.data
|
||||||
|
|
||||||
let
|
let
|
||||||
base_len = rawMsg.rangeToPaddedUint256(0, 31).truncate(int)
|
base = rawMsg.rangeToPadded[:T](96, 95 + base_len)
|
||||||
exp_len = rawMsg.rangeToPaddedUint256(32, 63).truncate(int)
|
exp = rawMsg.rangeToPadded[:T](96 + base_len, 95 + base_len + exp_len)
|
||||||
mod_len = rawMsg.rangeToPaddedUint256(64, 95).truncate(int)
|
modulo = rawMsg.rangeToPadded[:T](96 + base_len + exp_len, 95 + base_len + exp_len + mod_len)
|
||||||
|
|
||||||
start_exp = 96 + base_len
|
|
||||||
start_mod = start_exp + exp_len
|
|
||||||
|
|
||||||
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
|
block: # Gas cost
|
||||||
func gasModExp_f(x: Natural): int =
|
func gasModExp_f(x: Natural): int =
|
||||||
|
## Estimates the difficulty of Karatsuba multiplication
|
||||||
# x: maximum length in bytes between modulo and base
|
# x: maximum length in bytes between modulo and base
|
||||||
# TODO: Deal with negative max_len
|
# TODO: Deal with negative max_len
|
||||||
result = case x
|
result = case x
|
||||||
|
@ -131,11 +124,11 @@ proc modExp*(computation: var BaseComputation) =
|
||||||
# TODO deal with negative length
|
# TODO deal with negative length
|
||||||
if exp_len <= 32:
|
if exp_len <= 32:
|
||||||
if exp.isZero(): 0
|
if exp.isZero(): 0
|
||||||
else: log2(exp)
|
else: log2(exp) # highest-bit in exponent
|
||||||
else:
|
else:
|
||||||
let extra = rawMsg.rangeToPaddedUint256(96 + base_len, 127 + base_len)
|
let first32 = rawMsg.rangeToPadded[:Uint256](96 + base_len, 95 + base_len + exp_len)
|
||||||
if not extra.isZero:
|
if not first32.isZero:
|
||||||
8 * (exp_len - 32) + extra.log2
|
8 * (exp_len - 32) + first32.log2
|
||||||
else:
|
else:
|
||||||
8 * (exp_len - 32)
|
8 * (exp_len - 32)
|
||||||
|
|
||||||
|
@ -146,27 +139,57 @@ proc modExp*(computation: var BaseComputation) =
|
||||||
) div GasQuadDivisor
|
) div GasQuadDivisor
|
||||||
|
|
||||||
block: # Processing
|
block: # Processing
|
||||||
# Start with EVM special cases
|
# TODO: specs mentions that we should return in "M" format
|
||||||
|
# i.e. if Base and exp are uint512 and Modulo an uint256
|
||||||
|
# we should return a 256-bit big-endian byte array
|
||||||
|
|
||||||
# Force static evaluation
|
# Force static evaluation
|
||||||
func zero256(): static array[32, byte] = discard
|
func zero(): static array[T.bits div 8, byte] = discard
|
||||||
func one256(): static array[32, byte] =
|
func one(): static array[T.bits div 8, byte] =
|
||||||
when cpuEndian == bigEndian:
|
when cpuEndian == bigEndian:
|
||||||
result[^1] = 1
|
result[^1] = 1
|
||||||
else:
|
else:
|
||||||
result[0] = 1
|
result[0] = 1
|
||||||
|
|
||||||
|
# Start with EVM special cases
|
||||||
if modulo <= 1:
|
if modulo <= 1:
|
||||||
# If m == 0: EVM returns 0.
|
# If m == 0: EVM returns 0.
|
||||||
# If m == 1: we can shortcut that to 0 as well
|
# If m == 1: we can shortcut that to 0 as well
|
||||||
computation.rawOutput = @(zero256())
|
computation.rawOutput = @(zero())
|
||||||
elif exp.isZero():
|
elif exp.isZero():
|
||||||
# If 0^0: EVM returns 1
|
# If 0^0: EVM returns 1
|
||||||
# For all x != 0, x^0 == 1 as well
|
# For all x != 0, x^0 == 1 as well
|
||||||
computation.rawOutput = @(one256())
|
computation.rawOutput = @(one())
|
||||||
else:
|
else:
|
||||||
computation.rawOutput = @(powmod(base, exp, modulo).toByteArrayBE)
|
computation.rawOutput = @(powmod(base, exp, modulo).toByteArrayBE)
|
||||||
|
|
||||||
|
proc modExp*(computation: var BaseComputation) =
|
||||||
|
## Modular exponentiation precompiled contract
|
||||||
|
## Yellow Paper Appendix E
|
||||||
|
## EIP-198 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md
|
||||||
|
# Parsing the data
|
||||||
|
template rawMsg: untyped {.dirty.} =
|
||||||
|
computation.msg.data
|
||||||
|
let # lengths Base, Exponent, Modulus
|
||||||
|
base_len = rawMsg.rangeToPadded[:Uint256](0, 31).truncate(int)
|
||||||
|
exp_len = rawMsg.rangeToPadded[:Uint256](32, 63).truncate(int)
|
||||||
|
mod_len = rawMsg.rangeToPadded[:Uint256](64, 95).truncate(int)
|
||||||
|
|
||||||
|
let maxBytes = max(base_len, max(exp_len, mod_len))
|
||||||
|
|
||||||
|
if maxBytes <= 32:
|
||||||
|
computation.modExpInternal(base_len, exp_len, mod_len, UInt256)
|
||||||
|
elif maxBytes <= 64:
|
||||||
|
computation.modExpInternal(base_len, exp_len, mod_len, StUint[512])
|
||||||
|
elif maxBytes <= 128:
|
||||||
|
computation.modExpInternal(base_len, exp_len, mod_len, StUint[1024])
|
||||||
|
elif maxBytes <= 256:
|
||||||
|
computation.modExpInternal(base_len, exp_len, mod_len, StUint[2048])
|
||||||
|
elif maxBytes <= 512:
|
||||||
|
computation.modExpInternal(base_len, exp_len, mod_len, StUint[4096])
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint4096")
|
||||||
|
|
||||||
proc bn256ecAdd*(computation: var BaseComputation) =
|
proc bn256ecAdd*(computation: var BaseComputation) =
|
||||||
var
|
var
|
||||||
input: array[128, byte]
|
input: array[128, byte]
|
||||||
|
|
Loading…
Reference in New Issue