mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-24 03:00:25 +00:00
commit
86853ea97c
@ -6,9 +6,9 @@ PrecompileTests
|
||||
+ bn256mul.json OK
|
||||
+ ecrecover.json OK
|
||||
+ identity.json OK
|
||||
- modexp.json Fail
|
||||
+ modexp.json OK
|
||||
+ pairing.json OK
|
||||
+ ripemd160.json OK
|
||||
+ sha256.json OK
|
||||
```
|
||||
OK: 7/8 Fail: 1/8 Skip: 0/8
|
||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||
|
@ -12,9 +12,8 @@ import
|
||||
|
||||
# some methods based on py-evm utils/numeric
|
||||
|
||||
func log2*(value: UInt256): Natural {.inline.}=
|
||||
# TODO: do we use ln for log2 like Nim convention?
|
||||
255 - value.countLeadingZeroBits
|
||||
func log2*[bits: static int](value: StUint[bits]): Natural {.inline.}=
|
||||
(bits - 1) - value.countLeadingZeroBits
|
||||
|
||||
func log256*(value: UInt256): Natural {.inline.}=
|
||||
value.log2 shr 3 # div 8 (= log2(256), Logb x = Loga x/Loga b)
|
||||
@ -55,7 +54,7 @@ func cleanMemRef*(x: UInt256): int {.inline.} =
|
||||
return high(int32) shr 2
|
||||
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
|
||||
## representation of an Uint256. Use padding for sequence shorter than 32 bytes
|
||||
## including 0-length sequences.
|
||||
@ -63,10 +62,10 @@ proc rangeToPaddedUint256*(x: seq[byte], first, last: int): Uint256 =
|
||||
let lo = max(0, first)
|
||||
let hi = min(x.high, last)
|
||||
|
||||
if not(lo < hi):
|
||||
if not(lo <= hi):
|
||||
return # 0
|
||||
|
||||
result = UInt256.fromBytesBE(
|
||||
result = T.fromBytesBE(
|
||||
x.toOpenArray(lo, hi),
|
||||
allowPadding = true
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ proc getSignature*(computation: BaseComputation): (array[32, byte], Signature) =
|
||||
let v = data[63] # TODO: Endian
|
||||
assert v.int in 27..28
|
||||
bytes[64] = v - 27
|
||||
|
||||
|
||||
if recoverSignature(bytes, result[1]) != EthKeysStatus.Success:
|
||||
raise newException(ValidationError, "Could not recover signature computation")
|
||||
|
||||
@ -68,7 +68,7 @@ proc ecRecover*(computation: var BaseComputation) =
|
||||
|
||||
if sig.recoverSignatureKey(msgHash, pubKey) != EthKeysStatus.Success:
|
||||
raise newException(ValidationError, "Could not derive public key from computation")
|
||||
|
||||
|
||||
computation.rawOutput.setLen(32)
|
||||
computation.rawOutput[12..31] = pubKey.toCanonicalAddress()
|
||||
debug "ECRecover precompile", derivedKey = pubKey.toCanonicalAddress()
|
||||
@ -101,25 +101,18 @@ proc identity*(computation: var BaseComputation) =
|
||||
computation.rawOutput = computation.msg.data
|
||||
debug "Identity precompile", output = computation.rawOutput.toHex
|
||||
|
||||
proc modExp*(computation: var BaseComputation) =
|
||||
## Modular exponentiation precompiled contract
|
||||
# Parsing the data
|
||||
proc modExpInternal(computation: var BaseComputation, base_len, exp_len, mod_len: int, T: type StUint) =
|
||||
template rawMsg: untyped {.dirty.} =
|
||||
computation.msg.data
|
||||
|
||||
let
|
||||
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 = rawMsg.rangeToPaddedUint256(96, start_exp - 1)
|
||||
exp = rawMsg.rangeToPaddedUint256(start_exp, start_mod - 1)
|
||||
modulo = rawMsg.rangeToPaddedUint256(start_mod, start_mod + mod_len - 1)
|
||||
base = rawMsg.rangeToPadded[:T](96, 95 + base_len)
|
||||
exp = rawMsg.rangeToPadded[:T](96 + base_len, 95 + base_len + exp_len)
|
||||
modulo = rawMsg.rangeToPadded[:T](96 + base_len + exp_len, 95 + base_len + exp_len + mod_len)
|
||||
|
||||
block: # Gas cost
|
||||
func gasModExp_f(x: Natural): int =
|
||||
## Estimates the difficulty of Karatsuba multiplication
|
||||
# x: maximum length in bytes between modulo and base
|
||||
# TODO: Deal with negative max_len
|
||||
result = case x
|
||||
@ -131,11 +124,11 @@ proc modExp*(computation: var BaseComputation) =
|
||||
# TODO deal with negative length
|
||||
if exp_len <= 32:
|
||||
if exp.isZero(): 0
|
||||
else: log2(exp)
|
||||
else: log2(exp) # highest-bit in exponent
|
||||
else:
|
||||
let extra = rawMsg.rangeToPaddedUint256(96 + base_len, 127 + base_len)
|
||||
if not extra.isZero:
|
||||
8 * (exp_len - 32) + extra.log2
|
||||
let first32 = rawMsg.rangeToPadded[:Uint256](96 + base_len, 95 + base_len + exp_len)
|
||||
if not first32.isZero:
|
||||
8 * (exp_len - 32) + first32.log2
|
||||
else:
|
||||
8 * (exp_len - 32)
|
||||
|
||||
@ -145,28 +138,62 @@ proc modExp*(computation: var BaseComputation) =
|
||||
max(adj_exp_len, 1)
|
||||
) div GasQuadDivisor
|
||||
|
||||
computation.gasMeter.consumeGas(gasFee, reason="ModExp Precompile")
|
||||
|
||||
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
|
||||
func zero256(): static array[32, byte] = discard
|
||||
func one256(): static array[32, byte] =
|
||||
func zero(): static array[T.bits div 8, byte] = discard
|
||||
func one(): static array[T.bits div 8, byte] =
|
||||
when cpuEndian == bigEndian:
|
||||
result[^1] = 1
|
||||
else:
|
||||
result[0] = 1
|
||||
|
||||
# Start with EVM special cases
|
||||
if modulo <= 1:
|
||||
# If m == 0: EVM returns 0.
|
||||
# If m == 1: we can shortcut that to 0 as well
|
||||
computation.rawOutput = @(zero256())
|
||||
computation.rawOutput = @(zero())
|
||||
elif exp.isZero():
|
||||
# If 0^0: EVM returns 1
|
||||
# For all x != 0, x^0 == 1 as well
|
||||
computation.rawOutput = @(one256())
|
||||
computation.rawOutput = @(one())
|
||||
else:
|
||||
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])
|
||||
elif maxBytes <= 1024:
|
||||
computation.modExpInternal(base_len, exp_len, mod_len, StUint[8192])
|
||||
else:
|
||||
raise newException(ValueError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint8192")
|
||||
|
||||
proc bn256ecAdd*(computation: var BaseComputation) =
|
||||
var
|
||||
input: array[128, byte]
|
||||
|
@ -14,4 +14,5 @@ import ./test_code_stream,
|
||||
./test_caching_db_backend,
|
||||
./test_genesis,
|
||||
./test_vm_json,
|
||||
./test_precompiles,
|
||||
./test_generalstate_json
|
||||
|
2
tests/fixtures/PrecompileTests/modexp.json
vendored
2
tests/fixtures/PrecompileTests/modexp.json
vendored
@ -87,4 +87,4 @@
|
||||
"expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user