Merge branch 'dev' into full-exit-has-partial-withdrawal-test
This commit is contained in:
commit
111a65816a
|
@ -52,6 +52,16 @@ jobs:
|
|||
cp -r presets/ ../consensus-spec-tests/presets
|
||||
cp -r configs/ ../consensus-spec-tests/configs
|
||||
find . -type d -empty -delete
|
||||
- name: Check for errors
|
||||
run: |
|
||||
if grep -q "\[ERROR\]" consensustestgen.log; then
|
||||
echo "There is an error in the log"
|
||||
exit 1
|
||||
fi
|
||||
if find . -type f -name "INCOMPLETE" | grep -q "INCOMPLETE"; then
|
||||
echo "There is an INCOMPLETE file"
|
||||
exit 1
|
||||
fi
|
||||
- name: Archive configurations
|
||||
run: |
|
||||
cd consensus-spec-tests
|
||||
|
|
20
Makefile
20
Makefile
|
@ -34,7 +34,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \
|
|||
$(wildcard $(SPEC_DIR)/_features/*/*/*.md) \
|
||||
$(wildcard $(SSZ_DIR)/*.md)
|
||||
|
||||
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7732
|
||||
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 eip7594 eip7732
|
||||
# The parameters for commands. Use `foreach` to avoid listing specs again.
|
||||
COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
|
||||
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S)
|
||||
|
@ -105,7 +105,7 @@ generate_tests: $(GENERATOR_TARGETS)
|
|||
|
||||
# "make pyspec" to create the pyspec for all phases.
|
||||
pyspec:
|
||||
python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev
|
||||
@python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev
|
||||
|
||||
# check the setup tool requirements
|
||||
preinstallation:
|
||||
|
@ -141,13 +141,21 @@ endif
|
|||
open_cov:
|
||||
((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) &
|
||||
|
||||
# Check all files and error if any ToC were modified.
|
||||
check_toc: $(MARKDOWN_FILES:=.toc)
|
||||
@[ "$$(find . -name '*.md.tmp' -print -quit)" ] && exit 1 || exit 0
|
||||
|
||||
# Generate ToC sections & save copy of original if modified.
|
||||
%.toc:
|
||||
cp $* $*.tmp && \
|
||||
doctoc $* && \
|
||||
diff -q $* $*.tmp && \
|
||||
rm $*.tmp
|
||||
@cp $* $*.tmp; \
|
||||
doctoc $* > /dev/null; \
|
||||
if diff -q $* $*.tmp > /dev/null; then \
|
||||
echo "Good $*"; \
|
||||
rm $*.tmp; \
|
||||
else \
|
||||
echo "\033[1;33m Bad $*\033[0m"; \
|
||||
echo "\033[1;34m See $*.tmp\033[0m"; \
|
||||
fi
|
||||
|
||||
codespell:
|
||||
codespell . --skip "./.git,./venv,$(PY_SPEC_DIR)/.mypy_cache" -I .codespell-whitelist
|
||||
|
|
|
@ -8,7 +8,7 @@ This repository hosts the current Ethereum proof-of-stake specifications. Discus
|
|||
|
||||
## Specs
|
||||
|
||||
[![GitHub release](https://img.shields.io/github/v/release/ethereum/eth2.0-specs)](https://github.com/ethereum/eth2.0-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec)
|
||||
[![GitHub release](https://img.shields.io/github/v/release/ethereum/consensus-specs)](https://github.com/ethereum/consensus-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) [![testgen](https://github.com/ethereum/consensus-specs/actions/workflows/generate_vectors.yml/badge.svg?branch=dev&event=schedule)](https://github.com/ethereum/consensus-specs/actions/workflows/generate_vectors.yml)
|
||||
|
||||
Core specifications for Ethereum proof-of-stake clients can be found in [specs](specs/). These are divided into features.
|
||||
Features are researched and developed in parallel, and then consolidated into sequential upgrades when ready.
|
||||
|
|
|
@ -41,5 +41,5 @@ MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2
|
|||
|
||||
# Withdrawals processing
|
||||
# ---------------------------------------------------------------
|
||||
# 2**0 ( = 1) pending withdrawals
|
||||
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1
|
||||
# 2**1 ( = 2) pending withdrawals
|
||||
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2
|
||||
|
|
|
@ -119,6 +119,7 @@ def objects_to_spec(preset_name: str,
|
|||
hardcoded_func_dep_presets = reduce(lambda obj, builder: {**obj, **builder.hardcoded_func_dep_presets(spec_object)}, builders, {})
|
||||
# Concatenate all strings
|
||||
imports = reduce(lambda txt, builder: (txt + "\n\n" + builder.imports(preset_name) ).strip("\n"), builders, "")
|
||||
classes = reduce(lambda txt, builder: (txt + "\n\n" + builder.classes() ).strip("\n"), builders, "")
|
||||
preparations = reduce(lambda txt, builder: (txt + "\n\n" + builder.preparations() ).strip("\n"), builders, "")
|
||||
sundry_functions = reduce(lambda txt, builder: (txt + "\n\n" + builder.sundry_functions() ).strip("\n"), builders, "")
|
||||
# Keep engine from the most recent fork
|
||||
|
@ -154,6 +155,8 @@ def objects_to_spec(preset_name: str,
|
|||
constant_vars_spec,
|
||||
preset_vars_spec,
|
||||
config_spec,
|
||||
# Custom classes which are not required to be SSZ containers.
|
||||
classes,
|
||||
ordered_class_objects_spec,
|
||||
protocols_spec,
|
||||
functions_spec,
|
||||
|
|
|
@ -15,6 +15,13 @@ class BaseSpecBuilder(ABC):
|
|||
"""
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def classes(cls) -> str:
|
||||
"""
|
||||
Define special classes.
|
||||
"""
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def preparations(cls) -> str:
|
||||
"""
|
||||
|
|
|
@ -12,6 +12,21 @@ class DenebSpecBuilder(BaseSpecBuilder):
|
|||
from eth2spec.capella import {preset_name} as capella
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def classes(cls):
|
||||
return f'''
|
||||
class BLSFieldElement(bls.Scalar):
|
||||
pass
|
||||
|
||||
|
||||
class Polynomial(list):
|
||||
def __init__(self, evals: Optional[Sequence[BLSFieldElement]] = None):
|
||||
if evals is None:
|
||||
evals = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB
|
||||
if len(evals) != FIELD_ELEMENTS_PER_BLOB:
|
||||
raise ValueError("expected FIELD_ELEMENTS_PER_BLOB evals")
|
||||
super().__init__(evals)
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def preparations(cls):
|
||||
|
|
|
@ -12,12 +12,41 @@ class EIP7594SpecBuilder(BaseSpecBuilder):
|
|||
return f'''
|
||||
from eth2spec.deneb import {preset_name} as deneb
|
||||
'''
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def classes(cls):
|
||||
return f'''
|
||||
class PolynomialCoeff(list):
|
||||
def __init__(self, coeffs: Sequence[BLSFieldElement]):
|
||||
if len(coeffs) > FIELD_ELEMENTS_PER_EXT_BLOB:
|
||||
raise ValueError("expected <= FIELD_ELEMENTS_PER_EXT_BLOB coeffs")
|
||||
super().__init__(coeffs)
|
||||
|
||||
|
||||
class Coset(list):
|
||||
def __init__(self, coeffs: Optional[Sequence[BLSFieldElement]] = None):
|
||||
if coeffs is None:
|
||||
coeffs = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_CELL
|
||||
if len(coeffs) != FIELD_ELEMENTS_PER_CELL:
|
||||
raise ValueError("expected FIELD_ELEMENTS_PER_CELL coeffs")
|
||||
super().__init__(coeffs)
|
||||
|
||||
|
||||
class CosetEvals(list):
|
||||
def __init__(self, evals: Optional[Sequence[BLSFieldElement]] = None):
|
||||
if evals is None:
|
||||
evals = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_CELL
|
||||
if len(evals) != FIELD_ELEMENTS_PER_CELL:
|
||||
raise ValueError("expected FIELD_ELEMENTS_PER_CELL coeffs")
|
||||
super().__init__(evals)
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def sundry_functions(cls) -> str:
|
||||
return """
|
||||
def retrieve_column_sidecars(beacon_block_root: Root) -> Sequence[DataColumnSidecar]:
|
||||
# pylint: disable=unused-argument
|
||||
return []
|
||||
"""
|
||||
|
||||
|
|
27
setup.py
27
setup.py
|
@ -35,6 +35,16 @@ from pysetup.helpers import (
|
|||
)
|
||||
from pysetup.md_doc_paths import get_md_doc_paths
|
||||
|
||||
# Ignore '1.5.0-alpha.*' to '1.5.0a*' messages.
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore', message='Normalizing .* to .*')
|
||||
|
||||
# Ignore 'running' and 'creating' messages
|
||||
import logging
|
||||
class PyspecFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
return not record.getMessage().startswith(('running ', 'creating '))
|
||||
logging.getLogger().addFilter(PyspecFilter())
|
||||
|
||||
# NOTE: have to programmatically include third-party dependencies in `setup.py`.
|
||||
def installPackage(package: str):
|
||||
|
@ -173,7 +183,7 @@ def _update_constant_vars_with_kzg_setups(constant_vars, preset_name):
|
|||
constant_vars['KZG_SETUP_G1_MONOMIAL'] = VariableDefinition(constant_vars['KZG_SETUP_G1_MONOMIAL'].value, str(kzg_setups[0]), comment, None)
|
||||
constant_vars['KZG_SETUP_G1_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_G1_LAGRANGE'].value, str(kzg_setups[1]), comment, None)
|
||||
constant_vars['KZG_SETUP_G2_MONOMIAL'] = VariableDefinition(constant_vars['KZG_SETUP_G2_MONOMIAL'].value, str(kzg_setups[2]), comment, None)
|
||||
|
||||
|
||||
|
||||
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject:
|
||||
functions: Dict[str, str] = {}
|
||||
|
@ -251,10 +261,17 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
|
|||
# marko parses `**X**` as a list containing a X
|
||||
description = description[0].children
|
||||
|
||||
if isinstance(name, list):
|
||||
# marko parses `[X]()` as a list containing a X
|
||||
name = name[0].children
|
||||
if isinstance(value, list):
|
||||
# marko parses `**X**` as a list containing a X
|
||||
value = value[0].children
|
||||
|
||||
# Skip types that have been defined elsewhere
|
||||
if description is not None and description.startswith("<!-- predefined-type -->"):
|
||||
continue
|
||||
|
||||
if not _is_constant_id(name):
|
||||
# Check for short type declarations
|
||||
if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List", "ByteVector")):
|
||||
|
@ -394,8 +411,6 @@ class PySpecCommand(Command):
|
|||
def finalize_options(self):
|
||||
"""Post-process options."""
|
||||
if len(self.md_doc_paths) == 0:
|
||||
print("no paths were specified, using default markdown file paths for pyspec"
|
||||
" build (spec fork: %s)" % self.spec_fork)
|
||||
self.md_doc_paths = get_md_doc_paths(self.spec_fork)
|
||||
if len(self.md_doc_paths) == 0:
|
||||
raise Exception('no markdown files specified, and spec fork "%s" is unknown', self.spec_fork)
|
||||
|
@ -428,6 +443,7 @@ class PySpecCommand(Command):
|
|||
if not self.dry_run:
|
||||
dir_util.mkpath(self.out_dir)
|
||||
|
||||
print(f'Building pyspec: {self.spec_fork}')
|
||||
for (name, preset_paths, config_path) in self.parsed_build_targets:
|
||||
spec_str = build_spec(
|
||||
spec_builders[self.spec_fork].fork,
|
||||
|
@ -492,7 +508,6 @@ class PyspecDevCommand(Command):
|
|||
self.run_command('pyspec')
|
||||
|
||||
def run(self):
|
||||
print("running build_py command")
|
||||
for spec_fork in spec_builders:
|
||||
self.run_pyspec_cmd(spec_fork=spec_fork)
|
||||
|
||||
|
@ -561,7 +576,7 @@ setup(
|
|||
RUAMEL_YAML_VERSION,
|
||||
"lru-dict==1.2.0",
|
||||
MARKO_VERSION,
|
||||
"py_arkworks_bls12381==0.3.4",
|
||||
"curdleproofs==0.1.1",
|
||||
"py_arkworks_bls12381==0.3.8",
|
||||
"curdleproofs==0.1.2",
|
||||
]
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
- [Introduction](#introduction)
|
||||
- [Public Methods](#public-methods)
|
||||
- [Custom types](#custom-types)
|
||||
- [Cryptographic types](#cryptographic-types)
|
||||
- [Preset](#preset)
|
||||
- [Cells](#cells)
|
||||
- [Helper functions](#helper-functions)
|
||||
|
@ -69,13 +70,18 @@ The following is a list of the public methods:
|
|||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `PolynomialCoeff` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | A polynomial in coefficient form |
|
||||
| `Coset` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The evaluation domain of a cell |
|
||||
| `CosetEvals` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The internal representation of a cell (the evaluations over its Coset) |
|
||||
| `Cell` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL]` | The unit of blob data that can come with its own KZG proof |
|
||||
| `CellIndex` | `uint64` | Validation: `x < CELLS_PER_EXT_BLOB` |
|
||||
| `CommitmentIndex` | `uint64` | The type which represents the index of an element in the list of commitments |
|
||||
|
||||
## Cryptographic types
|
||||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| [`PolynomialCoeff`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L20-L24) | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | <!-- predefined-type --> A polynomial in coefficient form |
|
||||
| [`Coset`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L27-L33) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | <!-- predefined-type --> The evaluation domain of a cell |
|
||||
| [`CosetEvals`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/eip7594.py#L36-L42) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | <!-- predefined-type --> A cell's evaluations over its coset |
|
||||
|
||||
## Preset
|
||||
|
||||
### Cells
|
||||
|
@ -101,13 +107,12 @@ def cell_to_coset_evals(cell: Cell) -> CosetEvals:
|
|||
"""
|
||||
Convert an untrusted ``Cell`` into a trusted ``CosetEvals``.
|
||||
"""
|
||||
evals = []
|
||||
evals = CosetEvals()
|
||||
for i in range(FIELD_ELEMENTS_PER_CELL):
|
||||
start = i * BYTES_PER_FIELD_ELEMENT
|
||||
end = (i + 1) * BYTES_PER_FIELD_ELEMENT
|
||||
value = bytes_to_bls_field(cell[start:end])
|
||||
evals.append(value)
|
||||
return CosetEvals(evals)
|
||||
evals[i] = bytes_to_bls_field(cell[start:end])
|
||||
return evals
|
||||
```
|
||||
|
||||
#### `coset_evals_to_cell`
|
||||
|
@ -128,17 +133,16 @@ def coset_evals_to_cell(coset_evals: CosetEvals) -> Cell:
|
|||
#### `_fft_field`
|
||||
|
||||
```python
|
||||
def _fft_field(vals: Sequence[BLSFieldElement],
|
||||
roots_of_unity: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
|
||||
def _fft_field(vals: Sequence[BLSFieldElement], roots_of_unity: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
|
||||
if len(vals) == 1:
|
||||
return vals
|
||||
L = _fft_field(vals[::2], roots_of_unity[::2])
|
||||
R = _fft_field(vals[1::2], roots_of_unity[::2])
|
||||
o = [BLSFieldElement(0) for _ in vals]
|
||||
for i, (x, y) in enumerate(zip(L, R)):
|
||||
y_times_root = (int(y) * int(roots_of_unity[i])) % BLS_MODULUS
|
||||
o[i] = BLSFieldElement((int(x) + y_times_root) % BLS_MODULUS)
|
||||
o[i + len(L)] = BLSFieldElement((int(x) - y_times_root + BLS_MODULUS) % BLS_MODULUS)
|
||||
y_times_root = y * roots_of_unity[i]
|
||||
o[i] = x + y_times_root
|
||||
o[i + len(L)] = x - y_times_root
|
||||
return o
|
||||
```
|
||||
|
||||
|
@ -150,9 +154,8 @@ def fft_field(vals: Sequence[BLSFieldElement],
|
|||
inv: bool=False) -> Sequence[BLSFieldElement]:
|
||||
if inv:
|
||||
# Inverse FFT
|
||||
invlen = pow(len(vals), BLS_MODULUS - 2, BLS_MODULUS)
|
||||
return [BLSFieldElement((int(x) * invlen) % BLS_MODULUS)
|
||||
for x in _fft_field(vals, list(roots_of_unity[0:1]) + list(roots_of_unity[:0:-1]))]
|
||||
invlen = BLSFieldElement(len(vals)).pow(BLSFieldElement(BLS_MODULUS - 2))
|
||||
return [x * invlen for x in _fft_field(vals, list(roots_of_unity[0:1]) + list(roots_of_unity[:0:-1]))]
|
||||
else:
|
||||
# Regular FFT
|
||||
return _fft_field(vals, roots_of_unity)
|
||||
|
@ -169,26 +172,26 @@ def coset_fft_field(vals: Sequence[BLSFieldElement],
|
|||
This is useful for when one wants to divide by a polynomial which
|
||||
vanishes on one or more elements in the domain.
|
||||
"""
|
||||
vals = vals.copy()
|
||||
vals = [v for v in vals] # copy
|
||||
|
||||
def shift_vals(vals: Sequence[BLSFieldElement], factor: BLSFieldElement) -> Sequence[BLSFieldElement]:
|
||||
"""
|
||||
Multiply each entry in `vals` by succeeding powers of `factor`
|
||||
i.e., [vals[0] * factor^0, vals[1] * factor^1, ..., vals[n] * factor^n]
|
||||
"""
|
||||
shift = 1
|
||||
updated_vals: List[BLSFieldElement] = []
|
||||
shift = BLSFieldElement(1)
|
||||
for i in range(len(vals)):
|
||||
vals[i] = BLSFieldElement((int(vals[i]) * shift) % BLS_MODULUS)
|
||||
shift = (shift * int(factor)) % BLS_MODULUS
|
||||
return vals
|
||||
updated_vals.append(vals[i] * shift)
|
||||
shift = shift * factor
|
||||
return updated_vals
|
||||
|
||||
# This is the coset generator; it is used to compute a FFT/IFFT over a coset of
|
||||
# the roots of unity.
|
||||
shift_factor = BLSFieldElement(PRIMITIVE_ROOT_OF_UNITY)
|
||||
if inv:
|
||||
vals = fft_field(vals, roots_of_unity, inv)
|
||||
shift_inv = bls_modular_inverse(shift_factor)
|
||||
return shift_vals(vals, shift_inv)
|
||||
return shift_vals(vals, shift_factor.inverse())
|
||||
else:
|
||||
vals = shift_vals(vals, shift_factor)
|
||||
return fft_field(vals, roots_of_unity, inv)
|
||||
|
@ -234,9 +237,7 @@ def polynomial_eval_to_coeff(polynomial: Polynomial) -> PolynomialCoeff:
|
|||
Interpolates a polynomial (given in evaluation form) to a polynomial in coefficient form.
|
||||
"""
|
||||
roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB)
|
||||
polynomial_coeff = fft_field(bit_reversal_permutation(list(polynomial)), roots_of_unity, inv=True)
|
||||
|
||||
return polynomial_coeff
|
||||
return PolynomialCoeff(fft_field(bit_reversal_permutation(polynomial), roots_of_unity, inv=True))
|
||||
```
|
||||
|
||||
#### `add_polynomialcoeff`
|
||||
|
@ -247,9 +248,8 @@ def add_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoe
|
|||
Sum the coefficient form polynomials ``a`` and ``b``.
|
||||
"""
|
||||
a, b = (a, b) if len(a) >= len(b) else (b, a)
|
||||
length_a = len(a)
|
||||
length_b = len(b)
|
||||
return [(a[i] + (b[i] if i < length_b else 0)) % BLS_MODULUS for i in range(length_a)]
|
||||
length_a, length_b = len(a), len(b)
|
||||
return PolynomialCoeff([a[i] + (b[i] if i < length_b else BLSFieldElement(0)) for i in range(length_a)])
|
||||
```
|
||||
|
||||
#### `neg_polynomialcoeff`
|
||||
|
@ -257,9 +257,9 @@ def add_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoe
|
|||
```python
|
||||
def neg_polynomialcoeff(a: PolynomialCoeff) -> PolynomialCoeff:
|
||||
"""
|
||||
Negative of coefficient form polynomial ``a``
|
||||
Negative of coefficient form polynomial ``a``.
|
||||
"""
|
||||
return [(BLS_MODULUS - x) % BLS_MODULUS for x in a]
|
||||
return PolynomialCoeff([-x for x in a])
|
||||
```
|
||||
|
||||
#### `multiply_polynomialcoeff`
|
||||
|
@ -267,13 +267,13 @@ def neg_polynomialcoeff(a: PolynomialCoeff) -> PolynomialCoeff:
|
|||
```python
|
||||
def multiply_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff:
|
||||
"""
|
||||
Multiplies the coefficient form polynomials ``a`` and ``b``
|
||||
Multiplies the coefficient form polynomials ``a`` and ``b``.
|
||||
"""
|
||||
assert len(a) + len(b) <= FIELD_ELEMENTS_PER_EXT_BLOB
|
||||
|
||||
r = [0]
|
||||
r = PolynomialCoeff([BLSFieldElement(0)])
|
||||
for power, coef in enumerate(a):
|
||||
summand = [0] * power + [int(coef) * int(x) % BLS_MODULUS for x in b]
|
||||
summand = PolynomialCoeff([BLSFieldElement(0)] * power + [coef * x for x in b])
|
||||
r = add_polynomialcoeff(r, summand)
|
||||
return r
|
||||
```
|
||||
|
@ -283,21 +283,21 @@ def multiply_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> Polynomi
|
|||
```python
|
||||
def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff:
|
||||
"""
|
||||
Long polynomial division for two coefficient form polynomials ``a`` and ``b``
|
||||
Long polynomial division for two coefficient form polynomials ``a`` and ``b``.
|
||||
"""
|
||||
a = a.copy() # Make a copy since `a` is passed by reference
|
||||
o: List[BLSFieldElement] = []
|
||||
a = PolynomialCoeff(a[:]) # copy
|
||||
o = PolynomialCoeff([])
|
||||
apos = len(a) - 1
|
||||
bpos = len(b) - 1
|
||||
diff = apos - bpos
|
||||
while diff >= 0:
|
||||
quot = div(a[apos], b[bpos])
|
||||
quot = a[apos] / b[bpos]
|
||||
o.insert(0, quot)
|
||||
for i in range(bpos, -1, -1):
|
||||
a[diff + i] = (int(a[diff + i]) - int(b[i] + BLS_MODULUS) * int(quot)) % BLS_MODULUS
|
||||
a[diff + i] = a[diff + i] - b[i] * quot
|
||||
apos -= 1
|
||||
diff -= 1
|
||||
return [x % BLS_MODULUS for x in o]
|
||||
return o
|
||||
```
|
||||
|
||||
#### `interpolate_polynomialcoeff`
|
||||
|
@ -305,23 +305,21 @@ def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> Polynomial
|
|||
```python
|
||||
def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSFieldElement]) -> PolynomialCoeff:
|
||||
"""
|
||||
Lagrange interpolation: Finds the lowest degree polynomial that takes the value ``ys[i]`` at ``x[i]``
|
||||
for all i.
|
||||
Lagrange interpolation: Finds the lowest degree polynomial that takes the value ``ys[i]`` at ``x[i]`` for all i.
|
||||
Outputs a coefficient form polynomial. Leading coefficients may be zero.
|
||||
"""
|
||||
assert len(xs) == len(ys)
|
||||
r = [0]
|
||||
|
||||
r = PolynomialCoeff([BLSFieldElement(0)])
|
||||
for i in range(len(xs)):
|
||||
summand = [ys[i]]
|
||||
summand = PolynomialCoeff([ys[i]])
|
||||
for j in range(len(ys)):
|
||||
if j != i:
|
||||
weight_adjustment = bls_modular_inverse(int(xs[i]) - int(xs[j]))
|
||||
weight_adjustment = (xs[i] - xs[j]).inverse()
|
||||
summand = multiply_polynomialcoeff(
|
||||
summand, [((BLS_MODULUS - int(weight_adjustment)) * int(xs[j])) % BLS_MODULUS, weight_adjustment]
|
||||
summand, PolynomialCoeff([-weight_adjustment * xs[j], weight_adjustment])
|
||||
)
|
||||
r = add_polynomialcoeff(r, summand)
|
||||
|
||||
return r
|
||||
```
|
||||
|
||||
|
@ -330,11 +328,11 @@ def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSF
|
|||
```python
|
||||
def vanishing_polynomialcoeff(xs: Sequence[BLSFieldElement]) -> PolynomialCoeff:
|
||||
"""
|
||||
Compute the vanishing polynomial on ``xs`` (in coefficient form)
|
||||
Compute the vanishing polynomial on ``xs`` (in coefficient form).
|
||||
"""
|
||||
p = [1]
|
||||
p = PolynomialCoeff([BLSFieldElement(1)])
|
||||
for x in xs:
|
||||
p = multiply_polynomialcoeff(p, [-int(x) + BLS_MODULUS, 1])
|
||||
p = multiply_polynomialcoeff(p, PolynomialCoeff([-x, BLSFieldElement(1)]))
|
||||
return p
|
||||
```
|
||||
|
||||
|
@ -343,12 +341,12 @@ def vanishing_polynomialcoeff(xs: Sequence[BLSFieldElement]) -> PolynomialCoeff:
|
|||
```python
|
||||
def evaluate_polynomialcoeff(polynomial_coeff: PolynomialCoeff, z: BLSFieldElement) -> BLSFieldElement:
|
||||
"""
|
||||
Evaluate a coefficient form polynomial at ``z`` using Horner's schema
|
||||
Evaluate a coefficient form polynomial at ``z`` using Horner's schema.
|
||||
"""
|
||||
y = 0
|
||||
y = BLSFieldElement(0)
|
||||
for coef in polynomial_coeff[::-1]:
|
||||
y = (int(y) * int(z) + int(coef)) % BLS_MODULUS
|
||||
return BLSFieldElement(y % BLS_MODULUS)
|
||||
y = y * z + coef
|
||||
return y
|
||||
```
|
||||
|
||||
### KZG multiproofs
|
||||
|
@ -371,11 +369,11 @@ def compute_kzg_proof_multi_impl(
|
|||
- Z(X) is the degree `k` polynomial that evaluates to zero on all `k` points
|
||||
|
||||
We further note that since the degree of I(X) is less than the degree of Z(X),
|
||||
the computation can be simplified in monomial form to Q(X) = f(X) / Z(X)
|
||||
the computation can be simplified in monomial form to Q(X) = f(X) / Z(X).
|
||||
"""
|
||||
|
||||
# For all points, compute the evaluation of those points
|
||||
ys = [evaluate_polynomialcoeff(polynomial_coeff, z) for z in zs]
|
||||
ys = CosetEvals([evaluate_polynomialcoeff(polynomial_coeff, z) for z in zs])
|
||||
|
||||
# Compute Z(X)
|
||||
denominator_poly = vanishing_polynomialcoeff(zs)
|
||||
|
@ -453,28 +451,28 @@ def verify_cell_kzg_proof_batch_impl(commitments: Sequence[KZGCommitment],
|
|||
# Step 4.1: Compute RLC = sum_i weights[i] commitments[i]
|
||||
# Step 4.1a: Compute weights[i]: the sum of all r^k for which cell k is associated with commitment i.
|
||||
# Note: we do that by iterating over all k and updating the correct weights[i] accordingly
|
||||
weights = [0] * num_commitments
|
||||
weights = [BLSFieldElement(0)] * num_commitments
|
||||
for k in range(num_cells):
|
||||
i = commitment_indices[k]
|
||||
weights[i] = (weights[i] + int(r_powers[k])) % BLS_MODULUS
|
||||
weights[i] += r_powers[k]
|
||||
# Step 4.1b: Linearly combine the weights with the commitments to get RLC
|
||||
rlc = bls.bytes48_to_G1(g1_lincomb(commitments, weights))
|
||||
|
||||
# Step 4.2: Compute RLI = [sum_k r^k interpolation_poly_k(s)]
|
||||
# Note: an efficient implementation would use the IDFT based method explained in the blog post
|
||||
sum_interp_polys_coeff = [0] * n
|
||||
sum_interp_polys_coeff = PolynomialCoeff([BLSFieldElement(0)] * n)
|
||||
for k in range(num_cells):
|
||||
interp_poly_coeff = interpolate_polynomialcoeff(coset_for_cell(cell_indices[k]), cosets_evals[k])
|
||||
interp_poly_scaled_coeff = multiply_polynomialcoeff([r_powers[k]], interp_poly_coeff)
|
||||
interp_poly_scaled_coeff = multiply_polynomialcoeff(PolynomialCoeff([r_powers[k]]), interp_poly_coeff)
|
||||
sum_interp_polys_coeff = add_polynomialcoeff(sum_interp_polys_coeff, interp_poly_scaled_coeff)
|
||||
rli = bls.bytes48_to_G1(g1_lincomb(KZG_SETUP_G1_MONOMIAL[:n], sum_interp_polys_coeff))
|
||||
|
||||
# Step 4.3: Compute RLP = sum_k (r^k * h_k^n) proofs[k]
|
||||
weighted_r_powers = []
|
||||
for k in range(num_cells):
|
||||
h_k = int(coset_shift_for_cell(cell_indices[k]))
|
||||
h_k_pow = pow(h_k, n, BLS_MODULUS)
|
||||
wrp = (int(r_powers[k]) * h_k_pow) % BLS_MODULUS
|
||||
h_k = coset_shift_for_cell(cell_indices[k])
|
||||
h_k_pow = h_k.pow(BLSFieldElement(n))
|
||||
wrp = r_powers[k] * h_k_pow
|
||||
weighted_r_powers.append(wrp)
|
||||
rlp = bls.bytes48_to_G1(g1_lincomb(proofs, weighted_r_powers))
|
||||
|
||||
|
@ -544,7 +542,7 @@ def compute_cells_and_kzg_proofs_polynomialcoeff(polynomial_coeff: PolynomialCoe
|
|||
for i in range(CELLS_PER_EXT_BLOB):
|
||||
coset = coset_for_cell(CellIndex(i))
|
||||
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
|
||||
cells.append(coset_evals_to_cell(ys))
|
||||
cells.append(coset_evals_to_cell(CosetEvals(ys)))
|
||||
proofs.append(proof)
|
||||
return cells, proofs
|
||||
```
|
||||
|
@ -605,7 +603,8 @@ def verify_cell_kzg_proof_batch(commitments_bytes: Sequence[Bytes48],
|
|||
deduplicated_commitments = [bytes_to_kzg_commitment(commitment_bytes)
|
||||
for commitment_bytes in set(commitments_bytes)]
|
||||
# Create indices list mapping initial commitments (that may contain duplicates) to the deduplicated commitments
|
||||
commitment_indices = [deduplicated_commitments.index(commitment_bytes) for commitment_bytes in commitments_bytes]
|
||||
commitment_indices = [CommitmentIndex(deduplicated_commitments.index(commitment_bytes))
|
||||
for commitment_bytes in commitments_bytes]
|
||||
|
||||
cosets_evals = [cell_to_coset_evals(cell) for cell in cells]
|
||||
proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes]
|
||||
|
@ -656,19 +655,19 @@ def construct_vanishing_polynomial(missing_cell_indices: Sequence[CellIndex]) ->
|
|||
|
||||
```python
|
||||
def recover_polynomialcoeff(cell_indices: Sequence[CellIndex],
|
||||
cells: Sequence[Cell]) -> Sequence[BLSFieldElement]:
|
||||
cosets_evals: Sequence[CosetEvals]) -> PolynomialCoeff:
|
||||
"""
|
||||
Recover the polynomial in coefficient form that when evaluated at the roots of unity will give the extended blob.
|
||||
"""
|
||||
# Get the extended domain. This will be referred to as the FFT domain.
|
||||
roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
|
||||
|
||||
# Flatten the cells into evaluations
|
||||
# Flatten the cosets evaluations.
|
||||
# If a cell is missing, then its evaluation is zero.
|
||||
# We let E(x) be a polynomial of degree FIELD_ELEMENTS_PER_EXT_BLOB - 1
|
||||
# that interpolates the evaluations including the zeros for missing ones.
|
||||
extended_evaluation_rbo = [0] * FIELD_ELEMENTS_PER_EXT_BLOB
|
||||
for cell_index, cell in zip(cell_indices, cells):
|
||||
extended_evaluation_rbo = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_EXT_BLOB
|
||||
for cell_index, cell in zip(cell_indices, cosets_evals):
|
||||
start = cell_index * FIELD_ELEMENTS_PER_CELL
|
||||
end = (cell_index + 1) * FIELD_ELEMENTS_PER_CELL
|
||||
extended_evaluation_rbo[start:end] = cell
|
||||
|
@ -686,8 +685,7 @@ def recover_polynomialcoeff(cell_indices: Sequence[CellIndex],
|
|||
# Compute (E*Z)(x) = E(x) * Z(x) in evaluation form over the FFT domain
|
||||
# Note: over the FFT domain, the polynomials (E*Z)(x) and (P*Z)(x) agree, where
|
||||
# P(x) is the polynomial we want to reconstruct (degree FIELD_ELEMENTS_PER_BLOB - 1).
|
||||
extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS)
|
||||
for a, b in zip(zero_poly_eval, extended_evaluation)]
|
||||
extended_evaluation_times_zero = [a * b for a, b in zip(zero_poly_eval, extended_evaluation)]
|
||||
|
||||
# We know that (E*Z)(x) and (P*Z)(x) agree over the FFT domain,
|
||||
# and we know that (P*Z)(x) has degree at most FIELD_ELEMENTS_PER_EXT_BLOB - 1.
|
||||
|
@ -705,12 +703,12 @@ def recover_polynomialcoeff(cell_indices: Sequence[CellIndex],
|
|||
zero_poly_over_coset = coset_fft_field(zero_poly_coeff, roots_of_unity_extended)
|
||||
|
||||
# Compute P(x) = (P*Z)(x) / Z(x) in evaluation form over a coset of the FFT domain
|
||||
reconstructed_poly_over_coset = [div(a, b) for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)]
|
||||
reconstructed_poly_over_coset = [a / b for a, b in zip(extended_evaluations_over_coset, zero_poly_over_coset)]
|
||||
|
||||
# Convert P(x) to coefficient form
|
||||
reconstructed_poly_coeff = coset_fft_field(reconstructed_poly_over_coset, roots_of_unity_extended, inv=True)
|
||||
|
||||
return reconstructed_poly_coeff[:FIELD_ELEMENTS_PER_BLOB]
|
||||
return PolynomialCoeff(reconstructed_poly_coeff[:FIELD_ELEMENTS_PER_BLOB])
|
||||
```
|
||||
|
||||
### `recover_cells_and_kzg_proofs`
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
- [Introduction](#introduction)
|
||||
- [Custom types](#custom-types)
|
||||
- [Cryptographic types](#cryptographic-types)
|
||||
- [Constants](#constants)
|
||||
- [Preset](#preset)
|
||||
- [Blob](#blob)
|
||||
|
@ -27,8 +28,6 @@
|
|||
- [`bytes_to_kzg_proof`](#bytes_to_kzg_proof)
|
||||
- [`blob_to_polynomial`](#blob_to_polynomial)
|
||||
- [`compute_challenge`](#compute_challenge)
|
||||
- [`bls_modular_inverse`](#bls_modular_inverse)
|
||||
- [`div`](#div)
|
||||
- [`g1_lincomb`](#g1_lincomb)
|
||||
- [`compute_powers`](#compute_powers)
|
||||
- [`compute_roots_of_unity`](#compute_roots_of_unity)
|
||||
|
@ -63,12 +62,17 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
|
|||
| - | - | - |
|
||||
| `G1Point` | `Bytes48` | |
|
||||
| `G2Point` | `Bytes96` | |
|
||||
| `BLSFieldElement` | `uint256` | Validation: `x < BLS_MODULUS` |
|
||||
| `KZGCommitment` | `Bytes48` | Validation: Perform [BLS standard's](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-2.5) "KeyValidate" check but do allow the identity point |
|
||||
| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` |
|
||||
| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | A polynomial in evaluation form |
|
||||
| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | A basic data blob |
|
||||
|
||||
## Cryptographic types
|
||||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| [`BLSFieldElement`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/deneb.py#L18-L19) | `uint256` | <!-- predefined-type --> A value in the finite field defined by `BLS_MODULUS` |
|
||||
| [`Polynomial`](https://github.com/ethereum/consensus-specs/blob/36a5719b78523c057065515c8f8fcaeba75d065b/pysetup/spec_builders/deneb.py#L22-L28) | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | <!-- predefined-type --> A polynomial in evaluation form |
|
||||
|
||||
## Constants
|
||||
|
||||
| Name | Value | Notes |
|
||||
|
@ -82,7 +86,6 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
|
|||
| `KZG_ENDIANNESS` | `'big'` | The endianness of the field elements including blobs |
|
||||
| `PRIMITIVE_ROOT_OF_UNITY` | `7` | The primitive root of unity from which all roots of unity should be derived |
|
||||
|
||||
|
||||
## Preset
|
||||
|
||||
### Blob
|
||||
|
@ -188,7 +191,7 @@ def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
|
|||
|
||||
```python
|
||||
def bls_field_to_bytes(x: BLSFieldElement) -> Bytes32:
|
||||
return int.to_bytes(x % BLS_MODULUS, 32, KZG_ENDIANNESS)
|
||||
return int.to_bytes(int(x), 32, KZG_ENDIANNESS)
|
||||
```
|
||||
|
||||
#### `validate_kzg_g1`
|
||||
|
@ -243,8 +246,7 @@ def blob_to_polynomial(blob: Blob) -> Polynomial:
|
|||
#### `compute_challenge`
|
||||
|
||||
```python
|
||||
def compute_challenge(blob: Blob,
|
||||
commitment: KZGCommitment) -> BLSFieldElement:
|
||||
def compute_challenge(blob: Blob, commitment: KZGCommitment) -> BLSFieldElement:
|
||||
"""
|
||||
Return the Fiat-Shamir challenge required by the rest of the protocol.
|
||||
"""
|
||||
|
@ -260,28 +262,6 @@ def compute_challenge(blob: Blob,
|
|||
return hash_to_bls_field(data)
|
||||
```
|
||||
|
||||
#### `bls_modular_inverse`
|
||||
|
||||
```python
|
||||
def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
|
||||
"""
|
||||
Compute the modular inverse of x (for x != 0)
|
||||
i.e. return y such that x * y % BLS_MODULUS == 1
|
||||
"""
|
||||
assert (int(x) % BLS_MODULUS) != 0
|
||||
return BLSFieldElement(pow(x, -1, BLS_MODULUS))
|
||||
```
|
||||
|
||||
#### `div`
|
||||
|
||||
```python
|
||||
def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement:
|
||||
"""
|
||||
Divide two field elements: ``x`` by `y``.
|
||||
"""
|
||||
return BLSFieldElement((int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS)
|
||||
```
|
||||
|
||||
#### `g1_lincomb`
|
||||
|
||||
```python
|
||||
|
@ -309,11 +289,11 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
|
|||
"""
|
||||
Return ``x`` to power of [0, n-1], if n > 0. When n==0, an empty array is returned.
|
||||
"""
|
||||
current_power = 1
|
||||
current_power = BLSFieldElement(1)
|
||||
powers = []
|
||||
for _ in range(n):
|
||||
powers.append(BLSFieldElement(current_power))
|
||||
current_power = current_power * int(x) % BLS_MODULUS
|
||||
powers.append(current_power)
|
||||
current_power = current_power * x
|
||||
return powers
|
||||
```
|
||||
|
||||
|
@ -334,8 +314,7 @@ def compute_roots_of_unity(order: uint64) -> Sequence[BLSFieldElement]:
|
|||
#### `evaluate_polynomial_in_evaluation_form`
|
||||
|
||||
```python
|
||||
def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
|
||||
z: BLSFieldElement) -> BLSFieldElement:
|
||||
def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, z: BLSFieldElement) -> BLSFieldElement:
|
||||
"""
|
||||
Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``.
|
||||
- When ``z`` is in the domain, the evaluation can be found by indexing the polynomial at the
|
||||
|
@ -345,22 +324,23 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
|
|||
"""
|
||||
width = len(polynomial)
|
||||
assert width == FIELD_ELEMENTS_PER_BLOB
|
||||
inverse_width = bls_modular_inverse(BLSFieldElement(width))
|
||||
inverse_width = BLSFieldElement(width).inverse()
|
||||
|
||||
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
|
||||
|
||||
# If we are asked to evaluate within the domain, we already know the answer
|
||||
if z in roots_of_unity_brp:
|
||||
eval_index = roots_of_unity_brp.index(z)
|
||||
return BLSFieldElement(polynomial[eval_index])
|
||||
return polynomial[eval_index]
|
||||
|
||||
result = 0
|
||||
result = BLSFieldElement(0)
|
||||
for i in range(width):
|
||||
a = BLSFieldElement(int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS)
|
||||
b = BLSFieldElement((int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS)
|
||||
result += int(div(a, b) % BLS_MODULUS)
|
||||
result = result * int(BLS_MODULUS + pow(z, width, BLS_MODULUS) - 1) * int(inverse_width)
|
||||
return BLSFieldElement(result % BLS_MODULUS)
|
||||
a = polynomial[i] * roots_of_unity_brp[i]
|
||||
b = z - roots_of_unity_brp[i]
|
||||
result += a / b
|
||||
r = z.pow(BLSFieldElement(width)) - BLSFieldElement(1)
|
||||
result = result * r * inverse_width
|
||||
return result
|
||||
```
|
||||
|
||||
### KZG
|
||||
|
@ -415,9 +395,9 @@ def verify_kzg_proof_impl(commitment: KZGCommitment,
|
|||
# Verify: P - y = Q * (X - z)
|
||||
X_minus_z = bls.add(
|
||||
bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]),
|
||||
bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS),
|
||||
bls.multiply(bls.G2(), -z),
|
||||
)
|
||||
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
|
||||
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), -y))
|
||||
return bls.pairing_check([
|
||||
[P_minus_y, bls.neg(bls.G2())],
|
||||
[bls.bytes48_to_G1(proof), X_minus_z]
|
||||
|
@ -445,10 +425,7 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
|
|||
|
||||
# Append all inputs to the transcript before we hash
|
||||
for commitment, z, y, proof in zip(commitments, zs, ys, proofs):
|
||||
data += commitment \
|
||||
+ int.to_bytes(z, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \
|
||||
+ int.to_bytes(y, BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS) \
|
||||
+ proof
|
||||
data += commitment + bls_field_to_bytes(z) + bls_field_to_bytes(y) + proof
|
||||
|
||||
r = hash_to_bls_field(data)
|
||||
r_powers = compute_powers(r, len(commitments))
|
||||
|
@ -456,11 +433,8 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
|
|||
# Verify: e(sum r^i proof_i, [s]) ==
|
||||
# e(sum r^i (commitment_i - [y_i]) + sum r^i z_i proof_i, [1])
|
||||
proof_lincomb = g1_lincomb(proofs, r_powers)
|
||||
proof_z_lincomb = g1_lincomb(
|
||||
proofs,
|
||||
[BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)],
|
||||
)
|
||||
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
|
||||
proof_z_lincomb = g1_lincomb(proofs, [z * r_power for z, r_power in zip(zs, r_powers)])
|
||||
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), -y))
|
||||
for commitment, y in zip(commitments, ys)]
|
||||
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
|
||||
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
|
||||
|
@ -484,7 +458,7 @@ def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]:
|
|||
assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT
|
||||
polynomial = blob_to_polynomial(blob)
|
||||
proof, y = compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z_bytes))
|
||||
return proof, y.to_bytes(BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS)
|
||||
return proof, int(y).to_bytes(BYTES_PER_FIELD_ELEMENT, KZG_ENDIANNESS)
|
||||
```
|
||||
|
||||
#### `compute_quotient_eval_within_domain`
|
||||
|
@ -492,8 +466,7 @@ def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]:
|
|||
```python
|
||||
def compute_quotient_eval_within_domain(z: BLSFieldElement,
|
||||
polynomial: Polynomial,
|
||||
y: BLSFieldElement
|
||||
) -> BLSFieldElement:
|
||||
y: BLSFieldElement) -> BLSFieldElement:
|
||||
"""
|
||||
Given `y == p(z)` for a polynomial `p(x)`, compute `q(z)`: the KZG quotient polynomial evaluated at `z` for the
|
||||
special case where `z` is in roots of unity.
|
||||
|
@ -502,17 +475,17 @@ def compute_quotient_eval_within_domain(z: BLSFieldElement,
|
|||
when one of the points is zero". The code below computes q(x_m) for the roots of unity special case.
|
||||
"""
|
||||
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
|
||||
result = 0
|
||||
result = BLSFieldElement(0)
|
||||
for i, omega_i in enumerate(roots_of_unity_brp):
|
||||
if omega_i == z: # skip the evaluation point in the sum
|
||||
continue
|
||||
|
||||
f_i = int(BLS_MODULUS) + int(polynomial[i]) - int(y) % BLS_MODULUS
|
||||
numerator = f_i * int(omega_i) % BLS_MODULUS
|
||||
denominator = int(z) * (int(BLS_MODULUS) + int(z) - int(omega_i)) % BLS_MODULUS
|
||||
result += int(div(BLSFieldElement(numerator), BLSFieldElement(denominator)))
|
||||
f_i = polynomial[i] - y
|
||||
numerator = f_i * omega_i
|
||||
denominator = z * (z - omega_i)
|
||||
result += numerator / denominator
|
||||
|
||||
return BLSFieldElement(result % BLS_MODULUS)
|
||||
return result
|
||||
```
|
||||
|
||||
#### `compute_kzg_proof_impl`
|
||||
|
@ -526,21 +499,20 @@ def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> Tuple[
|
|||
|
||||
# For all x_i, compute p(x_i) - p(z)
|
||||
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
|
||||
polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial]
|
||||
polynomial_shifted = [p - y for p in polynomial]
|
||||
|
||||
# For all x_i, compute (x_i - z)
|
||||
denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS)
|
||||
for x in roots_of_unity_brp]
|
||||
denominator_poly = [x - z for x in roots_of_unity_brp]
|
||||
|
||||
# Compute the quotient polynomial directly in evaluation form
|
||||
quotient_polynomial = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB
|
||||
for i, (a, b) in enumerate(zip(polynomial_shifted, denominator_poly)):
|
||||
if b == 0:
|
||||
if b == BLSFieldElement(0):
|
||||
# The denominator is zero hence `z` is a root of unity: we must handle it as a special case
|
||||
quotient_polynomial[i] = compute_quotient_eval_within_domain(roots_of_unity_brp[i], polynomial, y)
|
||||
else:
|
||||
# Compute: q(x_i) = (p(x_i) - p(z)) / (x_i - z).
|
||||
quotient_polynomial[i] = div(a, b)
|
||||
quotient_polynomial[i] = a / b
|
||||
|
||||
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), quotient_polynomial)), y
|
||||
```
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
- [New `get_balance_churn_limit`](#new-get_balance_churn_limit)
|
||||
- [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit)
|
||||
- [New `get_consolidation_churn_limit`](#new-get_consolidation_churn_limit)
|
||||
- [New `get_active_balance`](#new-get_active_balance)
|
||||
- [New `get_pending_balance_to_withdraw`](#new-get_pending_balance_to_withdraw)
|
||||
- [Modified `get_attesting_indices`](#modified-get_attesting_indices)
|
||||
- [Modified `get_next_sync_committee_indices`](#modified-get_next_sync_committee_indices)
|
||||
|
@ -61,7 +60,6 @@
|
|||
- [Modified `initiate_validator_exit`](#modified-initiate_validator_exit)
|
||||
- [New `switch_to_compounding_validator`](#new-switch_to_compounding_validator)
|
||||
- [New `queue_excess_active_balance`](#new-queue_excess_active_balance)
|
||||
- [New `queue_entire_balance_and_reset_validator`](#new-queue_entire_balance_and_reset_validator)
|
||||
- [New `compute_exit_epoch_and_update_churn`](#new-compute_exit_epoch_and_update_churn)
|
||||
- [New `compute_consolidation_epoch_and_update_churn`](#new-compute_consolidation_epoch_and_update_churn)
|
||||
- [Modified `slash_validator`](#modified-slash_validator)
|
||||
|
@ -539,14 +537,6 @@ def get_consolidation_churn_limit(state: BeaconState) -> Gwei:
|
|||
return get_balance_churn_limit(state) - get_activation_exit_churn_limit(state)
|
||||
```
|
||||
|
||||
#### New `get_active_balance`
|
||||
|
||||
```python
|
||||
def get_active_balance(state: BeaconState, validator_index: ValidatorIndex) -> Gwei:
|
||||
max_effective_balance = get_max_effective_balance(state.validators[validator_index])
|
||||
return min(state.balances[validator_index], max_effective_balance)
|
||||
```
|
||||
|
||||
#### New `get_pending_balance_to_withdraw`
|
||||
|
||||
```python
|
||||
|
@ -655,20 +645,6 @@ def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> No
|
|||
)
|
||||
```
|
||||
|
||||
#### New `queue_entire_balance_and_reset_validator`
|
||||
|
||||
```python
|
||||
def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
||||
balance = state.balances[index]
|
||||
state.balances[index] = 0
|
||||
validator = state.validators[index]
|
||||
validator.effective_balance = 0
|
||||
validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH
|
||||
state.pending_balance_deposits.append(
|
||||
PendingBalanceDeposit(index=index, amount=balance)
|
||||
)
|
||||
```
|
||||
|
||||
#### New `compute_exit_epoch_and_update_churn`
|
||||
|
||||
```python
|
||||
|
@ -884,10 +860,14 @@ def process_pending_consolidations(state: BeaconState) -> None:
|
|||
|
||||
# Churn any target excess active balance of target and raise its max
|
||||
switch_to_compounding_validator(state, pending_consolidation.target_index)
|
||||
|
||||
# Calculate the consolidated balance
|
||||
max_effective_balance = get_max_effective_balance(source_validator)
|
||||
source_effective_balance = min(state.balances[pending_consolidation.source_index], max_effective_balance)
|
||||
|
||||
# Move active balance to target. Excess balance is withdrawable.
|
||||
active_balance = get_active_balance(state, pending_consolidation.source_index)
|
||||
decrease_balance(state, pending_consolidation.source_index, active_balance)
|
||||
increase_balance(state, pending_consolidation.target_index, active_balance)
|
||||
decrease_balance(state, pending_consolidation.source_index, source_effective_balance)
|
||||
increase_balance(state, pending_consolidation.target_index, source_effective_balance)
|
||||
next_pending_consolidation += 1
|
||||
|
||||
state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:]
|
||||
|
@ -906,16 +886,13 @@ def process_effective_balance_updates(state: BeaconState) -> None:
|
|||
DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
||||
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
||||
# [Modified in Electra:EIP7251]
|
||||
EFFECTIVE_BALANCE_LIMIT = (
|
||||
MAX_EFFECTIVE_BALANCE_ELECTRA if has_compounding_withdrawal_credential(validator)
|
||||
else MIN_ACTIVATION_BALANCE
|
||||
)
|
||||
max_effective_balance = get_max_effective_balance(validator)
|
||||
|
||||
if (
|
||||
balance + DOWNWARD_THRESHOLD < validator.effective_balance
|
||||
or validator.effective_balance + UPWARD_THRESHOLD < balance
|
||||
):
|
||||
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, EFFECTIVE_BALANCE_LIMIT)
|
||||
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance)
|
||||
```
|
||||
|
||||
### Execution engine
|
||||
|
@ -1007,6 +984,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||
withdrawal_index = state.next_withdrawal_index
|
||||
validator_index = state.next_withdrawal_validator_index
|
||||
withdrawals: List[Withdrawal] = []
|
||||
partial_withdrawals_count = 0
|
||||
|
||||
# [New in Electra:EIP7251] Consume pending partial withdrawals
|
||||
for withdrawal in state.pending_partial_withdrawals:
|
||||
|
@ -1026,7 +1004,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
|
|||
))
|
||||
withdrawal_index += WithdrawalIndex(1)
|
||||
|
||||
partial_withdrawals_count = len(withdrawals)
|
||||
partial_withdrawals_count += 1
|
||||
|
||||
# Sweep for remaining.
|
||||
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
||||
|
|
|
@ -152,7 +152,14 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
|
|||
))
|
||||
|
||||
for index in pre_activation:
|
||||
queue_entire_balance_and_reset_validator(post, ValidatorIndex(index))
|
||||
balance = post.balances[index]
|
||||
post.balances[index] = 0
|
||||
validator = post.validators[index]
|
||||
validator.effective_balance = 0
|
||||
validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH
|
||||
post.pending_balance_deposits.append(
|
||||
PendingBalanceDeposit(index=index, amount=balance)
|
||||
)
|
||||
|
||||
# Ensure early adopters of compounding credentials go through the activation churn
|
||||
for index, validator in enumerate(post.validators):
|
||||
|
|
|
@ -35,6 +35,8 @@ The `beacon_block` topic is modified to also support Electra blocks.
|
|||
|
||||
The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of the new attestation type.
|
||||
|
||||
The `attester_slashing` topic is modified to support the gossip of the new `AttesterSlashing` type.
|
||||
|
||||
The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here.
|
||||
|
||||
The derivation of the `message-id` remains stable.
|
||||
|
|
|
@ -263,7 +263,7 @@ We similarly define "summary types" and "expansion types". For example, [`Beacon
|
|||
|
||||
## Implementations
|
||||
|
||||
See https://github.com/ethereum/eth2.0-specs/issues/2138 for a list of current known implementations.
|
||||
See https://github.com/ethereum/consensus-specs/issues/2138 for a list of current known implementations.
|
||||
|
||||
## JSON mapping
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import random
|
|||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
expect_assertion_error,
|
||||
with_presets,
|
||||
with_capella_and_later,
|
||||
)
|
||||
|
@ -24,80 +23,10 @@ from eth2spec.test.helpers.withdrawals import (
|
|||
set_eth1_withdrawal_credential_with_balance,
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
run_withdrawals_processing,
|
||||
)
|
||||
|
||||
|
||||
def verify_post_state(state, spec, expected_withdrawals,
|
||||
fully_withdrawable_indices, partial_withdrawals_indices):
|
||||
# Consider verifying also the condition when no withdrawals are expected.
|
||||
if len(expected_withdrawals) == 0:
|
||||
return
|
||||
|
||||
expected_withdrawals_validator_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals]
|
||||
assert state.next_withdrawal_index == expected_withdrawals[-1].index + 1
|
||||
|
||||
if len(expected_withdrawals) == spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
# NOTE: ideally we would also check in the case with
|
||||
# fewer than maximum withdrawals but that requires the pre-state info
|
||||
next_withdrawal_validator_index = (expected_withdrawals_validator_indices[-1] + 1) % len(state.validators)
|
||||
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index
|
||||
|
||||
for index in fully_withdrawable_indices:
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == 0
|
||||
else:
|
||||
assert state.balances[index] > 0
|
||||
for index in partial_withdrawals_indices:
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == spec.MAX_EFFECTIVE_BALANCE
|
||||
else:
|
||||
assert state.balances[index] > spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
|
||||
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None,
|
||||
fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True):
|
||||
"""
|
||||
Run ``process_withdrawals``, yielding:
|
||||
- pre-state ('pre')
|
||||
- execution payload ('execution_payload')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
expected_withdrawals = get_expected_withdrawals(spec, state)
|
||||
assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(expected_withdrawals) == num_expected_withdrawals
|
||||
|
||||
pre_state = state.copy()
|
||||
yield 'pre', state
|
||||
yield 'execution_payload', execution_payload
|
||||
|
||||
if not valid:
|
||||
expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload))
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
spec.process_withdrawals(state, execution_payload)
|
||||
|
||||
yield 'post', state
|
||||
|
||||
if len(expected_withdrawals) == 0:
|
||||
next_withdrawal_validator_index = (
|
||||
pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
||||
)
|
||||
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index % len(state.validators)
|
||||
elif len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
bound = min(spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
assert len(get_expected_withdrawals(spec, state)) <= bound
|
||||
elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD')
|
||||
|
||||
if fully_withdrawable_indices is not None or partial_withdrawals_indices is not None:
|
||||
verify_post_state(state, spec, expected_withdrawals, fully_withdrawable_indices, partial_withdrawals_indices)
|
||||
|
||||
return expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_zero_expected_withdrawals(spec, state):
|
||||
|
|
|
@ -39,7 +39,7 @@ def test_verify_kzg_proof(spec):
|
|||
"""
|
||||
Test the wrapper functions (taking bytes arguments) for computing and verifying KZG proofs.
|
||||
"""
|
||||
x = spec.bls_field_to_bytes(3)
|
||||
x = spec.bls_field_to_bytes(spec.BLSFieldElement(3))
|
||||
blob = get_sample_blob(spec)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
proof, y = spec.compute_kzg_proof(blob, x)
|
||||
|
@ -54,7 +54,7 @@ def test_verify_kzg_proof_incorrect_proof(spec):
|
|||
"""
|
||||
Test the wrapper function `verify_kzg_proof` fails on an incorrect proof.
|
||||
"""
|
||||
x = spec.bls_field_to_bytes(3465)
|
||||
x = spec.bls_field_to_bytes(spec.BLSFieldElement(3465))
|
||||
blob = get_sample_blob(spec)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
proof, y = spec.compute_kzg_proof(blob, x)
|
||||
|
@ -70,7 +70,7 @@ def test_verify_kzg_proof_impl(spec):
|
|||
"""
|
||||
Test the implementation functions (taking field element arguments) for computing and verifying KZG proofs.
|
||||
"""
|
||||
x = BLS_MODULUS - 1
|
||||
x = spec.BLSFieldElement(BLS_MODULUS - 1)
|
||||
blob = get_sample_blob(spec)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
polynomial = spec.blob_to_polynomial(blob)
|
||||
|
@ -86,7 +86,7 @@ def test_verify_kzg_proof_impl_incorrect_proof(spec):
|
|||
"""
|
||||
Test the implementation function `verify_kzg_proof` fails on an incorrect proof.
|
||||
"""
|
||||
x = 324561
|
||||
x = spec.BLSFieldElement(324561)
|
||||
blob = get_sample_blob(spec)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
polynomial = spec.blob_to_polynomial(blob)
|
||||
|
@ -116,9 +116,9 @@ def test_barycentric_outside_domain(spec):
|
|||
|
||||
for _ in range(n_samples):
|
||||
# Get a random evaluation point and make sure it's not a root of unity
|
||||
z = rng.randint(0, BLS_MODULUS - 1)
|
||||
z = spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1))
|
||||
while z in roots_of_unity_brp:
|
||||
z = rng.randint(0, BLS_MODULUS - 1)
|
||||
z = spec.BLSFieldElement(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)
|
||||
|
@ -152,7 +152,7 @@ def test_barycentric_within_domain(spec):
|
|||
for i in range(12):
|
||||
i = rng.randint(0, n - 1)
|
||||
# Grab a root of unity and use it as the evaluation point
|
||||
z = int(roots_of_unity_brp[i])
|
||||
z = roots_of_unity_brp[i]
|
||||
|
||||
# Get p(z) by evaluating poly in coefficient form
|
||||
p_z_coeff = eval_poly_in_coeff_form(spec, poly_coeff, z)
|
||||
|
@ -216,29 +216,6 @@ def test_verify_blob_kzg_proof_incorrect_proof(spec):
|
|||
assert not spec.verify_blob_kzg_proof(blob, commitment, proof)
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_test
|
||||
@single_phase
|
||||
def test_bls_modular_inverse(spec):
|
||||
"""
|
||||
Verify computation of multiplicative inverse
|
||||
"""
|
||||
rng = random.Random(5566)
|
||||
|
||||
# Should fail for x == 0
|
||||
expect_assertion_error(lambda: spec.bls_modular_inverse(0))
|
||||
expect_assertion_error(lambda: spec.bls_modular_inverse(spec.BLS_MODULUS))
|
||||
expect_assertion_error(lambda: spec.bls_modular_inverse(2 * spec.BLS_MODULUS))
|
||||
|
||||
# Test a trivial inversion
|
||||
assert 1 == int(spec.bls_modular_inverse(1))
|
||||
|
||||
# Test a random inversion
|
||||
r = rng.randint(0, spec.BLS_MODULUS - 1)
|
||||
r_inv = int(spec.bls_modular_inverse(r))
|
||||
assert r * r_inv % BLS_MODULUS == 1
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_test
|
||||
@single_phase
|
||||
|
|
|
@ -29,7 +29,7 @@ def test_fft(spec):
|
|||
roots_of_unity = spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)
|
||||
|
||||
# sample a random polynomial
|
||||
poly_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
poly_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
|
||||
# do an FFT and then an inverse FFT
|
||||
poly_eval = spec.fft_field(poly_coeff, roots_of_unity)
|
||||
|
@ -63,10 +63,10 @@ def test_coset_fft(spec):
|
|||
roots_of_unity = spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)
|
||||
|
||||
# this is the shift that generates the coset
|
||||
coset_shift = spec.PRIMITIVE_ROOT_OF_UNITY
|
||||
coset_shift = spec.BLSFieldElement(spec.PRIMITIVE_ROOT_OF_UNITY)
|
||||
|
||||
# sample a random polynomial
|
||||
poly_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
poly_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
|
||||
# do a coset FFT and then an inverse coset FFT
|
||||
poly_eval = spec.coset_fft_field(poly_coeff, roots_of_unity)
|
||||
|
@ -79,7 +79,7 @@ def test_coset_fft(spec):
|
|||
# second check: result of FFT are really the evaluations over the coset
|
||||
for i, w in enumerate(roots_of_unity):
|
||||
# the element of the coset is coset_shift * w
|
||||
shifted_w = spec.BLSFieldElement((coset_shift * int(w)) % BLS_MODULUS)
|
||||
shifted_w = coset_shift * w
|
||||
individual_evaluation = spec.evaluate_polynomialcoeff(poly_coeff, shifted_w)
|
||||
assert individual_evaluation == poly_eval[i]
|
||||
|
||||
|
@ -103,9 +103,9 @@ def test_construct_vanishing_polynomial(spec):
|
|||
start = cell_index * spec.FIELD_ELEMENTS_PER_CELL
|
||||
end = (cell_index + 1) * spec.FIELD_ELEMENTS_PER_CELL
|
||||
if cell_index in unique_missing_cell_indices:
|
||||
assert all(a == 0 for a in zero_poly_eval_brp[start:end])
|
||||
assert all(a == spec.BLSFieldElement(0) for a in zero_poly_eval_brp[start:end])
|
||||
else: # cell_index in cell_indices
|
||||
assert all(a != 0 for a in zero_poly_eval_brp[start:end])
|
||||
assert all(a != spec.BLSFieldElement(0) for a in zero_poly_eval_brp[start:end])
|
||||
|
||||
|
||||
@with_eip7594_and_later
|
||||
|
@ -182,6 +182,7 @@ def test_verify_cell_kzg_proof_batch_invalid(spec):
|
|||
blob = get_sample_blob(spec)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
cells, proofs = spec.compute_cells_and_kzg_proofs(blob)
|
||||
return
|
||||
|
||||
assert len(cells) == len(proofs)
|
||||
|
||||
|
@ -274,10 +275,11 @@ def test_multiply_polynomial_degree_overflow(spec):
|
|||
rng = random.Random(5566)
|
||||
|
||||
# Perform a legitimate-but-maxed-out polynomial multiplication
|
||||
poly1_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
poly2_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
poly1_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
poly2_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
_ = spec.multiply_polynomialcoeff(poly1_coeff, poly2_coeff)
|
||||
|
||||
# Now overflow the degree by pumping the degree of one of the inputs by one
|
||||
poly2_coeff = [rng.randint(0, BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB + 1)]
|
||||
poly2_coeff = [spec.BLSFieldElement(rng.randint(0, BLS_MODULUS - 1))
|
||||
for _ in range(spec.FIELD_ELEMENTS_PER_BLOB + 1)]
|
||||
expect_assertion_error(lambda: spec.multiply_polynomialcoeff(poly1_coeff, poly2_coeff))
|
||||
|
|
|
@ -658,6 +658,8 @@ def test_insufficient_effective_balance(spec, state):
|
|||
state.validators[
|
||||
validator_index
|
||||
].effective_balance -= spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
# Make sure validator has enough balance to withdraw
|
||||
state.balances[validator_index] += spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
|
||||
withdrawal_request = spec.WithdrawalRequest(
|
||||
|
@ -787,6 +789,33 @@ def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
|
|||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_insufficient_balance(spec, state):
|
||||
rng = random.Random(1361)
|
||||
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
|
||||
validator_pubkey = state.validators[validator_index].pubkey
|
||||
address = b"\x22" * 20
|
||||
amount = spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
# Validator will not be able to partial withdrawal because MIN_ACTIVATION_BALANCE + amount > balance
|
||||
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
|
||||
withdrawal_request = spec.WithdrawalRequest(
|
||||
source_address=address,
|
||||
validator_pubkey=validator_pubkey,
|
||||
amount=amount,
|
||||
)
|
||||
|
||||
yield from run_withdrawal_request_processing(
|
||||
spec,
|
||||
state,
|
||||
withdrawal_request,
|
||||
success=False,
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_full_exit_request_has_partial_withdrawal(spec, state):
|
||||
|
@ -821,7 +850,6 @@ def test_full_exit_request_has_partial_withdrawal(spec, state):
|
|||
spec, state, withdrawal_request, success=False
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Run processing
|
||||
#
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
import random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_electra_and_later,
|
||||
)
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
prepare_expected_withdrawals_compounding,
|
||||
run_withdrawals_processing,
|
||||
set_compounding_withdrawal_credential_with_balance,
|
||||
prepare_pending_withdrawal,
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_success_mixed_fully_and_partial_withdrawable_compounding(spec, state):
|
||||
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
|
||||
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals_compounding(
|
||||
spec, state,
|
||||
rng=random.Random(42),
|
||||
num_full_withdrawals=num_full_withdrawals,
|
||||
num_partial_withdrawals_sweep=num_partial_withdrawals,
|
||||
)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_max_effective_balance_compounding(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator's effective balance must be maxed out
|
||||
effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA - spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance)
|
||||
|
||||
validator = state.validators[validator_index]
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_excess_balance_compounding(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs an excess balance
|
||||
effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
|
||||
set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance)
|
||||
|
||||
validator = state.validators[validator_index]
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_success_excess_balance_but_no_max_effective_balance_compounding(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance
|
||||
effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA - spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
set_compounding_withdrawal_credential_with_balance(spec, state, validator_index, effective_balance, balance)
|
||||
|
||||
validator = state.validators[validator_index]
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_pending_withdrawals_one_skipped_one_effective(spec, state):
|
||||
index_0 = 3
|
||||
index_1 = 5
|
||||
|
||||
withdrawal_0 = prepare_pending_withdrawal(spec, state, index_0)
|
||||
withdrawal_1 = prepare_pending_withdrawal(spec, state, index_1)
|
||||
|
||||
# If validator doesn't have an excess balance pending withdrawal is skipped
|
||||
state.balances[index_0] = spec.MIN_ACTIVATION_BALANCE
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
assert state.pending_partial_withdrawals == [withdrawal_0, withdrawal_1]
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
assert state.pending_partial_withdrawals == []
|
|
@ -1 +1,4 @@
|
|||
# This is a "hack" which allows other test files (e.g., test_deposit_transition.py)
|
||||
# to reuse the sanity/block test format.
|
||||
from .test_blocks import * # noqa: F401 F403
|
||||
from .test_deposit_transition import * # noqa: F401 F403
|
||||
|
|
|
@ -71,10 +71,10 @@ def eval_poly_in_coeff_form(spec, coeffs, x):
|
|||
"""
|
||||
Evaluate a polynomial in coefficient form at 'x' using Horner's rule
|
||||
"""
|
||||
total = 0
|
||||
total = spec.BLSFieldElement(0)
|
||||
for a in reversed(coeffs):
|
||||
total = (total * x + a) % spec.BLS_MODULUS
|
||||
return total % spec.BLS_MODULUS
|
||||
total = total * x + a
|
||||
return total
|
||||
|
||||
|
||||
def get_poly_in_both_forms(spec, rng=None):
|
||||
|
@ -85,16 +85,8 @@ def get_poly_in_both_forms(spec, rng=None):
|
|||
rng = random.Random(5566)
|
||||
|
||||
roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB))
|
||||
|
||||
coeffs = [
|
||||
rng.randint(0, spec.BLS_MODULUS - 1)
|
||||
for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)
|
||||
]
|
||||
|
||||
evals = [
|
||||
eval_poly_in_coeff_form(spec, coeffs, int(z))
|
||||
for z in roots_of_unity_brp
|
||||
]
|
||||
coeffs = [spec.BLSFieldElement(rng.randint(0, spec.BLS_MODULUS - 1)) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)]
|
||||
evals = [eval_poly_in_coeff_form(spec, coeffs, z) for z in roots_of_unity_brp]
|
||||
|
||||
return coeffs, evals
|
||||
|
||||
|
|
|
@ -41,14 +41,17 @@ def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance=None
|
|||
|
||||
|
||||
def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000):
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance)
|
||||
validator = state.validators[index]
|
||||
if is_post_electra(spec) and spec.has_compounding_withdrawal_credential(validator):
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
|
||||
state.balances[index] = validator.effective_balance + excess_balance
|
||||
else:
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance)
|
||||
|
||||
assert spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
assert spec.is_partially_withdrawable_validator(state.validators[index], state.balances[index])
|
||||
|
||||
|
||||
def prepare_expected_withdrawals(spec, state, rng,
|
||||
num_full_withdrawals=0, num_partial_withdrawals=0):
|
||||
def sample_withdrawal_indices(spec, state, rng, num_full_withdrawals, num_partial_withdrawals):
|
||||
bound = min(len(state.validators), spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
||||
assert num_full_withdrawals + num_partial_withdrawals <= bound
|
||||
eligible_validator_indices = list(range(bound))
|
||||
|
@ -56,6 +59,15 @@ def prepare_expected_withdrawals(spec, state, rng,
|
|||
fully_withdrawable_indices = rng.sample(sampled_indices, num_full_withdrawals)
|
||||
partial_withdrawals_indices = list(set(sampled_indices).difference(set(fully_withdrawable_indices)))
|
||||
|
||||
return fully_withdrawable_indices, partial_withdrawals_indices
|
||||
|
||||
|
||||
def prepare_expected_withdrawals(spec, state, rng,
|
||||
num_full_withdrawals=0, num_partial_withdrawals=0):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = sample_withdrawal_indices(
|
||||
spec, state, rng, num_full_withdrawals, num_partial_withdrawals
|
||||
)
|
||||
|
||||
for index in fully_withdrawable_indices:
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
for index in partial_withdrawals_indices:
|
||||
|
@ -70,3 +82,142 @@ def set_compounding_withdrawal_credential(spec, state, index, address=None):
|
|||
|
||||
validator = state.validators[index]
|
||||
validator.withdrawal_credentials = spec.COMPOUNDING_WITHDRAWAL_PREFIX + b'\x00' * 11 + address
|
||||
|
||||
|
||||
def set_compounding_withdrawal_credential_with_balance(spec, state, index,
|
||||
effective_balance=None, balance=None, address=None):
|
||||
set_compounding_withdrawal_credential(spec, state, index, address)
|
||||
|
||||
if effective_balance is None:
|
||||
effective_balance = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
|
||||
if balance is None:
|
||||
balance = effective_balance
|
||||
|
||||
state.validators[index].effective_balance = effective_balance
|
||||
state.balances[index] = balance
|
||||
|
||||
|
||||
def prepare_expected_withdrawals_compounding(spec, state, rng,
|
||||
num_full_withdrawals=0,
|
||||
num_partial_withdrawals_sweep=0,
|
||||
excess_balance=1000000000):
|
||||
assert is_post_electra(spec)
|
||||
|
||||
fully_withdrawable_indices, partial_withdrawals_sweep_indices = sample_withdrawal_indices(
|
||||
spec, state, rng, num_full_withdrawals, num_partial_withdrawals_sweep
|
||||
)
|
||||
|
||||
for index in fully_withdrawable_indices + partial_withdrawals_sweep_indices:
|
||||
address = state.validators[index].withdrawal_credentials[12:]
|
||||
set_compounding_withdrawal_credential_with_balance(spec, state, index, address=address)
|
||||
|
||||
for index in fully_withdrawable_indices:
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
for index in partial_withdrawals_sweep_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index)
|
||||
|
||||
return fully_withdrawable_indices, partial_withdrawals_sweep_indices
|
||||
|
||||
|
||||
def prepare_pending_withdrawal(spec, state, validator_index,
|
||||
effective_balance=32_000_000_000, amount=1_000_000_000):
|
||||
assert is_post_electra(spec)
|
||||
|
||||
balance = effective_balance + amount
|
||||
set_compounding_withdrawal_credential_with_balance(
|
||||
spec, state, validator_index, effective_balance, balance
|
||||
)
|
||||
|
||||
withdrawal = spec.PendingPartialWithdrawal(
|
||||
index=validator_index,
|
||||
amount=amount,
|
||||
withdrawable_epoch=spec.get_current_epoch(state),
|
||||
)
|
||||
state.pending_partial_withdrawals.append(withdrawal)
|
||||
|
||||
return withdrawal
|
||||
|
||||
#
|
||||
# Run processing
|
||||
#
|
||||
|
||||
|
||||
def verify_post_state(state, spec, expected_withdrawals,
|
||||
fully_withdrawable_indices, partial_withdrawals_indices):
|
||||
# Consider verifying also the condition when no withdrawals are expected.
|
||||
if len(expected_withdrawals) == 0:
|
||||
return
|
||||
|
||||
expected_withdrawals_validator_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals]
|
||||
assert state.next_withdrawal_index == expected_withdrawals[-1].index + 1
|
||||
|
||||
if len(expected_withdrawals) == spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
# NOTE: ideally we would also check in the case with
|
||||
# fewer than maximum withdrawals but that requires the pre-state info
|
||||
next_withdrawal_validator_index = (expected_withdrawals_validator_indices[-1] + 1) % len(state.validators)
|
||||
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index
|
||||
|
||||
for index in fully_withdrawable_indices:
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == 0
|
||||
else:
|
||||
assert state.balances[index] > 0
|
||||
for index in partial_withdrawals_indices:
|
||||
if is_post_electra(spec):
|
||||
max_effective_balance = spec.get_max_effective_balance(state.validators[index])
|
||||
else:
|
||||
max_effective_balance = spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == max_effective_balance
|
||||
else:
|
||||
assert state.balances[index] > max_effective_balance
|
||||
|
||||
|
||||
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None,
|
||||
fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True):
|
||||
"""
|
||||
Run ``process_withdrawals``, yielding:
|
||||
- pre-state ('pre')
|
||||
- execution payload ('execution_payload')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
expected_withdrawals = get_expected_withdrawals(spec, state)
|
||||
assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(expected_withdrawals) == num_expected_withdrawals
|
||||
|
||||
pre_state = state.copy()
|
||||
yield 'pre', state
|
||||
yield 'execution_payload', execution_payload
|
||||
|
||||
if not valid:
|
||||
try:
|
||||
spec.process_withdrawals(state, execution_payload)
|
||||
raise AssertionError('expected an assertion error, but got none.')
|
||||
except AssertionError:
|
||||
pass
|
||||
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
spec.process_withdrawals(state, execution_payload)
|
||||
|
||||
yield 'post', state
|
||||
|
||||
if len(expected_withdrawals) == 0:
|
||||
next_withdrawal_validator_index = (
|
||||
pre_state.next_withdrawal_validator_index + spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
||||
)
|
||||
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index % len(state.validators)
|
||||
elif len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
bound = min(spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
assert len(get_expected_withdrawals(spec, state)) <= bound
|
||||
elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD')
|
||||
|
||||
if fully_withdrawable_indices is not None or partial_withdrawals_indices is not None:
|
||||
verify_post_state(state, spec, expected_withdrawals, fully_withdrawable_indices, partial_withdrawals_indices)
|
||||
|
||||
return expected_withdrawals
|
||||
|
|
|
@ -137,7 +137,7 @@ def test_finality_rule_2(spec, state):
|
|||
def test_finality_rule_3(spec, state):
|
||||
"""
|
||||
Test scenario described here
|
||||
https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892
|
||||
https://github.com/ethereum/consensus-specs/issues/611#issuecomment-463612892
|
||||
"""
|
||||
# get past first two epochs that finality does not run on
|
||||
next_epoch_via_block(spec, state)
|
||||
|
|
|
@ -41,11 +41,12 @@ def make_id(*args):
|
|||
return hash(bytes(values_str, "utf-8"))[:8].hex()
|
||||
|
||||
|
||||
def field_element_bytes(x):
|
||||
return int.to_bytes(x % spec.BLS_MODULUS, 32, spec.KZG_ENDIANNESS)
|
||||
def field_element_bytes(x: int):
|
||||
assert x < spec.BLS_MODULUS
|
||||
return int.to_bytes(x, 32, spec.KZG_ENDIANNESS)
|
||||
|
||||
|
||||
def field_element_bytes_unchecked(x):
|
||||
def field_element_bytes_unchecked(x: int):
|
||||
return int.to_bytes(x, 32, spec.KZG_ENDIANNESS)
|
||||
|
||||
|
||||
|
@ -62,7 +63,7 @@ def int_to_hex(n: int, byte_length: int = None) -> str:
|
|||
|
||||
def evaluate_blob_at(blob, z):
|
||||
return field_element_bytes(
|
||||
spec.evaluate_polynomial_in_evaluation_form(spec.blob_to_polynomial(blob), spec.bytes_to_bls_field(z))
|
||||
int(spec.evaluate_polynomial_in_evaluation_form(spec.blob_to_polynomial(blob), spec.bytes_to_bls_field(z)))
|
||||
)
|
||||
|
||||
|
||||
|
@ -79,7 +80,7 @@ FE_VALID2 = field_element_bytes(1)
|
|||
FE_VALID3 = field_element_bytes(2)
|
||||
FE_VALID4 = field_element_bytes(pow(5, 1235, spec.BLS_MODULUS))
|
||||
FE_VALID5 = field_element_bytes(spec.BLS_MODULUS - 1)
|
||||
FE_VALID6 = field_element_bytes(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[1])
|
||||
FE_VALID6 = field_element_bytes(int(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[1]))
|
||||
VALID_FIELD_ELEMENTS = [FE_VALID1, FE_VALID2, FE_VALID3, FE_VALID4, FE_VALID5, FE_VALID6]
|
||||
|
||||
FE_INVALID_EQUAL_TO_MODULUS = field_element_bytes_unchecked(spec.BLS_MODULUS)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from py_ecc.bls import G2ProofOfPossession as py_ecc_bls
|
||||
from py_ecc.bls.g2_primitives import signature_to_G2 as _signature_to_G2
|
||||
from py_ecc.utils import prime_field_inv as py_ecc_prime_field_inv
|
||||
from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G1 as py_ecc_G1,
|
||||
G2 as py_ecc_G2,
|
||||
|
@ -34,6 +35,28 @@ import milagro_bls_binding as milagro_bls # noqa: F401 for BLS switching option
|
|||
import py_arkworks_bls12381 as arkworks_bls # noqa: F401 for BLS switching option
|
||||
|
||||
|
||||
class py_ecc_Scalar(FQ):
|
||||
field_modulus = BLS_MODULUS
|
||||
|
||||
def __init__(self, value):
|
||||
"""
|
||||
Force underlying value to be a native integer.
|
||||
"""
|
||||
super().__init__(int(value))
|
||||
|
||||
def pow(self, exp):
|
||||
"""
|
||||
Raises the self to the power of the given exponent.
|
||||
"""
|
||||
return self**int(exp)
|
||||
|
||||
def inverse(self):
|
||||
"""
|
||||
Computes the modular inverse of self.
|
||||
"""
|
||||
return py_ecc_Scalar(py_ecc_prime_field_inv(self.n, self.field_modulus))
|
||||
|
||||
|
||||
class fastest_bls:
|
||||
G1 = arkworks_G1
|
||||
G2 = arkworks_G2
|
||||
|
@ -53,6 +76,7 @@ bls_active = True
|
|||
|
||||
# Default to fastest_bls
|
||||
bls = fastest_bls
|
||||
Scalar = fastest_bls.Scalar
|
||||
|
||||
STUB_SIGNATURE = b'\x11' * 96
|
||||
STUB_PUBKEY = b'\x22' * 48
|
||||
|
@ -66,6 +90,8 @@ def use_milagro():
|
|||
"""
|
||||
global bls
|
||||
bls = milagro_bls
|
||||
global Scalar
|
||||
Scalar = py_ecc_Scalar
|
||||
|
||||
|
||||
def use_arkworks():
|
||||
|
@ -74,6 +100,8 @@ def use_arkworks():
|
|||
"""
|
||||
global bls
|
||||
bls = arkworks_bls
|
||||
global Scalar
|
||||
Scalar = arkworks_Scalar
|
||||
|
||||
|
||||
def use_py_ecc():
|
||||
|
@ -82,6 +110,8 @@ def use_py_ecc():
|
|||
"""
|
||||
global bls
|
||||
bls = py_ecc_bls
|
||||
global Scalar
|
||||
Scalar = py_ecc_Scalar
|
||||
|
||||
|
||||
def use_fastest():
|
||||
|
@ -90,6 +120,8 @@ def use_fastest():
|
|||
"""
|
||||
global bls
|
||||
bls = fastest_bls
|
||||
global Scalar
|
||||
Scalar = fastest_bls.Scalar
|
||||
|
||||
|
||||
def only_with_bls(alt_return=None):
|
||||
|
@ -221,29 +253,27 @@ def multiply(point, scalar):
|
|||
`point` can either be in G1 or G2
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
int_as_bytes = scalar.to_bytes(32, 'little')
|
||||
scalar = arkworks_Scalar.from_le_bytes(int_as_bytes)
|
||||
if not isinstance(scalar, arkworks_Scalar):
|
||||
return point * arkworks_Scalar(int(scalar))
|
||||
return point * scalar
|
||||
return py_ecc_mul(point, scalar)
|
||||
return py_ecc_mul(point, int(scalar))
|
||||
|
||||
|
||||
def multi_exp(points, integers):
|
||||
def multi_exp(points, scalars):
|
||||
"""
|
||||
Performs a multi-scalar multiplication between
|
||||
`points` and `integers`.
|
||||
`points` and `scalars`.
|
||||
`points` can either be in G1 or G2.
|
||||
"""
|
||||
# Since this method accepts either G1 or G2, we need to know
|
||||
# the type of the point to return. Hence, we need at least one point.
|
||||
if not points or not integers:
|
||||
raise Exception("Cannot call multi_exp with zero points or zero integers")
|
||||
if not points or not scalars:
|
||||
raise Exception("Cannot call multi_exp with zero points or zero scalars")
|
||||
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
# Convert integers into arkworks Scalars
|
||||
scalars = []
|
||||
for integer in integers:
|
||||
int_as_bytes = integer.to_bytes(32, 'little')
|
||||
scalars.append(arkworks_Scalar.from_le_bytes(int_as_bytes))
|
||||
# If using py_ecc Scalars, convert to arkworks Scalars.
|
||||
if not isinstance(scalars[0], arkworks_Scalar):
|
||||
scalars = [arkworks_Scalar(int(s)) for s in scalars]
|
||||
|
||||
# Check if we need to perform a G1 or G2 multiexp
|
||||
if isinstance(points[0], arkworks_G1):
|
||||
|
@ -261,7 +291,7 @@ def multi_exp(points, integers):
|
|||
else:
|
||||
raise Exception("Invalid point type")
|
||||
|
||||
for point, scalar in zip(points, integers):
|
||||
for point, scalar in zip(points, scalars):
|
||||
result = add(result, multiply(point, scalar))
|
||||
return result
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ if __name__ == "__main__":
|
|||
]}
|
||||
deneb_mods = combine_mods(_new_deneb_mods, capella_mods)
|
||||
|
||||
# This is a "hack" which allows other test files (e.g., test_deposit_transition.py)
|
||||
# to reuse the sanity/block test format. If a new test file is added or removed,
|
||||
# do not forget to update sanity/block/__init__.py accordingly.
|
||||
_new_electra_mods = {key: 'eth2spec.test.electra.sanity.' + key for key in [
|
||||
'blocks',
|
||||
]}
|
||||
|
|
Loading…
Reference in New Issue