zkevm_circuits 0.153.12

ZKsync Era circuits for EraVM
#! File for validating field extension arithmetic
import json

# --- Fq2 tests ---

# Defining the base prime field
q = 21888242871839275222246405745257275088696311157297823662689037894645226208583 # EC group order
Fq = GF(q) 

# Defining the extensions
# Fq2...
K2.<x> = PolynomialRing(Fq)
Fq2.<u> = Fq.extension(x^2+1)

# Fq6...
K6.<y> = PolynomialRing(Fq2)
Fq6.<v> = Fq2.extension(y^3 - (u+9))

# Defining the Fq12 is a bit more tricky...
p = Fq.characteristic()
Fq12.<G> = GF(p^12)

i = sqrt(Fq12(-1))
R12.<Y> = PolynomialRing(Fq12)

j = (Y^3 - (i+9)).roots(multiplicities=False)[0]
w = sqrt(j)

P = w.minpoly()
Fq12.<W> = GF(p^12, modulus=P)

# Preparing helper debugging lambda functions
fq2_to_dictionary = lambda f : {
    'c0': str(f[0]), 
    'c1': str(f[1])
}
fq6_to_dictionary = lambda f : {
    'c0': {
        'c0': str(f[0][0]), 
        'c1': str(f[0][1])
    }, 
    'c1': {
        'c0': str(f[1][0]), 
        'c1': str(f[1][1])
    },
    'c2': {
        'c0': str(f[2][0]), 
        'c1': str(f[2][1])
    }
}
fq12_to_dictionary = lambda f: {
    'c0': { # Fq6
        'c0': { #Fq2
            'c0': str(f[0]+9*f[6]),
            'c1': str(f[6]),
        },
        'c1': { #Fq2
            'c0': str(f[2]+9*f[8]),
            'c1': str(f[8]),
        },
        'c2': { #Fq2
            'c0': str(f[4]+9*f[10]),
            'c1': str(f[10]),
        }
    }, 
    'c1': { # Fq6
        'c0': { #Fq2
            'c0': str(f[1]+9*f[7]),
            'c1': str(f[7]),
        },
        'c1': { #Fq2
            'c0': str(f[3]+9*f[9]),
            'c1': str(f[9]),
        },
        'c2': { #Fq2
            'c0': str(f[5]+9*f[11]),
            'c1': str(f[11]),
        }
    }
}

# Generating Fq2 tests
print('Preparing the Fq2 tests...')
tests_dict = {'tests': []}

FQ2_TESTS_NUMBER = 5

for _ in range(FQ2_TESTS_NUMBER):
    f = Fq2.random_element()
    g = Fq2.random_element()
    sum = f + g
    diff = f - g
    prod = f * g
    quot = f / g
    f_non_residue = f * (u + 9)
    frobenius_6 = f**(q**6)

    tests_dict['tests'].append({
        'scalar_1': fq2_to_dictionary(f),
        'scalar_2': fq2_to_dictionary(g),
        'expected': {
            'sum': fq2_to_dictionary(sum),
            'difference': fq2_to_dictionary(diff),
            'product': fq2_to_dictionary(prod),
            'quotient': fq2_to_dictionary(quot),
            'scalar_1_non_residue': fq2_to_dictionary(f_non_residue),
            'frobenius_6': fq2_to_dictionary(frobenius_6),
        }
    })

print('Fq2 tests formed successfully!')

# Saving the json file
FILE_NAME = '../json/field_extensions/fq2_tests.json'

print(f'Saving the Fq6 tests to {FILE_NAME}...')
with open(FILE_NAME, 'w') as f:
    json.dump(tests_dict, f, indent=4)

print('Successfully saved the Fq6 tests!')

# Generating Fq6 tests
print('Preparing the Fq6 tests...')
tests_dict = {'tests': []}

FQ6_TESTS_NUMBER = 1

for _ in range(FQ6_TESTS_NUMBER):
    # Defining inputs
    f = Fq6.random_element()
    g = Fq6.random_element()
    c0 = Fq2.random_element()
    c1 = Fq2.random_element()
    c2 = Fq2.random_element()
    h_c0c1 = c0 + c1*v
    h_c1 = c1*v
    h_c2 = c2*v^2

    # Defining the operations tested
    sum = f + g
    diff = f - g
    prod = f * g
    prod_c1 = f * h_c1
    prod_c0c1 = f * h_c0c1
    prod_c2 = f * h_c2
    f_inv = f.inverse()
    g_inv = g.inverse()
    quot = f / g
    f_square = f^2
    f_non_residue = f * v
    f_frobenius_1 = f^(q^1)
    g_frobenius_2 = g^(q^2)
    f_frobenius_3 = f^(q^3)

    tests_dict['tests'].append({
        'scalar_1': fq6_to_dictionary(f),
        'scalar_2': fq6_to_dictionary(g),
        'c0': fq2_to_dictionary(c0),
        'c1': fq2_to_dictionary(c1),
        'c2': fq2_to_dictionary(c2),
        'expected': {
            'sum': fq6_to_dictionary(sum),
            'difference': fq6_to_dictionary(diff),
            'product': fq6_to_dictionary(prod),
            'quotient': fq6_to_dictionary(quot),
            'product_c1': fq6_to_dictionary(prod_c1),
            'product_c0c1': fq6_to_dictionary(prod_c0c1),
            'product_c2': fq6_to_dictionary(prod_c2),
            'scalar_1_inverse': fq6_to_dictionary(f_inv),
            'scalar_1_square': fq6_to_dictionary(f_square),
            'scalar_1_non_residue': fq6_to_dictionary(f_non_residue),
            'scalar_1_frobenius_1': fq6_to_dictionary(f_frobenius_1),
            'scalar_2_frobenius_2': fq6_to_dictionary(g_frobenius_2),
            'scalar_1_frobenius_3': fq6_to_dictionary(f_frobenius_3),
        }
    })

