Problem
We have 4 lookup tables to use in the keccak256 gadget. Listing their Python pseudocode below.
from_binary_converter_table
Purpose: Convert 64 bits value from binary to base 13 in 16 bits chunks
>>> import itertools
>>> list(itertools.product([1, 2, 3], [4, 5, 6]))
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
This table has 2**16 rows
class FromBinaryConverterTable:
def __init__(self):
axies = [[0, 1] for i in range(16)]
for coefs in itertools.product(*axies):
assert len(coefs) == 16
# key is the binary evaluation of coefs
key = sum([coef*(2**i) for i, coef in enumerate(coefs)])
value0 = sum([coef*(13**i) for i, coef in enumerate(coefs)])
value1 = sum([coef*(9**i) for i, coef in enumerate(coefs)])
self.add_row(key, value0, value1)
first_to_second_base_converter_table
Purpose: convert the middle segments, where the chunk size is 4
This table has 13**4 rows
def block_counting_function(n: int) -> int:
table = [0, 0, 1, 13, 170]
return table[n]
class FirstToSecondBaseConverterTable:
def __init__(self):
axies = [list(range(13)) for i in range(4)]
for coefs in itertools.product(*axies):
assert len(coefs) == 4
# x0, x1, x2, x3 are 0~12
x0, x1, x2, x3 = coefs
key = x0 + x1 * 13 + x2 *13**2 + x3*13**3
fx0 = keccak_u64_first_converter(x0)
fx1 = keccak_u64_first_converter(x1)
fx2 = keccak_u64_first_converter(x2)
fx3 = keccak_u64_first_converter(x3)
# fx0, fx1, fx2, fx3 are 0~1
value = fx0 + fx1 * 9 + fx2 *9**2 + fx3*9**3
non_zero_chunk_count = 4 - len([i for i in coefs if i==0])
block_count = block_counting_function(non_zero_chunk_count)
self.add_row(key, block_count, value)
def keccak_u64_first_converter(n: int) -> int:
"""
n is the sum of 12 different bits.
The theta step has 12 xor operations
If the sum is odd, then the 12 xor operations result 1
If the sum is even, then the 12 xor operations result 0
"""
assert n < 13
return n & 1
def keccak_u64_second_converter(n: int) -> int:
"""
n is the output of 2a + b + 3c + 2d, where a, b, c, d are bits
every possible n can be uniquely mapped to the output of a ^ (~b & c) ^ d
bit_table is the output of a ^ (~b & c) ^ d
"""
assert n < 9
bit_table = [0, 0, 1, 1, 0, 0, 1, 1, 0]
return bit_table[n]
of_first_to_second_base_converter_table
Purpose: convert the last segment, where the chunk size is less than 4
The table size should be (13 + 1) * 13 / 2 = 91 rows
class OfFirstToSecondBaseConverterTable:
def __init__(self):
for i in range(13):
for j in range(13 - i):
low = i
high = j
key = low + high * 13**64
value = keccak_u64_first_converter(low + high)
self.add_row(key, value)
from_second_base_converter_table
Purpose: Convert from base 9 to base 13 or binary
This table has 9**5 rows
class FromSecondBaseConverterTable:
def __init__(self):
axies = [list(range(9)) for i in range(5)]
for coefs in itertools.product(*axies):
assert len(coefs) == 5
# key is the binary evaluation of coefs
key = sum([coef*(9**i) for i, coef in enumerate(coefs)])
value0 = sum([coef*(13**i) for i, coef in enumerate(coefs)])
value1 = sum([coef*(2**i) for i, coef in enumerate(coefs)])
self.add_row(key, value0, value1)
Solution
Build the 4 tables
Note
- See this note for other details
- Could create an abstraction MultiBaseNormalizationTable