From ca8a51fcf9b6b14135126d82e687010b0b4a57d5 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Mon, 6 Mar 2023 22:56:17 +0000 Subject: [PATCH 01/15] More CI tests for polynomial commitments --- .../test_polynomial_commitments.py | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index 67dce5c5b..b4904580b 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -9,6 +9,24 @@ from eth2spec.test.helpers.sharding import ( get_poly_in_both_forms, eval_poly_in_coeff_form, ) +from eth2spec.utils import bls + + +BLS_MODULUS = bls.curve_order + + +def bls_add_one(x): + """ + Adds "one" (actually bls.G1()) to a compressed group element. + Useful to compute definitely incorrect proofs. + """ + return bls.G1_to_bytes48( + bls.add(bls.bytes48_to_G1(x), bls.G1()) + ) + + +def field_element_bytes(x): + return int.to_bytes(x % BLS_MODULUS, 32, "little") @with_deneb_and_later @@ -18,12 +36,53 @@ def test_verify_kzg_proof(spec, state): blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) polynomial = spec.blob_to_polynomial(blob) + proof = spec.compute_kzg_proof(blob, field_element_bytes(x)) + + y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) + assert spec.verify_kzg_proof(commitment, field_element_bytes(x), field_element_bytes(y), proof) + + +@with_deneb_and_later +@spec_state_test +def test_verify_kzg_proof_incorrect_proof(spec, state): + x = 3465 + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + polynomial = spec.blob_to_polynomial(blob) + proof = spec.compute_kzg_proof(blob, field_element_bytes(x)) + proof = bls_add_one(proof) + + y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) + assert not spec.verify_kzg_proof(commitment, field_element_bytes(x), field_element_bytes(y), proof) + + +@with_deneb_and_later +@spec_state_test +def test_verify_kzg_proof_impl(spec, state): + x = spec.BLS_MODULUS - 1 + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + polynomial = spec.blob_to_polynomial(blob) proof = spec.compute_kzg_proof_impl(polynomial, x) y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) assert spec.verify_kzg_proof_impl(commitment, x, y, proof) +@with_deneb_and_later +@spec_state_test +def test_verify_kzg_proof_impl_incorrect_proof(spec, state): + x = 324561 + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + polynomial = spec.blob_to_polynomial(blob) + proof = spec.compute_kzg_proof_impl(polynomial, x) + proof = bls_add_one(proof) + + y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) + assert not spec.verify_kzg_proof_impl(commitment, x, y, proof) + + @with_deneb_and_later @spec_state_test def test_barycentric_outside_domain(spec, state): @@ -107,3 +166,42 @@ def test_compute_kzg_proof_within_domain(spec, state): y = spec.evaluate_polynomial_in_evaluation_form(polynomial, z) assert spec.verify_kzg_proof_impl(commitment, z, y, proof) + + +@with_deneb_and_later +@spec_state_test +def test_verify_blob_kzg_proof(spec, state): + """ + Create and verify KZG proof that p(z) == y + where z is in the domain of our KZG scheme (i.e. a relevant root of unity). + """ + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + proof = spec.compute_blob_kzg_proof(blob) + + assert spec.verify_blob_kzg_proof(blob, commitment, proof) + + +@with_deneb_and_later +@spec_state_test +def test_verify_blob_kzg_proof_incorrect_proof(spec, state): + """ + Create and verify KZG proof that p(z) == y + where z is in the domain of our KZG scheme (i.e. a relevant root of unity). + """ + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + proof = spec.compute_blob_kzg_proof(blob) + proof = bls_add_one(proof) + + assert not spec.verify_blob_kzg_proof(blob, commitment, proof) + + +@with_deneb_and_later +@spec_state_test +def test_validate_kzg_g1(spec, state): + """ + Verify that `validate_kzg_g1` allows the neutral element in G1 + """ + + spec.validate_kzg_g1(bls.G1_to_bytes48(bls.Z1())) From 661cca59c065af32cd6ede1ef673be6093ed26e1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 7 Mar 2023 16:52:09 +0800 Subject: [PATCH 02/15] Import `curve_order as BLS_MODULUS` --- .../polynomial_commitments/test_polynomial_commitments.py | 2 +- tests/core/pyspec/eth2spec/utils/bls.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index b4904580b..9b60f7e7c 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -12,7 +12,7 @@ from eth2spec.test.helpers.sharding import ( from eth2spec.utils import bls -BLS_MODULUS = bls.curve_order +BLS_MODULUS = bls.BLS_MODULUS def bls_add_one(x): diff --git a/tests/core/pyspec/eth2spec/utils/bls.py b/tests/core/pyspec/eth2spec/utils/bls.py index 7ea22be46..7dd9597eb 100644 --- a/tests/core/pyspec/eth2spec/utils/bls.py +++ b/tests/core/pyspec/eth2spec/utils/bls.py @@ -12,6 +12,7 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401 FQ12 as py_ecc_GT, ) from py_ecc.bls.g2_primitives import ( # noqa: F401 + curve_order as BLS_MODULUS, G1_to_pubkey as py_ecc_G1_to_bytes48, pubkey_to_G1 as py_ecc_bytes48_to_G1, G2_to_signature as py_ecc_G2_to_bytes96, From 81ab7de44a6815f388f4af0292ce9e9f11544197 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 7 Mar 2023 10:54:17 +0000 Subject: [PATCH 03/15] Add unit tests for validate_kzg_g1 and bytes_to_bls_field --- .../test_polynomial_commitments.py | 101 ++++++++++++++++-- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index 9b60f7e7c..a13ce6851 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -3,6 +3,7 @@ import random from eth2spec.test.context import ( spec_state_test, with_deneb_and_later, + expect_assertion_error ) from eth2spec.test.helpers.sharding import ( get_sample_blob, @@ -10,9 +11,13 @@ from eth2spec.test.helpers.sharding import ( eval_poly_in_coeff_form, ) from eth2spec.utils import bls +from eth2spec.utils.bls import BLS_MODULUS - -BLS_MODULUS = bls.BLS_MODULUS +G1 = bls.G1_to_bytes48(bls.G1()) +P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + + "0123456789abcdef0123456789abcdef0123456789abcdef") +P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + + "0123456789abcdef0123456789abcdef0123456789abcde0") def bls_add_one(x): @@ -32,6 +37,9 @@ def field_element_bytes(x): @with_deneb_and_later @spec_state_test def test_verify_kzg_proof(spec, state): + """ + Test the wrapper functions (taking bytes arguments) for computing and verifying KZG proofs. + """ x = 3 blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -45,6 +53,9 @@ def test_verify_kzg_proof(spec, state): @with_deneb_and_later @spec_state_test def test_verify_kzg_proof_incorrect_proof(spec, state): + """ + Test the wrapper function `verify_kzg_proof` fails on an incorrect proof. + """ x = 3465 blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -59,6 +70,9 @@ def test_verify_kzg_proof_incorrect_proof(spec, state): @with_deneb_and_later @spec_state_test def test_verify_kzg_proof_impl(spec, state): + """ + Test the implementation functions (taking field element arguments) for computing and verifying KZG proofs. + """ x = spec.BLS_MODULUS - 1 blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -72,6 +86,9 @@ def test_verify_kzg_proof_impl(spec, state): @with_deneb_and_later @spec_state_test def test_verify_kzg_proof_impl_incorrect_proof(spec, state): + """ + Test the implementation function `verify_kzg_proof` fails on an incorrect proof. + """ x = 324561 blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -172,8 +189,7 @@ def test_compute_kzg_proof_within_domain(spec, state): @spec_state_test def test_verify_blob_kzg_proof(spec, state): """ - Create and verify KZG proof that p(z) == y - where z is in the domain of our KZG scheme (i.e. a relevant root of unity). + Test the functions to compute and verify a blob KZG proof """ blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -186,8 +202,7 @@ def test_verify_blob_kzg_proof(spec, state): @spec_state_test def test_verify_blob_kzg_proof_incorrect_proof(spec, state): """ - Create and verify KZG proof that p(z) == y - where z is in the domain of our KZG scheme (i.e. a relevant root of unity). + Check that `verify_blob_kzg_proof` fails on an incorrect proof """ blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) @@ -199,9 +214,81 @@ def test_verify_blob_kzg_proof_incorrect_proof(spec, state): @with_deneb_and_later @spec_state_test -def test_validate_kzg_g1(spec, state): +def test_validate_kzg_g1_generator(spec, state): + """ + Verify that `validate_kzg_g1` allows the generator G1 + """ + + spec.validate_kzg_g1(bls.G1_to_bytes48(bls.G1())) + + +@with_deneb_and_later +@spec_state_test +def test_validate_kzg_g1_neutral_element(spec, state): """ Verify that `validate_kzg_g1` allows the neutral element in G1 """ spec.validate_kzg_g1(bls.G1_to_bytes48(bls.Z1())) + + +@with_deneb_and_later +@spec_state_test +def test_validate_kzg_g1_not_in_g1(spec, state): + """ + Verify that `validate_kzg_g1` fails on point not in G1 + """ + + expect_assertion_error(lambda: spec.validate_kzg_g1(P1_NOT_IN_G1)) + + +@with_deneb_and_later +@spec_state_test +def test_validate_kzg_g1_not_on_curve(spec, state): + """ + Verify that `validate_kzg_g1` fails on point not in G1 + """ + + expect_assertion_error(lambda: spec.validate_kzg_g1(P1_NOT_ON_CURVE)) + + +@with_deneb_and_later +@spec_state_test +def test_bytes_to_bls_field_zero(spec, state): + """ + Verify that `bytes_to_bls_field` handles zero + """ + + spec.bytes_to_bls_field(b"\0" * 32) + + +@with_deneb_and_later +@spec_state_test +def test_bytes_to_bls_field_modulus_minus_one(spec, state): + """ + Verify that `bytes_to_bls_field` handles modulus minus one + """ + + spec.bytes_to_bls_field((BLS_MODULUS - 1).to_bytes(spec.BYTES_PER_FIELD_ELEMENT, spec.ENDIANNESS)) + + +@with_deneb_and_later +@spec_state_test +def test_bytes_to_bls_field_modulus(spec, state): + """ + Verify that `bytes_to_bls_field` fails on BLS modulus + """ + + expect_assertion_error(lambda: spec.bytes_to_bls_field( + BLS_MODULUS.to_bytes(spec.BYTES_PER_FIELD_ELEMENT, spec.ENDIANNESS) + )) + + +@with_deneb_and_later +@spec_state_test +def test_bytes_to_bls_field_max(spec, state): + """ + Verify that `bytes_to_bls_field` fails on 2**256 - 1 + """ + + expect_assertion_error(lambda: spec.bytes_to_bls_field(b"\xFF" * 32)) From cce82b4938c6f05fc137e07739f5fdcca6ce71c7 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 7 Mar 2023 10:56:16 +0000 Subject: [PATCH 04/15] Remove spec. for getting BLS_MODULUS --- .../polynomial_commitments/test_polynomial_commitments.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index a13ce6851..91115a299 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -73,7 +73,7 @@ def test_verify_kzg_proof_impl(spec, state): """ Test the implementation functions (taking field element arguments) for computing and verifying KZG proofs. """ - x = spec.BLS_MODULUS - 1 + x = BLS_MODULUS - 1 blob = get_sample_blob(spec) commitment = spec.blob_to_kzg_commitment(blob) polynomial = spec.blob_to_polynomial(blob) @@ -119,9 +119,9 @@ def test_barycentric_outside_domain(spec, state): for _ in range(n_samples): # Get a random evaluation point and make sure it's not a root of unity - z = rng.randint(0, spec.BLS_MODULUS - 1) + z = rng.randint(0, BLS_MODULUS - 1) while z in roots_of_unity_brp: - z = rng.randint(0, spec.BLS_MODULUS - 1) + z = rng.randint(0, BLS_MODULUS - 1) # Get p(z) by evaluating poly in coefficient form p_z_coeff = eval_poly_in_coeff_form(spec, poly_coeff, z) From b4c130a4a25bd50299c3fbb67bc0b8723db26c01 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 7 Mar 2023 21:57:45 +0000 Subject: [PATCH 05/15] Remove state from unit tests --- .../test_polynomial_commitments.py | 88 +++++++++++-------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index 91115a299..8ad552056 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -1,7 +1,8 @@ import random from eth2spec.test.context import ( - spec_state_test, + spec_test, + single_phase, with_deneb_and_later, expect_assertion_error ) @@ -35,8 +36,9 @@ def field_element_bytes(x): @with_deneb_and_later -@spec_state_test -def test_verify_kzg_proof(spec, state): +@spec_test +@single_phase +def test_verify_kzg_proof(spec): """ Test the wrapper functions (taking bytes arguments) for computing and verifying KZG proofs. """ @@ -51,8 +53,9 @@ def test_verify_kzg_proof(spec, state): @with_deneb_and_later -@spec_state_test -def test_verify_kzg_proof_incorrect_proof(spec, state): +@spec_test +@single_phase +def test_verify_kzg_proof_incorrect_proof(spec): """ Test the wrapper function `verify_kzg_proof` fails on an incorrect proof. """ @@ -68,8 +71,9 @@ def test_verify_kzg_proof_incorrect_proof(spec, state): @with_deneb_and_later -@spec_state_test -def test_verify_kzg_proof_impl(spec, state): +@spec_test +@single_phase +def test_verify_kzg_proof_impl(spec): """ Test the implementation functions (taking field element arguments) for computing and verifying KZG proofs. """ @@ -84,8 +88,9 @@ def test_verify_kzg_proof_impl(spec, state): @with_deneb_and_later -@spec_state_test -def test_verify_kzg_proof_impl_incorrect_proof(spec, state): +@spec_test +@single_phase +def test_verify_kzg_proof_impl_incorrect_proof(spec): """ Test the implementation function `verify_kzg_proof` fails on an incorrect proof. """ @@ -101,8 +106,9 @@ def test_verify_kzg_proof_impl_incorrect_proof(spec, state): @with_deneb_and_later -@spec_state_test -def test_barycentric_outside_domain(spec, state): +@spec_test +@single_phase +def test_barycentric_outside_domain(spec): """ Test barycentric formula correctness by using it to evaluate a polynomial at a bunch of points outside its domain (the roots of unity). @@ -134,8 +140,9 @@ def test_barycentric_outside_domain(spec, state): @with_deneb_and_later -@spec_state_test -def test_barycentric_within_domain(spec, state): +@spec_test +@single_phase +def test_barycentric_within_domain(spec): """ Test barycentric formula correctness by using it to evaluate a polynomial at all the points of its domain (the roots of unity). @@ -166,8 +173,9 @@ def test_barycentric_within_domain(spec, state): @with_deneb_and_later -@spec_state_test -def test_compute_kzg_proof_within_domain(spec, state): +@spec_test +@single_phase +def test_compute_kzg_proof_within_domain(spec): """ Create and verify KZG proof that p(z) == y where z is in the domain of our KZG scheme (i.e. a relevant root of unity). @@ -186,8 +194,9 @@ def test_compute_kzg_proof_within_domain(spec, state): @with_deneb_and_later -@spec_state_test -def test_verify_blob_kzg_proof(spec, state): +@spec_test +@single_phase +def test_verify_blob_kzg_proof(spec): """ Test the functions to compute and verify a blob KZG proof """ @@ -199,8 +208,9 @@ def test_verify_blob_kzg_proof(spec, state): @with_deneb_and_later -@spec_state_test -def test_verify_blob_kzg_proof_incorrect_proof(spec, state): +@spec_test +@single_phase +def test_verify_blob_kzg_proof_incorrect_proof(spec): """ Check that `verify_blob_kzg_proof` fails on an incorrect proof """ @@ -213,8 +223,9 @@ def test_verify_blob_kzg_proof_incorrect_proof(spec, state): @with_deneb_and_later -@spec_state_test -def test_validate_kzg_g1_generator(spec, state): +@spec_test +@single_phase +def test_validate_kzg_g1_generator(spec): """ Verify that `validate_kzg_g1` allows the generator G1 """ @@ -223,8 +234,9 @@ def test_validate_kzg_g1_generator(spec, state): @with_deneb_and_later -@spec_state_test -def test_validate_kzg_g1_neutral_element(spec, state): +@spec_test +@single_phase +def test_validate_kzg_g1_neutral_element(spec): """ Verify that `validate_kzg_g1` allows the neutral element in G1 """ @@ -233,8 +245,9 @@ def test_validate_kzg_g1_neutral_element(spec, state): @with_deneb_and_later -@spec_state_test -def test_validate_kzg_g1_not_in_g1(spec, state): +@spec_test +@single_phase +def test_validate_kzg_g1_not_in_g1(spec): """ Verify that `validate_kzg_g1` fails on point not in G1 """ @@ -243,8 +256,9 @@ def test_validate_kzg_g1_not_in_g1(spec, state): @with_deneb_and_later -@spec_state_test -def test_validate_kzg_g1_not_on_curve(spec, state): +@spec_test +@single_phase +def test_validate_kzg_g1_not_on_curve(spec): """ Verify that `validate_kzg_g1` fails on point not in G1 """ @@ -253,8 +267,9 @@ def test_validate_kzg_g1_not_on_curve(spec, state): @with_deneb_and_later -@spec_state_test -def test_bytes_to_bls_field_zero(spec, state): +@spec_test +@single_phase +def test_bytes_to_bls_field_zero(spec): """ Verify that `bytes_to_bls_field` handles zero """ @@ -263,8 +278,9 @@ def test_bytes_to_bls_field_zero(spec, state): @with_deneb_and_later -@spec_state_test -def test_bytes_to_bls_field_modulus_minus_one(spec, state): +@spec_test +@single_phase +def test_bytes_to_bls_field_modulus_minus_one(spec): """ Verify that `bytes_to_bls_field` handles modulus minus one """ @@ -273,8 +289,9 @@ def test_bytes_to_bls_field_modulus_minus_one(spec, state): @with_deneb_and_later -@spec_state_test -def test_bytes_to_bls_field_modulus(spec, state): +@spec_test +@single_phase +def test_bytes_to_bls_field_modulus(spec): """ Verify that `bytes_to_bls_field` fails on BLS modulus """ @@ -285,8 +302,9 @@ def test_bytes_to_bls_field_modulus(spec, state): @with_deneb_and_later -@spec_state_test -def test_bytes_to_bls_field_max(spec, state): +@spec_test +@single_phase +def test_bytes_to_bls_field_max(spec): """ Verify that `bytes_to_bls_field` fails on 2**256 - 1 """ From a5333a1d108838b3bb20ec91759616b8bb4ab5be Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Fri, 10 Mar 2023 11:43:05 +0000 Subject: [PATCH 06/15] Remove useless test_kzg.py --- .../eth2spec/test/deneb/unittests/test_kzg.py | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 tests/core/pyspec/eth2spec/test/deneb/unittests/test_kzg.py diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_kzg.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_kzg.py deleted file mode 100644 index 71bfae8b8..000000000 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_kzg.py +++ /dev/null @@ -1,21 +0,0 @@ - -from eth2spec.test.helpers.constants import ( - DENEB, - MINIMAL, -) -from eth2spec.test.helpers.sharding import ( - get_sample_blob, -) -from eth2spec.test.context import ( - with_phases, - spec_state_test, - with_presets, -) - - -@with_phases([DENEB]) -@spec_state_test -@with_presets([MINIMAL]) -def test_blob_to_kzg_commitment(spec, state): - blob = get_sample_blob(spec) - spec.blob_to_kzg_commitment(blob) From 5e74c5141138cadf7c339031cac1aefc91f8b8bc Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sat, 11 Mar 2023 22:34:41 +0000 Subject: [PATCH 07/15] Tests for validate_blobs_and_kzg_commitments and fix --- specs/deneb/validator.md | 2 +- .../deneb/unittests/validator/__init__.py | 0 .../unittests/validator/test_validator.py | 113 ++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 tests/core/pyspec/eth2spec/test/deneb/unittests/validator/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index 50ab832e0..6519ec6e6 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -72,7 +72,7 @@ def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, # Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) assert len(blob_kzg_commitments) == len(blobs) - assert [blob_to_kzg_commitment(blob) == commitment for blob, commitment in zip(blobs, blob_kzg_commitments)] + assert all(blob_to_kzg_commitment(blob) == commitment for blob, commitment in zip(blobs, blob_kzg_commitments)) ``` 3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`. diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py new file mode 100644 index 000000000..48a01f624 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -0,0 +1,113 @@ +import random + +from eth2spec.test.context import ( + spec_state_test, + with_deneb_and_later, + expect_assertion_error +) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) +from eth2spec.test.helpers.sharding import ( + get_sample_opaque_tx, +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.utils import bls +from eth2spec.utils.bls import BLS_MODULUS + +G1 = bls.G1_to_bytes48(bls.G1()) +P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + + "0123456789abcdef0123456789abcdef0123456789abcdef") +P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + + "0123456789abcdef0123456789abcdef0123456789abcde0") + + +def bls_add_one(x): + """ + Adds "one" (actually bls.G1()) to a compressed group element. + Useful to compute definitely incorrect proofs. + """ + return bls.G1_to_bytes48( + bls.add(bls.bytes48_to_G1(x), bls.G1()) + ) + + +def field_element_bytes(x): + return int.to_bytes(x % BLS_MODULUS, 32, "little") + + +@with_deneb_and_later +@spec_state_test +def test_validate_blobs_and_kzg_commitments(spec, state): + """ + Test `validate_blobs_and_kzg_commitments` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + # state_transition_and_sign_block(spec, state, block) + + blob_sidecars = spec.get_blob_sidecars(block, blobs) + blobs = [sidecar.blob for sidecar in blob_sidecars] + + spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, + blobs, + blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_validate_blobs_and_kzg_commitments_missing_blob(spec, state): + """ + Test `validate_blobs_and_kzg_commitments` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + # state_transition_and_sign_block(spec, state, block) + + blob_sidecars = spec.get_blob_sidecars(block, blobs) + blobs = [sidecar.blob for sidecar in blob_sidecars][:-1] + + expect_assertion_error(lambda: + spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, + blobs, + blob_kzg_commitments) + ) + + +@with_deneb_and_later +@spec_state_test +def test_validate_blobs_and_kzg_commitments_incorrect_blob(spec, state): + """ + Test `validate_blobs_and_kzg_commitments` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + # state_transition_and_sign_block(spec, state, block) + + blob_sidecars = spec.get_blob_sidecars(block, blobs) + blobs = [sidecar.blob for sidecar in blob_sidecars] + + blobs[1] = spec.Blob(blobs[1][:13] + bytes([(blobs[1][13] + 1) % 256]) + blobs[1][14:]) + + expect_assertion_error(lambda: + spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, + blobs, + blob_kzg_commitments) + ) \ No newline at end of file From 29b5309f7df8166ef25803c6542116c69ffa1203 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sat, 11 Mar 2023 23:02:35 +0000 Subject: [PATCH 08/15] Add proofs to validate_blobs_and_kzg_commitments --- specs/deneb/validator.md | 9 +++-- .../unittests/validator/test_validator.py | 39 +++++++++++++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index 6519ec6e6..d2209986e 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -44,7 +44,7 @@ Note: This API is *unstable*. `get_blobs_and_kzg_commitments` and `get_payload` Implementers may also retrieve blobs individually per transaction. ```python -def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment]]: +def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment], Sequence[KZGProof]]: # pylint: disable=unused-argument ... ``` @@ -66,13 +66,14 @@ use the `payload_id` to retrieve `blobs` and `blob_kzg_commitments` via `get_blo ```python def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, blobs: Sequence[Blob], - blob_kzg_commitments: Sequence[KZGCommitment]) -> None: + blob_kzg_commitments: Sequence[KZGCommitment], + blob_kzg_proofs: Sequence[KZGProof]) -> None: # Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments) # Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) - assert len(blob_kzg_commitments) == len(blobs) - assert all(blob_to_kzg_commitment(blob) == commitment for blob, commitment in zip(blobs, blob_kzg_commitments)) + assert len(blob_kzg_commitments) == len(blobs) == len(blob_kzg_proofs) + assert verify_blob_kzg_proof_batch(blobs, blob_kzg_commitments, blob_kzg_proofs) ``` 3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`. diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 48a01f624..33b216e87 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -53,14 +53,15 @@ def test_validate_blobs_and_kzg_commitments(spec, state): block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - # state_transition_and_sign_block(spec, state, block) blob_sidecars = spec.get_blob_sidecars(block, blobs) blobs = [sidecar.blob for sidecar in blob_sidecars] + proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, blobs, - blob_kzg_commitments) + blob_kzg_commitments, + proofs) @with_deneb_and_later @@ -79,11 +80,39 @@ def test_validate_blobs_and_kzg_commitments_missing_blob(spec, state): blob_sidecars = spec.get_blob_sidecars(block, blobs) blobs = [sidecar.blob for sidecar in blob_sidecars][:-1] + proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] expect_assertion_error(lambda: spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, blobs, - blob_kzg_commitments) + blob_kzg_commitments, + proofs) + ) + + +@with_deneb_and_later +@spec_state_test +def test_validate_blobs_and_kzg_commitments_missing_proof(spec, state): + """ + Test `validate_blobs_and_kzg_commitments` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + # state_transition_and_sign_block(spec, state, block) + + blob_sidecars = spec.get_blob_sidecars(block, blobs) + blobs = [sidecar.blob for sidecar in blob_sidecars] + proofs = [sidecar.kzg_proof for sidecar in blob_sidecars][:-1] + + expect_assertion_error(lambda: + spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, + blobs, + blob_kzg_commitments, + proofs) ) @@ -103,11 +132,13 @@ def test_validate_blobs_and_kzg_commitments_incorrect_blob(spec, state): blob_sidecars = spec.get_blob_sidecars(block, blobs) blobs = [sidecar.blob for sidecar in blob_sidecars] + proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] blobs[1] = spec.Blob(blobs[1][:13] + bytes([(blobs[1][13] + 1) % 256]) + blobs[1][14:]) expect_assertion_error(lambda: spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, blobs, - blob_kzg_commitments) + blob_kzg_commitments, + proofs) ) \ No newline at end of file From 96ad61bcecb6473283e312094d23dc3378cfb796 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sun, 12 Mar 2023 23:05:01 +0000 Subject: [PATCH 09/15] Add tests for blob sidecar signature --- specs/deneb/p2p-interface.md | 11 ++ specs/deneb/validator.md | 9 +- .../eth2spec/test/deneb/sanity/test_blocks.py | 4 +- .../fork_choice/test_validate_blobs.py | 5 +- .../test/deneb/unittests/test_offset.py | 2 +- .../unittests/validator/test_validator.py | 122 ++++++++++++------ .../pyspec/eth2spec/test/helpers/sharding.py | 5 +- 7 files changed, 105 insertions(+), 53 deletions(-) diff --git a/specs/deneb/p2p-interface.md b/specs/deneb/p2p-interface.md index 107c90b9a..9f4c15849 100644 --- a/specs/deneb/p2p-interface.md +++ b/specs/deneb/p2p-interface.md @@ -73,6 +73,17 @@ class BlobIdentifier(Container): index: BlobIndex ``` +### Helpers + +#### `verify_sidecar_signature` + +```python +def verify_blob_sidecar_signature(state: BeaconState, signed_blob_sidecar: SignedBlobSidecar) -> bool: + proposer = state.validators[signed_blob_sidecar.message.proposer_index] + signing_root = compute_signing_root(signed_blob_sidecar.message, get_domain(state, DOMAIN_BLOB_SIDECAR)) + return bls.Verify(proposer.pubkey, signing_root, signed_blob_sidecar.signature) +``` + ## The gossip domain: gossipsub Some gossip meshes are upgraded in the fork of Deneb to support upgraded types. diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index d2209986e..a21fadd08 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -44,7 +44,8 @@ Note: This API is *unstable*. `get_blobs_and_kzg_commitments` and `get_payload` Implementers may also retrieve blobs individually per transaction. ```python -def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment], Sequence[KZGProof]]: +def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> \ + Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment], Sequence[KZGProof]]: # pylint: disable=unused-argument ... ``` @@ -88,7 +89,9 @@ Blobs associated with a block are packaged into sidecar objects for distribution Each `sidecar` is obtained from: ```python -def get_blob_sidecars(block: BeaconBlock, blobs: Sequence[Blob]) -> Sequence[BlobSidecar]: +def get_blob_sidecars(block: BeaconBlock, + blobs: Sequence[Blob], + blob_kzg_proofs: Sequence[KZGProof]) -> Sequence[BlobSidecar]: return [ BlobSidecar( block_root=hash_tree_root(block), @@ -97,7 +100,7 @@ def get_blob_sidecars(block: BeaconBlock, blobs: Sequence[Blob]) -> Sequence[Blo block_parent_root=block.parent_root, blob=blob, kzg_commitment=block.body.blob_kzg_commitments[index], - kzg_proof=compute_blob_kzg_proof(blob, block.body.blob_kzg_commitments[index]), + kzg_proof=blob_kzg_proofs[index], ) for index, blob in enumerate(blobs) ] diff --git a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py index c7fb708b8..5e65dbd4e 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py @@ -22,7 +22,7 @@ def test_one_blob(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec) + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) @@ -38,7 +38,7 @@ def test_max_blobs(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=spec.MAX_BLOBS_PER_BLOCK) + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=spec.MAX_BLOBS_PER_BLOCK) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/fork_choice/test_validate_blobs.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/fork_choice/test_validate_blobs.py index d9934c5ad..0d7bd53e5 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/fork_choice/test_validate_blobs.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/fork_choice/test_validate_blobs.py @@ -18,14 +18,13 @@ from eth2spec.test.helpers.sharding import ( def _run_validate_blobs(spec, state, blob_count): block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + opaque_tx, blobs, blob_kzg_commitments, kzg_proofs = get_sample_opaque_tx(spec, blob_count=blob_count) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) state_transition_and_sign_block(spec, state, block) - # Also test the proof generation in `get_blob_sidecars` - blob_sidecars = spec.get_blob_sidecars(block, blobs) + blob_sidecars = spec.get_blob_sidecars(block, blobs, kzg_proofs) blobs = [sidecar.blob for sidecar in blob_sidecars] kzg_proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] spec.validate_blobs(blob_kzg_commitments, blobs, kzg_proofs) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py index 13150180b..3c3b51ff1 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py @@ -17,7 +17,7 @@ from eth2spec.test.context import ( @spec_state_test @with_presets([MINIMAL]) def test_tx_peek_blob_versioned_hashes(spec, state): - otx, blobs, commitments = get_sample_opaque_tx(spec) + otx, _, commitments, _ = get_sample_opaque_tx(spec) data_hashes = spec.tx_peek_blob_versioned_hashes(otx) expected = [spec.kzg_commitment_to_versioned_hash(blob_commitment) for blob_commitment in commitments] assert expected == data_hashes diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 33b216e87..7e5cf8bfe 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -1,6 +1,5 @@ -import random - from eth2spec.test.context import ( + always_bls, spec_state_test, with_deneb_and_later, expect_assertion_error @@ -14,8 +13,8 @@ from eth2spec.test.helpers.sharding import ( from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot ) -from eth2spec.test.helpers.state import ( - state_transition_and_sign_block, +from eth2spec.test.helpers.keys import ( + pubkey_to_privkey ) from eth2spec.utils import bls from eth2spec.utils.bls import BLS_MODULUS @@ -49,15 +48,11 @@ def test_validate_blobs_and_kzg_commitments(spec, state): """ blob_count = 4 block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - blob_sidecars = spec.get_blob_sidecars(block, blobs) - blobs = [sidecar.blob for sidecar in blob_sidecars] - proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] - spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, blobs, blob_kzg_commitments, @@ -72,21 +67,18 @@ def test_validate_blobs_and_kzg_commitments_missing_blob(spec, state): """ blob_count = 4 block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - # state_transition_and_sign_block(spec, state, block) - blob_sidecars = spec.get_blob_sidecars(block, blobs) - blobs = [sidecar.blob for sidecar in blob_sidecars][:-1] - proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] - - expect_assertion_error(lambda: - spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, - blobs, - blob_kzg_commitments, - proofs) + expect_assertion_error( + lambda: spec.validate_blobs_and_kzg_commitments( + block.body.execution_payload, + blobs[:-1], + blob_kzg_commitments, + proofs + ) ) @@ -98,21 +90,18 @@ def test_validate_blobs_and_kzg_commitments_missing_proof(spec, state): """ blob_count = 4 block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - # state_transition_and_sign_block(spec, state, block) - blob_sidecars = spec.get_blob_sidecars(block, blobs) - blobs = [sidecar.blob for sidecar in blob_sidecars] - proofs = [sidecar.kzg_proof for sidecar in blob_sidecars][:-1] - - expect_assertion_error(lambda: - spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, - blobs, - blob_kzg_commitments, - proofs) + expect_assertion_error( + lambda: spec.validate_blobs_and_kzg_commitments( + block.body.execution_payload, + blobs, + blob_kzg_commitments, + proofs[:-1] + ) ) @@ -124,21 +113,68 @@ def test_validate_blobs_and_kzg_commitments_incorrect_blob(spec, state): """ blob_count = 4 block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - # state_transition_and_sign_block(spec, state, block) - - blob_sidecars = spec.get_blob_sidecars(block, blobs) - blobs = [sidecar.blob for sidecar in blob_sidecars] - proofs = [sidecar.kzg_proof for sidecar in blob_sidecars] blobs[1] = spec.Blob(blobs[1][:13] + bytes([(blobs[1][13] + 1) % 256]) + blobs[1][14:]) - expect_assertion_error(lambda: - spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, - blobs, - blob_kzg_commitments, - proofs) - ) \ No newline at end of file + expect_assertion_error( + lambda: spec.validate_blobs_and_kzg_commitments( + block.body.execution_payload, + blobs, + blob_kzg_commitments, + proofs + ) + ) + + +@with_deneb_and_later +@spec_state_test +def test_blob_sidecar_signature(spec, state): + """ + Test `get_blob_sidecar_signature` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + + blob_sidecars = spec.get_blob_sidecars(block, blobs, proofs) + proposer = state.validators[blob_sidecars[1].proposer_index] + privkey = pubkey_to_privkey[proposer.pubkey] + sidecar_signature = spec.get_blob_sidecar_signature(state, + blob_sidecars[1], + privkey) + + signed_blob_sidecar = spec.SignedBlobSidecar(message=blob_sidecars[1], signature=sidecar_signature) + + assert spec.verify_blob_sidecar_signature(state, signed_blob_sidecar) + + +@with_deneb_and_later +@spec_state_test +@always_bls +def test_blob_sidecar_signature_incorrect(spec, state): + """ + Test `get_blob_sidecar_signature` + """ + blob_count = 4 + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) + block.body.blob_kzg_commitments = blob_kzg_commitments + block.body.execution_payload.transactions = [opaque_tx] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + + blob_sidecars = spec.get_blob_sidecars(block, blobs, proofs) + + sidecar_signature = spec.get_blob_sidecar_signature(state, + blob_sidecars[1], + 123) + + signed_blob_sidecar = spec.SignedBlobSidecar(message=blob_sidecars[1], signature=sidecar_signature) + + assert not spec.verify_blob_sidecar_signature(state, signed_blob_sidecar) diff --git a/tests/core/pyspec/eth2spec/test/helpers/sharding.py b/tests/core/pyspec/eth2spec/test/helpers/sharding.py index fd60d5d3b..89f03c3c3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/sharding.py +++ b/tests/core/pyspec/eth2spec/test/helpers/sharding.py @@ -101,13 +101,16 @@ def get_poly_in_both_forms(spec, rng=None): def get_sample_opaque_tx(spec, blob_count=1, rng=None): blobs = [] blob_kzg_commitments = [] + blob_kzg_proofs = [] blob_versioned_hashes = [] for _ in range(blob_count): blob = get_sample_blob(spec, rng) blob_commitment = spec.KZGCommitment(spec.blob_to_kzg_commitment(blob)) + blob_kzg_proof = spec.compute_blob_kzg_proof(blob, blob_commitment) blob_versioned_hash = spec.kzg_commitment_to_versioned_hash(blob_commitment) blobs.append(blob) blob_kzg_commitments.append(blob_commitment) + blob_kzg_proofs.append(blob_kzg_proof) blob_versioned_hashes.append(blob_versioned_hash) signed_blob_tx = SignedBlobTransaction( @@ -117,4 +120,4 @@ def get_sample_opaque_tx(spec, blob_count=1, rng=None): ) serialized_tx = serialize(signed_blob_tx) opaque_tx = spec.uint_to_bytes(spec.BLOB_TX_TYPE) + serialized_tx - return opaque_tx, blobs, blob_kzg_commitments + return opaque_tx, blobs, blob_kzg_commitments, blob_kzg_proofs From 723e8a11fe4ee025f9f4faa3352c5ca27bfb59a5 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sun, 12 Mar 2023 23:07:19 +0000 Subject: [PATCH 10/15] Remove unused imports/functions in test_validator.py --- .../unittests/validator/test_validator.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 7e5cf8bfe..07039ccfe 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -16,28 +16,6 @@ from eth2spec.test.helpers.block import ( from eth2spec.test.helpers.keys import ( pubkey_to_privkey ) -from eth2spec.utils import bls -from eth2spec.utils.bls import BLS_MODULUS - -G1 = bls.G1_to_bytes48(bls.G1()) -P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + - "0123456789abcdef0123456789abcdef0123456789abcdef") -P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" + - "0123456789abcdef0123456789abcdef0123456789abcde0") - - -def bls_add_one(x): - """ - Adds "one" (actually bls.G1()) to a compressed group element. - Useful to compute definitely incorrect proofs. - """ - return bls.G1_to_bytes48( - bls.add(bls.bytes48_to_G1(x), bls.G1()) - ) - - -def field_element_bytes(x): - return int.to_bytes(x % BLS_MODULUS, 32, "little") @with_deneb_and_later From cc284b2b60af6c857210f0e413f2ed423a95e6fa Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sun, 12 Mar 2023 23:11:40 +0000 Subject: [PATCH 11/15] Toc --- specs/deneb/p2p-interface.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/deneb/p2p-interface.md b/specs/deneb/p2p-interface.md index 9f4c15849..6f10f7a34 100644 --- a/specs/deneb/p2p-interface.md +++ b/specs/deneb/p2p-interface.md @@ -15,6 +15,8 @@ The specification of these changes continues in the same format as the network s - [`BlobSidecar`](#blobsidecar) - [`SignedBlobSidecar`](#signedblobsidecar) - [`BlobIdentifier`](#blobidentifier) + - [Helpers](#helpers) + - [`verify_sidecar_signature`](#verify_sidecar_signature) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) From ff7a6c5d0e8ea7af80836fb808bb22e391390f6d Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 14 Mar 2023 22:14:31 +0000 Subject: [PATCH 12/15] Add description when to use verify_sidecar_signature --- specs/deneb/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/p2p-interface.md b/specs/deneb/p2p-interface.md index 6f10f7a34..b6960db3c 100644 --- a/specs/deneb/p2p-interface.md +++ b/specs/deneb/p2p-interface.md @@ -125,7 +125,7 @@ The following validations MUST pass before forwarding the `sidecar` on the netwo - _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `sidecar.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` - _[IGNORE]_ The sidecar's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved). - _[REJECT]_ The sidecar's block's parent (defined by `sidecar.block_parent_root`) passes validation. -- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid with respect to the `sidecar.proposer_index` pubkey. +- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid as verified by `verify_sidecar_signature` - _[IGNORE]_ The sidecar is the only sidecar with valid signature received for the tuple `(sidecar.block_root, sidecar.index)`. - _[REJECT]_ The sidecar is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `block_parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the sidecar MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case _do not_ `REJECT`, instead `IGNORE` this message. From 2d4bfabceba821cd1ce3fba67322a14294c7343c Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 14 Mar 2023 22:16:41 +0000 Subject: [PATCH 13/15] Correct signature of get_blobs_and_kzg_commitments --- specs/deneb/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index a21fadd08..04d8e2a40 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -45,7 +45,7 @@ Implementers may also retrieve blobs individually per transaction. ```python def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> \ - Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment], Sequence[KZGProof]]: + Tuple[Sequence[Blob], Sequence[KZGCommitment], Sequence[KZGProof]]: # pylint: disable=unused-argument ... ``` From 3e281e745770dc8282b3f9fc7cf944f39bf3f393 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 14 Mar 2023 22:22:23 +0000 Subject: [PATCH 14/15] Alternative for linter --- specs/deneb/validator.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index 04d8e2a40..b627de023 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -44,8 +44,9 @@ Note: This API is *unstable*. `get_blobs_and_kzg_commitments` and `get_payload` Implementers may also retrieve blobs individually per transaction. ```python -def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> \ - Tuple[Sequence[Blob], Sequence[KZGCommitment], Sequence[KZGProof]]: +def get_blobs_and_kzg_commitments( + payload_id: PayloadId +) -> Tuple[Sequence[Blob], Sequence[KZGCommitment], Sequence[KZGProof]]: # pylint: disable=unused-argument ... ``` From 5977f36fef8de792c3cf1eea5d5883adabdbe9a9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 15 Mar 2023 10:54:49 -0600 Subject: [PATCH 15/15] minor nits from code review --- specs/deneb/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/p2p-interface.md b/specs/deneb/p2p-interface.md index 6207cc76a..9be028620 100644 --- a/specs/deneb/p2p-interface.md +++ b/specs/deneb/p2p-interface.md @@ -126,7 +126,7 @@ The following validations MUST pass before forwarding the `sidecar` on the netwo - _[IGNORE]_ The sidecar's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved). - _[REJECT]_ The sidecar's block's parent (defined by `sidecar.block_parent_root`) passes validation. - _[REJECT]_ The sidecar is from a higher slot than the sidecar's block's parent (defined by `sidecar.block_parent_root`). -- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid as verified by `verify_sidecar_signature` +- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid as verified by `verify_sidecar_signature`. - _[IGNORE]_ The sidecar is the only sidecar with valid signature received for the tuple `(sidecar.block_root, sidecar.index)`. - _[REJECT]_ The sidecar is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `block_parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the sidecar MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case _do not_ `REJECT`, instead `IGNORE` this message.