Also includes an initial re-implementation of Montgomery form operations (for affine points) based on the rust zcash code, however this requires field inversions so isn't very performant - the idea is to combine wNAF with projective montgomery coordinates to see if the number of operations can be reduced even further.
If projective montgomery coordinates don't work very well in practice this will still provide a general speedup which can be implemented in the Solidity code to reduce gas cost.
We can benchmark the number of FQ operations with different windows:
>>> for i in list(range(2,8)):
... print("Window size =", i)
... FQ._reset_counts()
... mult_naf_lut(Point.generator().as_etec(), x, width=i)
... FQ._print_counts()
Window size = 2
add = 731
mul = 3340
sub = 1306
Window size = 3
add = 687
mul = 3184
sub = 1254
Window size = 4
add = 676
mul = 3181
sub = 1266
Window size = 5
add = 746
mul = 3515
sub = 1414
Window size = 6
add = 993
mul = 4634
sub = 1882
Window size = 7
add = 1601
mul = 7370
sub = 3010
Compared to affine multiplication (with inversion):
add = 712
inv = 712
mul = 4628
sub = 712
Compared to ETEC multiplication (without inversion):
add = 819
mul = 3660
sub = 1420
Compared to NAF multiplication (without a window, just simple negation):
add = 729
mul = 3330
sub = 1378
This shows the most efficient window is 4-bits wide, which provides a ~14% reduction in the number of field operations. However, is the cost of calculating the wNAF form in Solidity/EVM low enough to justify the extra complexity, and how much will the overall gas reduction be?
Benchmarking these different methods in Python, where results are in seconds per operation, with variables:
We can see a lookup table of 5 is the most efficient overall (in wall-clock time), and ETEC points in combination with a 5-bit w-NAF is the most efficient variant - being about 25% faster than affine coordinates without the wNAF method.
However, the trade-off between simplicity and speed between NAF and wNAF is very small, meaning wNAF only becomes faster than NAF when the windows size is ~4 or 5 bits... Otherwise NAF without the window is simpler and generally faster.
Also includes an initial re-implementation of Montgomery form operations (for affine points) based on the rust zcash code, however this requires field inversions so isn't very performant - the idea is to combine wNAF with projective montgomery coordinates to see if the number of operations can be reduced even further.
If projective montgomery coordinates don't work very well in practice this will still provide a general speedup which can be implemented in the Solidity code to reduce gas cost.
We can benchmark the number of FQ operations with different windows:
Compared to affine multiplication (with inversion):
Compared to ETEC multiplication (without inversion):
Compared to NAF multiplication (without a window, just simple negation):
This shows the most efficient window is 4-bits wide, which provides a ~14% reduction in the number of field operations. However, is the cost of calculating the wNAF form in Solidity/EVM low enough to justify the extra complexity, and how much will the overall gas reduction be?
Benchmarking these different methods in Python, where results are in seconds per operation, with variables:
a
= Affineb
= Projectivec
= extended projectived
= Montgomery affineWe can see a lookup table of 5 is the most efficient overall (in wall-clock time), and ETEC points in combination with a 5-bit w-NAF is the most efficient variant - being about 25% faster than affine coordinates without the wNAF method.
However, the trade-off between simplicity and speed between NAF and wNAF is very small, meaning wNAF only becomes faster than NAF when the windows size is ~4 or 5 bits... Otherwise NAF without the window is simpler and generally faster.