def test_G2_compress_and_decompress_flags(pt, on_curve, is_infinity): if on_curve: z1, z2 = compress_G2(pt) x1 = z1 % POW_2_381 c_flag1 = (z1 % 2**384) // POW_2_383 b_flag1 = (z1 % POW_2_383) // POW_2_382 a_flag1 = (z1 % POW_2_382) // POW_2_381 x2 = z2 % POW_2_381 c_flag2 = (z2 % 2**384) // POW_2_383 b_flag2 = (z2 % POW_2_383) // POW_2_382 a_flag2 = (z2 % POW_2_382) // POW_2_381 assert x1 < q assert x2 < q assert c_flag2 == b_flag2 == a_flag2 == 0 assert c_flag1 == 1 if is_infinity: assert b_flag1 == 1 assert a_flag1 == x1 == x2 == 0 else: assert b_flag1 == 0 _, y = normalize(pt) _, y_im = y.coeffs # TODO: need a case for y_im == 0 assert a_flag1 == (y_im * 2) // q # Correct flags should decompress correct x, y normalize(decompress_G2((z1, z2))) == normalize(pt) else: with pytest.raises(ValueError): compress_G2(pt)
def test_map_to_curve_matches_spec(proxy_contract, signing_root): field_elements_parts = proxy_contract.functions.hashToField(signing_root).call() field_elements = tuple( _convert_fp2_to_int(fp2_repr) for fp2_repr in field_elements_parts ) # NOTE: mapToCurve (called below) precompile includes "clearing the cofactor" first_group_element = normalize( clear_cofactor_G2(map_to_curve_G2(field_elements[0])) ) computed_first_group_element_parts = proxy_contract.functions.mapToCurve( field_elements_parts[0] ).call() computed_first_group_element = tuple( _convert_fp2_to_int(fp2_repr) for fp2_repr in computed_first_group_element_parts ) assert computed_first_group_element == first_group_element second_group_element = normalize( clear_cofactor_G2(map_to_curve_G2(field_elements[1])) ) computed_second_group_element_parts = proxy_contract.functions.mapToCurve( field_elements_parts[1] ).call() computed_second_group_element = tuple( _convert_fp2_to_int(fp2_repr) for fp2_repr in computed_second_group_element_parts ) assert computed_second_group_element == second_group_element
def compress_G2(pt: G2Uncompressed) -> G2Compressed: """ The compressed point (z1, z2) has the bit order: z1: (c_flag1, b_flag1, a_flag1, x1) z2: (c_flag2, b_flag2, a_flag2, x2) where - c_flag1 is always set to 1 - b_flag1 indicates infinity when set to 1 - a_flag1 helps determine the y-coordinate when decompressing, - a_flag2, b_flag2, and c_flag2 are always set to 0 """ if not is_on_curve(pt, b2): raise ValueError( "The given point is not on the twisted curve over FQ**2") if is_inf(pt): return G2Compressed((POW_2_383 + POW_2_382, 0)) x, y = normalize(pt) x_re, x_im = x.coeffs y_re, y_im = y.coeffs # Record the leftmost bit of y_im to the a_flag1 # If y_im happens to be zero, then use the bit of y_re a_flag1 = (y_im * 2) // q if y_im > 0 else (y_re * 2) // q # Imaginary part of x goes to z1, real part goes to z2 # c_flag1 = 1, b_flag1 = 0 z1 = x_im + a_flag1 * POW_2_381 + POW_2_383 # a_flag2 = b_flag2 = c_flag2 = 0 z2 = x_re return G2Compressed((z1, z2))
def generate_proof(data_tree, commitment_tree, proof_tree, indices, setup): committee_root = commitment_tree[0][0] # Generate a random r value; we use a power of r as a coefficient for each sub-leaf # to create a random linear combination r = int.from_bytes( hash(str([committee_root[0].n] + indices).encode('utf-8')), 'big') % b.curve_order #print("r", r) # Total polynomial that we are evaluating total_poly_evaluations = [0] * WIDTH # The set of all intermediate commitments commitments = [] total_proofs = b.Z1 for i, index in enumerate(indices): c = [] # Walk from top to bottom of the tree for d in range(DEPTH): # Power of r for this leaf rfactor = pow(r, i * DEPTH + d, MODULUS) # Position of the index in this layer of data position_of_leaf = index // WIDTH**(DEPTH - d - 1) # Position of the index within its data chunk sub_index = position_of_leaf % WIDTH proof = proof_tree[d][position_of_leaf] # Add in rfactor*D / (X - w**i) to the total total_proofs = b.add(total_proofs, b.multiply(proof, rfactor)) # Provide as part of the proof all intermediate-level commitments if d > 0: c.append(commitment_tree[d][position_of_leaf // WIDTH]) commitments.append(c) # Generate a polynomial commitment for the result return commitments, b.normalize(total_proofs)
def test_hash_to_curve_matches_spec(proxy_contract, signing_root, dst): result = proxy_contract.functions.hashToCurve(signing_root).call() converted_result = tuple(_convert_fp2_to_int(fp2_repr) for fp2_repr in result) spec_result = normalize(hash_to_G2(signing_root, dst, hashlib.sha256)) assert converted_result == spec_result
def from_point(cls, point: G1Uncompressed) -> 'BLS12_381_G1Type': if bls12_381.is_inf(point): x, y = POW_2_382, 0 else: x_pt, y_pt = bls12_381.normalize(point) x, y = x_pt.n, y_pt.n value = x.to_bytes(48, 'big') + y.to_bytes(48, 'big') return cls.from_value(value)
def to_hex(g1_point: G1Point) -> str: if bls12_381.is_inf(g1_point): xxx, yyy = bls.constants.POW_2_382, 0 else: xxx_pt, yyy_pt = bls12_381.normalize(g1_point) xxx, yyy = xxx_pt.n, yyy_pt.n xxx_bytes = xxx.to_bytes(48, byteorder='big') yyy_bytes = yyy.to_bytes(48, byteorder='big') return f'0x{(xxx_bytes + yyy_bytes).hex()}'
def from_point(cls, point: G2Uncompressed) -> 'BLS12_381_G2Type': if bls12_381.is_inf(point): x_re, x_im, y_re, y_im = 0, POW_2_382, 0, 0 else: x, y = bls12_381.normalize(point) x_re, x_im = x.coeffs y_re, y_im = y.coeffs value = x_im.to_bytes(48, 'big') + x_re.to_bytes(48, 'big') \ + y_im.to_bytes(48, 'big') + y_re.to_bytes(48, 'big') return cls(value)
def test_bls_pairing_check(proxy_contract, signing_root, bls_public_key, signature): public_key_point = pubkey_to_G1(bls_public_key) public_key = normalize(public_key_point) public_key_repr = ( _convert_int_to_fp_repr(public_key[0]), _convert_int_to_fp_repr(public_key[1]), ) # skip some data wrangling by calling contract function for this... message_on_curve = proxy_contract.functions.hashToCurve(signing_root).call() projective_signature_point = signature_to_G2(signature) signature_point = normalize(projective_signature_point) signature_repr = ( _convert_int_to_fp2_repr(signature_point[0]), _convert_int_to_fp2_repr(signature_point[1]), ) assert proxy_contract.functions.blsPairingCheck( public_key_repr, message_on_curve, signature_repr ).call()
def test_verify_and_deposit_fails_with_incorrect_signature( proxy_contract, deposit_contract, bls_public_key, withdrawal_credentials, signature, deposit_data_root, public_key_witness, signature_witness, deposit_amount, assert_tx_failed, signing_root, bls_private_key, ): assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 ) assert ( deposit_contract.functions.get_deposit_root().call().hex() == EMPTY_DEPOSIT_ROOT ) public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) another_message = hashlib.sha256(b"not the signing root").digest() assert signing_root != another_message signature = G2ProofOfPossession.Sign(bls_private_key, another_message) group_element = signature_to_G2(signature) normalized_group_element = normalize(group_element) signature_witness = normalized_group_element[1] signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) amount_in_wei = deposit_amount * 10 ** 9 txn = proxy_contract.functions.verifyAndDeposit( bls_public_key, withdrawal_credentials, signature, deposit_data_root, public_key_witness_repr, signature_witness_repr, ) assert_tx_failed(lambda: txn.transact({"value": amount_in_wei})) assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 )
def test_G1_compress_and_decompress_flags(pt, on_curve, is_infinity): assert on_curve == is_on_curve(pt, b) z = compress_G1(pt) if on_curve: x = z % POW_2_381 c_flag = (z % 2**384) // POW_2_383 b_flag = (z % POW_2_383) // POW_2_382 a_flag = (z % POW_2_382) // POW_2_381 assert x < q assert c_flag == 1 if is_infinity: assert b_flag == 1 assert a_flag == x == 0 else: assert b_flag == 0 pt_x, pt_y = normalize(pt) assert a_flag == (pt_y.n * 2) // q assert x == pt_x.n # Correct flags should decompress correct x, y normalize(decompress_G1(z)) == normalize(pt) else: with pytest.raises(ValueError): decompress_G1(z)
def to_hex(g2_point: G2Point) -> str: if bls12_381.is_inf(g2_point): x_re, x_im, y_re, y_im = 0, bls.constants.POW_2_382, 0, 0 else: xxx, yyy = bls12_381.normalize(g2_point) x_re, x_im = xxx.coeffs y_re, y_im = yyy.coeffs x_re_bytes = x_re.to_bytes(48, byteorder='big') x_im_bytes = x_im.to_bytes(48, byteorder='big') y_re_bytes = y_re.to_bytes(48, byteorder='big') y_im_bytes = y_im.to_bytes(48, byteorder='big') x_bytes = x_im_bytes + x_re_bytes y_bytes = y_im_bytes + y_re_bytes return f'0x{(x_bytes + y_bytes).hex()}'
def test_verify_and_deposit_fails_with_incorrect_public_key( proxy_contract, deposit_contract, withdrawal_credentials, signature, deposit_data_root, public_key_witness, signature_witness, deposit_amount, assert_tx_failed, seed, ): assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 ) assert ( deposit_contract.functions.get_deposit_root().call().hex() == EMPTY_DEPOSIT_ROOT ) another_seed = "another-secret".encode() assert seed != another_seed another_private_key = G2ProofOfPossession.KeyGen(another_seed) public_key = G2ProofOfPossession.SkToPk(another_private_key) group_element = pubkey_to_G1(public_key) normalized_group_element = normalize(group_element) public_key_witness = normalized_group_element[1] public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) amount_in_wei = deposit_amount * 10 ** 9 txn = proxy_contract.functions.verifyAndDeposit( public_key, withdrawal_credentials, signature, deposit_data_root, public_key_witness_repr, signature_witness_repr, ) assert_tx_failed(lambda: txn.transact({"value": amount_in_wei})) assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 )
def compress_G1(pt: G1Uncompressed) -> G1Compressed: """ A compressed point is a 384-bit integer with the bit order (c_flag, b_flag, a_flag, x), where the c_flag bit is always set to 1, the b_flag bit indicates infinity when set to 1, the a_flag bit helps determine the y-coordinate when decompressing, and the 381-bit integer x is the x-coordinate of the point. """ if is_inf(pt): # Set c_flag = 1 and b_flag = 1. leave a_flag = x = 0 return G1Compressed(POW_2_383 + POW_2_382) else: x, y = normalize(pt) # Record y's leftmost bit to the a_flag a_flag = (y.n * 2) // q # Set c_flag = 1 and b_flag = 0 return G1Compressed(x.n + a_flag * POW_2_381 + POW_2_383)
def test_bls_core(privkey): domain = (0).to_bytes(8, "little") p1 = multiply(G1, privkey) p2 = multiply(G2, privkey) msg = str(privkey).encode('utf-8') msghash = hash_to_G2(msg, domain=domain) assert normalize(decompress_G1(compress_G1(p1))) == normalize(p1) assert normalize(decompress_G2(compress_G2(p2))) == normalize(p2) assert normalize(decompress_G2(compress_G2(msghash))) == normalize(msghash) sig = sign(msg, privkey, domain=domain) pub = privtopub(privkey) assert verify(msg, pub, sig, domain=domain)
def test_bls_signature_is_valid_fails_with_invalid_signature( proxy_contract, bls_public_key, signing_root, public_key_witness, bls_private_key ): public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) another_message = hashlib.sha256(b"not the signing root").digest() assert signing_root != another_message signature = G2ProofOfPossession.Sign(bls_private_key, another_message) group_element = signature_to_G2(signature) normalized_group_element = normalize(group_element) signature_witness = normalized_group_element[1] signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) assert not proxy_contract.functions.blsSignatureIsValid( signing_root, bls_public_key, signature, public_key_witness_repr, signature_witness_repr, ).call()
def test_bls_signature_is_valid_fails_with_invalid_public_key( proxy_contract, seed, signing_root, signature, signature_witness ): another_seed = "another-secret".encode() assert seed != another_seed another_private_key = G2ProofOfPossession.KeyGen(another_seed) public_key = G2ProofOfPossession.SkToPk(another_private_key) group_element = pubkey_to_G1(public_key) normalized_group_element = normalize(group_element) public_key_witness = normalized_group_element[1] public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) assert not proxy_contract.functions.blsSignatureIsValid( signing_root, public_key, signature, public_key_witness_repr, signature_witness_repr, ).call()
def serialize_point(pt): pt = b.normalize(pt) return pt[0].n.to_bytes(64, 'little') + pt[1].n.to_bytes(64, 'little')
a = int.from_bytes(subchunk, "little") prod *= gmpy2.mpz((s1 if i % 2 == 0 else s2) + a) return 1 - jacobi_bit_mpz(prod, q) def legendre_aggregate_mpz_premul(x, s1, s2): bits = [ legendre_aggregate_chunk_mpz_premul(chunk, s1, s2) for chunk in chunkify(x) ] return jacobi_bit_mpz(int.from_bytes(bitarray_to_bytes(bits), "little"), q) time0 = timer() signature = normalize( signature_to_G2(sign(period_bytes, validator_privkey, DOMAIN_RANDAO))) time1 = timer() s1, s2 = signature[0].coeffs x = test_vector(2**21) time2 = timer() legendre_aggregate(x, gmpy2.mpz(s1), gmpy2.mpz(s2)) time3 = timer() legendre_aggregate_mpz(x, gmpy2.mpz(s1), gmpy2.mpz(s2))
def layer_commit(values, setup): values += [0] * (WIDTH - len(values)) coeffs = fft(values, MODULUS, ROOT_OF_UNITY, inv=True) return b.normalize(lincomb(setup[0][:len(coeffs)], coeffs, b.add, b.Z1))
def test_G1_pubkey_encode_decode(): G1_point = multiply(G1, 42) pubkey = G1_to_pubkey(G1_point) assert (normalize(pubkey_to_G1(pubkey)) == normalize(G1_point))
def compress_G1(pt: Tuple[FQ, FQ, FQ]) -> int: x, y = normalize(pt) return x.n + 2**383 * (y.n % 2)
def compress_G2(pt: Tuple[FQP, FQP, FQP]) -> Tuple[int, int]: if not is_on_curve(pt, b2): raise ValueError( "The given point is not on the twisted curve over FQ**2") x, y = normalize(pt) return (int(x.coeffs[0] + 2**383 * (y.coeffs[0] % 2)), int(x.coeffs[1]))
def test_G2_signature_encode_decode(): G2_point = multiply(G2, 42) signature = G2_to_signature(G2_point) assert (normalize(signature_to_G2(signature)) == normalize(G2_point))