print('Fq6 tests formed successfully!')

# Saving the json file
FILE_NAME = '../json/field_extensions/fq6_tests.json'

print(f'Saving the Fq6 tests to {FILE_NAME}...')
with open(FILE_NAME, 'w') as f:
    json.dump(tests_dict, f, indent=4)

print('Successfully saved the Fq6 tests!')

# --- Generating Fq12 tests ---
print('Preparing the Fq12 tests...')
tests_dict = {'tests': []}

FQ12_TESTS_NUMBER = 1
for _ in range(FQ12_TESTS_NUMBER):
    # Defining inputs
    f = Fq12.random_element()
    g = Fq12.random_element()

    # Defining sparse elements
    c0 = Fq2.random_element()
    c1 = Fq2.random_element()
    c3 = Fq2.random_element() 
    c4 = Fq2.random_element()
    c5 = Fq2.random_element()
    c0c1c4 = c0[0] + c0[1]*(W^6-9) + (c1[0]+c1[1]*(W^6-9))*W^2 + (c4[0]+c4[1]*(W^6-9))*W^3
    c0c3c4 = c0[0] + c0[1]*(W^6-9) + (c3[0]+c3[1]*(W^6-9))*W + (c4[0]+c4[1]*(W^6-9))*W^3
    c5v2w = (c5[0] + c5[1]*(W^6-9))*W^5

    # Defining the operations tested
    sum = f + g
    diff = f - g
    prod = f * g
    quot = f / g
    f_inv = f.inverse()
    f_square = f^2
    prod_c0c3c4 = f * c0c3c4
    prod_c0c1c4 = f * c0c1c4
    prod_c5v2w = f * c5v2w
    f_frobenius_1 = f^(q^1)
    g_frobenius_2 = g^(q^2)
    f_frobenius_3 = f^(q^3)
    pow_33 = f^(33)
    pow_67 = g^(67)
    pow_u = f^(4965661367192848881)
    pow_u2 = f^(4965661367192848881**2)
    pow_u3 = f^(4965661367192848881**3)

    tests_dict['tests'].append({
        'scalar_1': fq12_to_dictionary(f),
        'scalar_2': fq12_to_dictionary(g),
        'c0': fq2_to_dictionary(c0),
        'c1': fq2_to_dictionary(c1),
        'c3': fq2_to_dictionary(c3),
        'c4': fq2_to_dictionary(c4),
        'c5': fq2_to_dictionary(c5),
        'expected': {
            'sum': fq12_to_dictionary(sum),
            'difference': fq12_to_dictionary(diff),
            'product': fq12_to_dictionary(prod),
            'product_c0c3c4': fq12_to_dictionary(prod_c0c3c4),
            'product_c0c1c4': fq12_to_dictionary(prod_c0c1c4),
            'product_c5': fq12_to_dictionary(prod_c5v2w),
            'quotient': fq12_to_dictionary(quot),
            'scalar_1_inverse': fq12_to_dictionary(f_inv),
            'scalar_1_square': fq12_to_dictionary(f_square),
            'scalar_1_frobenius_1': fq12_to_dictionary(f_frobenius_1),
            'scalar_2_frobenius_2': fq12_to_dictionary(g_frobenius_2),
            'scalar_1_frobenius_3': fq12_to_dictionary(f_frobenius_3),
            'scalar_1_pow_33': fq12_to_dictionary(pow_33),
            'scalar_2_pow_67': fq12_to_dictionary(pow_67),
            'scalar_1_pow_u': fq12_to_dictionary(pow_u),
            'scalar_1_pow_u2': fq12_to_dictionary(pow_u2),
            'scalar_1_pow_u3': fq12_to_dictionary(pow_u3),
        }
    })

# Saving the fq12 tests
FILE_NAME = '../json/field_extensions/fq12_tests.json'
print(f'Saving the Fq12 tests to {FILE_NAME}...')
with open(FILE_NAME, 'w') as f:
    json.dump(tests_dict, f, indent=4)

print('Successfully saved the Fq12 tests!')