From cd855469543648f887f0cfb98fa505971d1ef25f Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 10 Apr 2019 15:09:53 +0100 Subject: [PATCH 01/28] Add option type and null --- specs/simple-serialize.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index b78eff93e..1f2c01949 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -30,6 +30,7 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * `"uintN"`: `N`-bit unsigned integer (where `N in [8, 16, 32, 64, 128, 256]`) * `"bool"`: `True` or `False` +* '"null"': `Null` ### Composite types @@ -39,6 +40,8 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * angle bracket notation `[type, N]`, e.g. `["uint64", N]` * **list**: ordered variable-length homogenous collection of values * angle bracket notation `[type]`, e.g. `["uint64"]` +* **option**: option type containing one of the given subtypes + * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` We recursively define "variable-size" types to be lists and all types that contains a variable-size type. All other types are said to be "fixed-size". @@ -70,7 +73,13 @@ assert value in (True, False) return b"\x01" if value is True else b"\x00" ``` -### Vectors, containers, lists +### `"null"` + +```python +return b"" +``` + +### Vectors, containers, lists, options If `value` is fixed-size: @@ -87,6 +96,17 @@ serialized_length = len(serialized_bytes).to_bytes(BYTES_PER_LENGTH_PREFIX, "lit return serialized_length + serialized_bytes ``` +If `value` is an option type: + +Define value as an object that has properties `value.value` with the contained value, and `value.type_index` which indexes the type. + +```python +serialized_bytes = serialize(value.value) +serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_PREFIX, "little") +return serialized_length + serialized_bytes +``` + + ## Deserialization Because serialization is an injective function (i.e. two distinct objects of the same type will serialize to different values) any bytestring has at most one object it could deserialize to. Efficient algorithms for computing this object can be found in [the implementations](#implementations). @@ -98,6 +118,7 @@ We first define helper functions: * `pack`: Given ordered objects of the same basic type, serialize them, pack them into `BYTES_PER_CHUNK`-byte chunks, right-pad the last chunk with zero bytes, and return the chunks. * `merkleize`: Given ordered `BYTES_PER_CHUNK`-byte chunks, if necessary append zero chunks so that the number of chunks is a power of two, Merkleize the chunks, and return the root. * `mix_in_length`: Given a Merkle root `root` and a length `length` (`"uint256"` little-endian serialization) return `hash(root + length)`. +* `mix_in_type`: Given a Merkle root `root` and a type_index `type_index` (`"uint256"` little-endian serialization) return `hash(root + type_index)`. We now define Merkleization `hash_tree_root(value)` of an object `value` recursively: @@ -105,6 +126,7 @@ We now define Merkleization `hash_tree_root(value)` of an object `value` recursi * `mix_in_length(merkleize(pack(value)), len(value))` if `value` is a list of basic objects * `merkleize([hash_tree_root(element) for element in value])` if `value` is a vector of composite objects or a container * `mix_in_length(merkleize([hash_tree_root(element) for element in value]), len(value))` if `value` is a list of composite objects +* `mix_in_type(merkleize(value.value), value.type_index)` if `value` is of option type ## Self-signed containers From 283ba8f7611e3112f1466eac63c0fc2966f94c79 Mon Sep 17 00:00:00 2001 From: jannikluhn Date: Thu, 11 Apr 2019 16:00:53 +0100 Subject: [PATCH 02/28] Update specs/simple-serialize.md Co-Authored-By: dankrad --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 1f2c01949..5b302e2f5 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -103,7 +103,7 @@ Define value as an object that has properties `value.value` with the contained v ```python serialized_bytes = serialize(value.value) serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_PREFIX, "little") -return serialized_length + serialized_bytes +return serialized_type_index + serialized_bytes ``` From 2017ce96149306721bfd8d18553f531594b937b5 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 11 Apr 2019 16:05:16 +0100 Subject: [PATCH 03/28] Rename "option" -> "union"; "null" only in unions --- specs/simple-serialize.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 5b302e2f5..8a8c78446 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -30,7 +30,9 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * `"uintN"`: `N`-bit unsigned integer (where `N in [8, 16, 32, 64, 128, 256]`) * `"bool"`: `True` or `False` -* '"null"': `Null` +* `"null"`: `None` + +The type `"null"` is only legal as one of several type in a `union` type. ### Composite types @@ -40,7 +42,7 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * angle bracket notation `[type, N]`, e.g. `["uint64", N]` * **list**: ordered variable-length homogenous collection of values * angle bracket notation `[type]`, e.g. `["uint64"]` -* **option**: option type containing one of the given subtypes +* **union**: union type containing one of the given subtypes * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` We recursively define "variable-size" types to be lists and all types that contains a variable-size type. All other types are said to be "fixed-size". @@ -79,7 +81,7 @@ return b"\x01" if value is True else b"\x00" return b"" ``` -### Vectors, containers, lists, options +### Vectors, containers, lists, unions If `value` is fixed-size: @@ -96,7 +98,7 @@ serialized_length = len(serialized_bytes).to_bytes(BYTES_PER_LENGTH_PREFIX, "lit return serialized_length + serialized_bytes ``` -If `value` is an option type: +If `value` is an union type: Define value as an object that has properties `value.value` with the contained value, and `value.type_index` which indexes the type. @@ -126,7 +128,7 @@ We now define Merkleization `hash_tree_root(value)` of an object `value` recursi * `mix_in_length(merkleize(pack(value)), len(value))` if `value` is a list of basic objects * `merkleize([hash_tree_root(element) for element in value])` if `value` is a vector of composite objects or a container * `mix_in_length(merkleize([hash_tree_root(element) for element in value]), len(value))` if `value` is a list of composite objects -* `mix_in_type(merkleize(value.value), value.type_index)` if `value` is of option type +* `mix_in_type(merkleize(value.value), value.type_index)` if `value` is of union type ## Self-signed containers From 63bdf95e79e2dcced7e2f5ba3945700972a5ef9d Mon Sep 17 00:00:00 2001 From: dankrad Date: Sat, 20 Apr 2019 11:46:31 +0100 Subject: [PATCH 04/28] Update simple-serialize.md --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 8a8c78446..c9f845873 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -45,7 +45,7 @@ The type `"null"` is only legal as one of several type in a `union` type. * **union**: union type containing one of the given subtypes * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` -We recursively define "variable-size" types to be lists and all types that contains a variable-size type. All other types are said to be "fixed-size". +We recursively define "variable-size" types to be lists and unions and all types that contain a variable-size type. All other types are said to be "fixed-size". ### Aliases From a481a4e96cf29d13dd19fef33172d0cefa390e04 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 20 Apr 2019 20:57:50 +1000 Subject: [PATCH 05/28] Update simple-serialize.md --- specs/simple-serialize.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index c9f845873..2d9b715b3 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -32,7 +32,7 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * `"bool"`: `True` or `False` * `"null"`: `None` -The type `"null"` is only legal as one of several type in a `union` type. +The `"null"` type is only legal as a union sub-type. ### Composite types @@ -108,7 +108,6 @@ serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_PREFIX, "litt return serialized_type_index + serialized_bytes ``` - ## Deserialization Because serialization is an injective function (i.e. two distinct objects of the same type will serialize to different values) any bytestring has at most one object it could deserialize to. Efficient algorithms for computing this object can be found in [the implementations](#implementations). From 101449e71a3492ff97c649c706866a0da2ec6962 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sat, 27 Apr 2019 21:00:50 +0100 Subject: [PATCH 06/28] Define null as alias of {} --- specs/simple-serialize.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index abef5c669..6858561e8 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -8,12 +8,13 @@ This is a **work in progress** describing typing, serialization and Merkleizatio - [Typing](#typing) - [Basic types](#basic-types) - [Composite types](#composite-types) + - [Illegal empty composites](#illegal-empty-composites) - [Aliases](#aliases) - [Default values](#default-values) - [Serialization](#serialization) - [`"uintN"`](#uintn) - [`"bool"`](#bool) - - [Containers, vectors, lists](#containers-vectors-lists) + - [Vectors, containers, lists, unions](#vectors-containers-lists-unions) - [Deserialization](#deserialization) - [Merkleization](#merkleization) - [Self-signed containers](#self-signed-containers) @@ -32,9 +33,6 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * `"uintN"`: `N`-bit unsigned integer (where `N in [8, 16, 32, 64, 128, 256]`) * `"bool"`: `True` or `False` -* `"null"`: `None` - -The `"null"` type is only legal as a union sub-type. ### Composite types @@ -49,6 +47,10 @@ The `"null"` type is only legal as a union sub-type. We recursively define "variable-size" types to be lists and unions and all types that contain a variable-size type. All other types are said to be "fixed-size". +#### Illegal empty composites + +The empty container `{}` (except as the `"null"` type inside a union, see below) and the empty fixed length list `[type, 0]` are **not** legal types. + ### Aliases For convenience we alias: @@ -56,6 +58,9 @@ For convenience we alias: * `"byte"` to `"uint8"` (this is a basic type) * `"bytes"` to `["byte"]` (this is *not* a basic type) * `"bytesN"` to `["byte", N]` (this is *not* a basic type) +* `"null"`: `{}`, i.e. the empty container + +The `"null"` type is only legal as a union sub-type. ### Default values From 4a483309a544dcd24095e5bb9d5a05b832e97b3b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 1 May 2019 12:39:07 +0200 Subject: [PATCH 07/28] Update specs/simple-serialize.md Co-Authored-By: dankrad --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 6858561e8..79ed0ef7b 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -113,7 +113,7 @@ fixed_parts = [part if part != None else variable_offsets[i] for i, part in enum return b"".join(fixed_parts + variable_parts) ``` -If `value` is an union type: +If `value` is a union type: Define value as an object that has properties `value.value` with the contained value, and `value.type_index` which indexes the type. From d0447022cb4982e544abdea22e616e2cc7c44444 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 1 May 2019 12:39:24 +0200 Subject: [PATCH 08/28] Update specs/simple-serialize.md Co-Authored-By: dankrad --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 79ed0ef7b..0e0468eb7 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -119,7 +119,7 @@ Define value as an object that has properties `value.value` with the contained v ```python serialized_bytes = serialize(value.value) -serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_PREFIX, "little") +serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_OFFSET, "little") return serialized_type_index + serialized_bytes ``` From cc22432bb91626d48ba62e786da72891f60424de Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 1 May 2019 12:39:44 +0200 Subject: [PATCH 09/28] Update specs/simple-serialize.md Co-Authored-By: dankrad --- specs/simple-serialize.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 0e0468eb7..f6b5cb232 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -45,6 +45,8 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * **union**: union type containing one of the given subtypes * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` +#### Variable-size and fixed-size + We recursively define "variable-size" types to be lists and unions and all types that contain a variable-size type. All other types are said to be "fixed-size". #### Illegal empty composites From a33ee00239f0ecd1f8bb557372844250431d0f68 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 1 May 2019 13:52:37 +0100 Subject: [PATCH 10/28] Update simple-serialize.md --- specs/simple-serialize.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index f6b5cb232..b7a1375e4 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -8,9 +8,10 @@ This is a **work in progress** describing typing, serialization and Merkleizatio - [Typing](#typing) - [Basic types](#basic-types) - [Composite types](#composite-types) - - [Illegal empty composites](#illegal-empty-composites) + - [Variable-size and fixed-size](#variable-size-and-fixed-size) - [Aliases](#aliases) - [Default values](#default-values) + - [Illegal types](#illegal-types) - [Serialization](#serialization) - [`"uintN"`](#uintn) - [`"bool"`](#bool) @@ -45,14 +46,10 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * **union**: union type containing one of the given subtypes * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` -#### Variable-size and fixed-size +### Variable-size and fixed-size We recursively define "variable-size" types to be lists and unions and all types that contain a variable-size type. All other types are said to be "fixed-size". -#### Illegal empty composites - -The empty container `{}` (except as the `"null"` type inside a union, see below) and the empty fixed length list `[type, 0]` are **not** legal types. - ### Aliases For convenience we alias: @@ -62,19 +59,20 @@ For convenience we alias: * `"bytesN"` to `["byte", N]` (this is *not* a basic type) * `"null"`: `{}`, i.e. the empty container -The `"null"` type is only legal as a union sub-type. - ### Default values The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists. +### Illegal types + +Empty vector types (i.e. `[subtype, 0]` for some `subtype`) are not legal. The `"null"` type is only legal as a union subtype. + ## Serialization We recursively define the `serialize` function which consumes an object `value` (of the type specified) and returns a bytestring of type `"bytes"`. > *Note*: In the function definitions below (`serialize`, `hash_tree_root`, `signing_root`, `is_variable_size`, etc.) objects implicitly carry their type. - ### `"uintN"` ```python From 563df146b9073da8f91b6ad200cd19273ea8f2ac Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 1 May 2019 13:55:02 +0100 Subject: [PATCH 11/28] Update simple-serialize.md --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index b7a1375e4..21a87a6f9 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -44,7 +44,7 @@ This is a **work in progress** describing typing, serialization and Merkleizatio * **list**: ordered variable-length homogeneous collection of values * angle bracket notation `[type]`, e.g. `["uint64"]` * **union**: union type containing one of the given subtypes - * round bracket notation `(type1, type2, ...)`, e.g. `("uint64", "null")` + * round bracket notation `(type_1, type_2, ...)`, e.g. `("uint64", "null")` ### Variable-size and fixed-size From b1930d22394911c9549f479adb92f62ec41b0958 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Wed, 1 May 2019 15:12:49 +0100 Subject: [PATCH 12/28] Union default values --- specs/simple-serialize.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 21a87a6f9..6adc9c4b8 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -61,11 +61,11 @@ For convenience we alias: ### Default values -The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists. +The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists. Unions default to the first type in the union (with type index zero), which is `"null"` if present in the union. ### Illegal types -Empty vector types (i.e. `[subtype, 0]` for some `subtype`) are not legal. The `"null"` type is only legal as a union subtype. +Empty vector types (i.e. `[subtype, 0]` for some `subtype`) are not legal. The `"null"` type is only legal as the first type in a union subtype (i.e., with type index zero). ## Serialization From c761fbc3181fcf94ef9201a8174806bacd859caa Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 2 May 2019 09:24:24 +0100 Subject: [PATCH 13/28] Clean up verify_indexed_attestation Cosmetic changes: * Add 4 lines of comments (now every statement has a comment) * Avoid unnecessary `assert` (the end goal for me is for `assert`s to be exclusive to the operation processing helpers). * Merge `return`s into one (increase readability, reduce verbosity) * Use shorter-named `bit_0_indices` and `bit_1_indices` helper variables Substantive change: * Remove the condition that `len(0_indices) + len(1_indices) > 0`. This condition is redundant in the context of `process_attester_slashing` because of `slashed_any`. It is largely artificial in `process_attestation` where validators are incentivised to maximise new attestations. --- specs/core/0_beacon-chain.md | 54 +++++++++++++++++------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2a0b0c11d..963c2ac44 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1018,37 +1018,33 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA ```python def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: """ - Verify validity of ``indexed_attestation`` fields. + Verify validity of ``indexed_attestation``. """ - custody_bit_0_indices = indexed_attestation.custody_bit_0_indices - custody_bit_1_indices = indexed_attestation.custody_bit_1_indices + bit_0_indices = indexed_attestation.custody_bit_0_indices + bit_1_indices = indexed_attestation.custody_bit_1_indices - # Ensure no duplicate indices across custody bits - assert len(set(custody_bit_0_indices).intersection(set(custody_bit_1_indices))) == 0 - - if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1] - return False - - if not (1 <= len(custody_bit_0_indices) + len(custody_bit_1_indices) <= MAX_INDICES_PER_ATTESTATION): - return False - - if custody_bit_0_indices != sorted(custody_bit_0_indices): - return False - - if custody_bit_1_indices != sorted(custody_bit_1_indices): - return False - - return bls_verify_multiple( - pubkeys=[ - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_indices]), - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]), - ], - message_hashes=[ - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), - ], - signature=indexed_attestation.signature, - domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target_epoch), + return ( + # Verify no index has custody bit equal to 1 [to be removed in phase 1] + len(bit_1_indices) == 0 and + # Verify max number of indices + len(bit_0_indices) + len(bit_1_indices) <= MAX_INDICES_PER_ATTESTATION and + # Verify index sets are disjoint + len(set(bit_0_indices).intersection(bit_1_indices)) == 0 and + # Verify indices are sorted + bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices) and + # Verify aggregate signature + bls_verify_multiple( + pubkeys=[ + bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_0_indices]), + bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_1_indices]), + ], + message_hashes=[ + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), + ], + signature=indexed_attestation.signature, + domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target_epoch), + ) ) ``` From 973f07223537e33dd05587b895143eb45ee6e3d0 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Thu, 2 May 2019 09:25:29 +0100 Subject: [PATCH 14/28] Remove unnecessary test --- .../block_processing/test_process_attestation.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test_libs/pyspec/tests/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py index bcf71376c..165f0c84a 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -142,14 +142,3 @@ def test_non_empty_custody_bitfield(state): pre_state, post_state = run_attestation_processing(state, attestation, False) return pre_state, attestation, post_state - - -def test_empty_aggregation_bitfield(state): - attestation = get_valid_attestation(state) - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - - attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield) - - pre_state, post_state = run_attestation_processing(state, attestation, False) - - return pre_state, attestation, post_state From a6e825d46056a4a92473e52f1ea0bcb19210e67b Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 5 May 2019 12:04:34 +0100 Subject: [PATCH 15/28] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 58 +++++++++++++++--------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 36ac92796..addaa865e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -279,6 +279,8 @@ The types are defined topologically to aid in facilitating an executable version ```python { + # Shard number + 'shard': 'uint64', # Epoch number 'epoch': 'uint64', # Root of the previous crosslink @@ -315,9 +317,7 @@ The types are defined topologically to aid in facilitating an executable version 'target_root': 'bytes32', # Crosslink vote - 'shard': 'uint64', - 'previous_crosslink_root': 'bytes32', - 'crosslink_data_root': 'bytes32', + 'crosslink': Crosslink, } ``` @@ -765,7 +765,7 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: def get_attestation_slot(state: BeaconState, attestation: Attestation) -> Slot: epoch = attestation.data.target_epoch committee_count = get_epoch_committee_count(state, epoch) - offset = (attestation.data.shard + SHARD_COUNT - get_epoch_start_shard(state, epoch)) % SHARD_COUNT + offset = (attestation.data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, epoch)) % SHARD_COUNT return get_epoch_start_slot(epoch) + offset // (committee_count // SLOTS_PER_EPOCH) ``` @@ -927,7 +927,7 @@ def get_attesting_indices(state: BeaconState, """ Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``. """ - committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.shard) + committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard) assert verify_bitfield(bitfield, len(committee)) return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1]) ``` @@ -1296,28 +1296,18 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) ``` -```python -def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink: - return Crosslink( - epoch=min(data.target_epoch, state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS), - previous_crosslink_root=data.previous_crosslink_root, - crosslink_data_root=data.crosslink_data_root, - ) -``` - ```python def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]: - shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.shard == shard] - shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations] + shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] candidate_crosslinks = [ - c for c in shard_crosslinks + c for c in [a.data.crosslink for a in shard_attestations] if hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c)) ] if len(candidate_crosslinks) == 0: return Crosslink(), [] def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]: - return [a for a in shard_attestations if get_crosslink_from_attestation_data(state, a.data) == crosslink] + return [a for a in shard_attestations if a.data.crosslink == crosslink] # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) winning_crosslink = max(candidate_crosslinks, key=lambda crosslink: ( get_attesting_balance(state, get_attestations_for(crosslink)), crosslink.crosslink_data_root @@ -1705,30 +1695,30 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: attestation_slot = get_attestation_slot(state, attestation) assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH - # Check target epoch, source epoch, source root, and source crosslink data = attestation.data - assert (data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in { - (get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])), - (get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])), - } - - # Check crosslink data root - assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] - - # Check signature and bitfields - assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) - - # Cache pending attestation pending_attestation = PendingAttestation( data=data, aggregation_bitfield=attestation.aggregation_bitfield, inclusion_delay=state.slot - attestation_slot, proposer_index=get_beacon_proposer_index(state), ) - if data.target_epoch == get_current_epoch(state): - state.current_epoch_attestations.append(pending_attestation) - else: + + assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state)) + if data.target_epoch == get_previous_epoch(state): + ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state)) + previous_crosslink = state.previous_crosslinks[data.crosslink.shard] state.previous_epoch_attestations.append(pending_attestation) + if data.target_epoch == get_current_epoch(state): + ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state)) + previous_crosslink = state.current_crosslinks[data.crosslink.shard] + state.current_epoch_attestations.append(pending_attestation) + + # Check FFG data, crosslink data, and signature + assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch) + assert data.crosslink.epoch == min(data.target_epoch, previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS) + assert data.crosslink.previous_crosslink_root == hash_tree_root(previous_crosslink) + assert data.crosslink.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] + assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) ``` ##### Deposits From 5fb32fd19b780d172e8738d887fd0675df7ec3ef Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Sun, 5 May 2019 12:10:39 +0100 Subject: [PATCH 16/28] Fix tests --- configs/constant_presets/mainnet.yaml | 4 ++-- configs/constant_presets/minimal.yaml | 4 ++-- specs/core/0_beacon-chain.md | 6 +++--- specs/core/1_custody-game.md | 16 ++++++++-------- specs/core/1_shard-data-chains.md | 16 ++++++++-------- specs/validator/0_beacon-chain-validator.md | 4 ++-- .../block_processing/test_process_attestation.py | 4 ++-- .../epoch_processing/test_process_crosslinks.py | 12 ++++++------ test_libs/pyspec/tests/helpers.py | 15 ++++++++++----- 9 files changed, 43 insertions(+), 38 deletions(-) diff --git a/configs/constant_presets/mainnet.yaml b/configs/constant_presets/mainnet.yaml index 72d0fdc8f..6ac3f422f 100644 --- a/configs/constant_presets/mainnet.yaml +++ b/configs/constant_presets/mainnet.yaml @@ -72,7 +72,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 # 2**11 (= 2,048) epochs 9 days PERSISTENT_COMMITTEE_PERIOD: 2048 # 2**6 (= 64) epochs ~7 hours -MAX_CROSSLINK_EPOCHS: 64 +MAX_EPOCHS_PER_CROSSLINK: 64 # 2**2 (= 4) epochs 25.6 minutes MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4 @@ -124,4 +124,4 @@ DOMAIN_RANDAO: 1 DOMAIN_ATTESTATION: 2 DOMAIN_DEPOSIT: 3 DOMAIN_VOLUNTARY_EXIT: 4 -DOMAIN_TRANSFER: 5 \ No newline at end of file +DOMAIN_TRANSFER: 5 diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index 0a6cab687..caae4623b 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -71,7 +71,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 # 2**11 (= 2,048) epochs 9 days PERSISTENT_COMMITTEE_PERIOD: 2048 # 2**6 (= 64) epochs ~7 hours -MAX_CROSSLINK_EPOCHS: 64 +MAX_EPOCHS_PER_CROSSLINK: 64 # 2**2 (= 4) epochs 25.6 minutes MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4 @@ -123,4 +123,4 @@ DOMAIN_RANDAO: 1 DOMAIN_ATTESTATION: 2 DOMAIN_DEPOSIT: 3 DOMAIN_VOLUNTARY_EXIT: 4 -DOMAIN_TRANSFER: 5 \ No newline at end of file +DOMAIN_TRANSFER: 5 diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index addaa865e..156a2523d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -206,10 +206,10 @@ These configurations are updated for releases, but may be out of sync during `de | `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | -| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours | +| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours | | `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | -* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` +* `MAX_EPOCHS_PER_CROSSLINK` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` ### State list lengths @@ -1715,7 +1715,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # Check FFG data, crosslink data, and signature assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch) - assert data.crosslink.epoch == min(data.target_epoch, previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS) + assert data.crosslink.epoch == min(data.target_epoch, previous_crosslink.epoch + MAX_EPOCHS_PER_CROSSLINK) assert data.crosslink.previous_crosslink_root == hash_tree_root(previous_crosslink) assert data.crosslink.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index d56526611..f386ee76e 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -265,7 +265,7 @@ The `empty` function accepts and SSZ type as input and returns an object of that def get_custody_chunk_count(attestation: Attestation) -> int: crosslink_start_epoch = attestation.data.latest_crosslink.epoch crosslink_end_epoch = slot_to_epoch(attestation.data.slot) - crosslink_crosslink_length = min(MAX_CROSSLINK_EPOCHS, end_epoch - start_epoch) + crosslink_crosslink_length = min(MAX_EPOCHS_PER_CROSSLINK, end_epoch - start_epoch) chunks_per_epoch = 2 * BYTES_PER_SHARD_BLOCK * SLOTS_PER_EPOCH // BYTES_PER_CUSTODY_CHUNK return crosslink_crosslink_length * chunks_per_epoch ``` @@ -426,10 +426,10 @@ def process_early_derived_secret_reveal(state: BeaconState, # round key slash_validator(state, reveal.revealed_index, reveal.masker_index) else: - # Only a small penalty proportional to proposer slot reward for RANDAO reveal + # Only a small penalty proportional to proposer slot reward for RANDAO reveal # that does not interfere with the custody period - # The penalty is proportional to the max proposer reward - + # The penalty is proportional to the max proposer reward + # Calculate penalty max_proposer_slot_reward = ( get_base_reward(state, reveal.revealed_index) * @@ -448,7 +448,7 @@ def process_early_derived_secret_reveal(state: BeaconState, increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward) decrease_balance(state, reveal.revealed_index, penalty) - # Mark this derived secret as exposed so validator cannot be punished repeatedly + # Mark this derived secret as exposed so validator cannot be punished repeatedly state.exposed_derived_secrets[reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS].append(reveal.revealed_index) ``` @@ -474,7 +474,7 @@ def process_chunk_challenge(state: BeaconState, # Verify the challenge is not a duplicate for record in state.custody_chunk_challenge_records: assert ( - record.crosslink_data_root != challenge.attestation.data.crosslink_data_root or + record.crosslink_data_root != challenge.attestation.data.crosslink.crosslink_data_root or record.chunk_index != challenge.chunk_index ) # Verify depth @@ -486,7 +486,7 @@ def process_chunk_challenge(state: BeaconState, challenger_index=get_beacon_proposer_index(state), responder_index=challenge.responder_index deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE, - crosslink_data_root=challenge.attestation.data.crosslink_data_root, + crosslink_data_root=challenge.attestation.data.crosslink.crosslink_data_root, depth=depth, chunk_index=challenge.chunk_index, ) @@ -564,7 +564,7 @@ def process_bit_challenge(state: BeaconState, challenger_index=challenge.challenger_index, responder_index=challenge.responder_index, deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE, - crosslink_data_root=challenge.attestation.data.crosslink_data_root, + crosslink_data_root=challenge.attestation.data.crosslink.crosslink_data_root, chunk_count=chunk_count, chunk_bits_merkle_root=merkle_root(pad_to_power_of_2((challenge.chunk_bits))), responder_key=challenge.responder_key, diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 33ef8632b..673a64a4a 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -215,7 +215,7 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader: def verify_shard_attestation_signature(state: BeaconState, attestation: ShardAttestation) -> None: data = attestation.data - persistent_committee = get_persistent_committee(state, data.shard, data.slot) + persistent_committee = get_persistent_committee(state, data.crosslink.shard, data.slot) assert verify_bitfield(attestation.aggregation_bitfield, len(persistent_committee)) pubkeys = [] for i, index in enumerate(persistent_committee): @@ -225,7 +225,7 @@ def verify_shard_attestation_signature(state: BeaconState, pubkeys.append(validator.pubkey) assert bls_verify( pubkey=bls_aggregate_pubkeys(pubkeys), - message_hash=data.shard_block_root, + message_hash=data.crosslink.shard_block_root, signature=attestation.aggregate_signature, domain=get_domain(state, slot_to_epoch(data.slot), DOMAIN_SHARD_ATTESTER) ) @@ -312,7 +312,7 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], for _, attestation in enumerate(block.attestations): assert max(GENESIS_SHARD_SLOT, block.slot - SLOTS_PER_EPOCH) <= attestation.data.slot assert attestation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY - assert attestation.data.shard == block.shard + assert attestation.data.crosslink.shard == block.shard verify_shard_attestation_signature(beacon_state, attestation) # Check signature @@ -343,11 +343,11 @@ def is_valid_shard_attestation(valid_shard_blocks: List[ShardBlock], # Check shard block shard_block = next( block for block in valid_shard_blocks if - signing_root(block) == candidate.attestation.data.shard_block_root + signing_root(block) == candidate.attestation.data.crosslink.shard_block_root , None) assert shard_block != None assert shard_block.slot == attestation.data.slot - assert shard_block.shard == attestation.data.shard + assert shard_block.shard == attestation.data.crosslink.shard # Check signature verify_shard_attestation_signature(beacon_state, attestation) @@ -382,18 +382,18 @@ def is_valid_beacon_attestation(shard: Shard, else: previous_attestation = next( attestation for attestation in valid_attestations if - attestation.data.crosslink_data_root == candidate.data.previous_crosslink.crosslink_data_root + attestation.data.crosslink.crosslink_data_root == candidate.data.previous_crosslink.crosslink_data_root , None) assert previous_attestation != None assert candidate.data.previous_attestation.epoch < slot_to_epoch(candidate.data.slot) # Check crosslink data root start_epoch = state.latest_crosslinks[shard].epoch - end_epoch = min(slot_to_epoch(candidate.data.slot) - CROSSLINK_LOOKBACK, start_epoch + MAX_CROSSLINK_EPOCHS) + end_epoch = min(slot_to_epoch(candidate.data.slot) - CROSSLINK_LOOKBACK, start_epoch + MAX_EPOCHS_PER_CROSSLINK) blocks = [] for slot in range(start_epoch * SLOTS_PER_EPOCH, end_epoch * SLOTS_PER_EPOCH): blocks.append(shard_blocks[slot]) - assert candidate.data.crosslink_data_root == compute_crosslink_data_root(blocks) + assert candidate.data.crosslink.crosslink_data_root == compute_crosslink_data_root(blocks) return True ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index ca7f0eb3b..61f80f68e 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -271,7 +271,7 @@ _Note:_ This can be looked up in the state using: ##### Shard -Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`. +Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`. ##### Previous crosslink root @@ -279,7 +279,7 @@ Set `attestation_data.previous_crosslink_root = hash_tree_root(head_state.curren ##### Crosslink data root -Set `attestation_data.crosslink_data_root = ZERO_HASH`. +Set `attestation_data.crosslink.crosslink_data_root = ZERO_HASH`. _Note:_ This is a stub for phase 0. diff --git a/test_libs/pyspec/tests/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py index bcf71376c..24cafc275 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -113,7 +113,7 @@ def test_non_zero_crosslink_data_root(state): attestation = get_valid_attestation(state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.data.crosslink_data_root = b'\x42' * 32 + attestation.data.crosslink.crosslink_data_root = b'\x42' * 32 pre_state, post_state = run_attestation_processing(state, attestation, False) @@ -126,7 +126,7 @@ def test_bad_previous_crosslink(state): for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY): next_slot(state) - state.current_crosslinks[attestation.data.shard].epoch += 10 + state.current_crosslinks[attestation.data.crosslink.shard].epoch += 10 pre_state, post_state = run_attestation_processing(state, attestation, False) diff --git a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py index d6765e3a7..29e7347b1 100644 --- a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py @@ -64,7 +64,7 @@ def test_single_crosslink_update_from_current_epoch(state): pre_state, post_state = run_process_crosslinks(state) - shard = attestation.data.shard + shard = attestation.data.crosslink.shard assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard] assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard] @@ -84,11 +84,11 @@ def test_single_crosslink_update_from_previous_epoch(state): pre_state, post_state = run_process_crosslinks(state) crosslink_deltas = get_crosslink_deltas(state) - shard = attestation.data.shard + shard = attestation.data.crosslink.shard assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard] assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard] # ensure rewarded - for index in get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.shard): + for index in get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard): assert crosslink_deltas[0][index] > 0 assert crosslink_deltas[1][index] == 0 @@ -108,7 +108,7 @@ def test_double_late_crosslink(state): for slot in range(spec.SLOTS_PER_EPOCH): attestation_2 = get_valid_attestation(state) - if attestation_2.data.shard == attestation_1.data.shard: + if attestation_2.data.crosslink.shard == attestation_1.data.crosslink.shard: break next_slot(state) fill_aggregate_attestation(state, attestation_2) @@ -124,12 +124,12 @@ def test_double_late_crosslink(state): pre_state, post_state = run_process_crosslinks(state) crosslink_deltas = get_crosslink_deltas(state) - shard = attestation_2.data.shard + shard = attestation_2.data.crosslink.shard # ensure that the current crosslinks were not updated by the second attestation assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard] # ensure no reward, only penalties for the failed crosslink - for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.shard): + for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.crosslink.shard): assert crosslink_deltas[0][index] == 0 assert crosslink_deltas[1][index] > 0 diff --git a/test_libs/pyspec/tests/helpers.py b/test_libs/pyspec/tests/helpers.py index 3b9b6904d..1ea299453 100644 --- a/test_libs/pyspec/tests/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -10,6 +10,7 @@ from eth2spec.utils.minimal_ssz import signing_root from eth2spec.phase0.spec import ( # constants ZERO_HASH, + MAX_EPOCHS_PER_CROSSLINK, # SSZ Attestation, AttestationData, @@ -17,6 +18,7 @@ from eth2spec.phase0.spec import ( AttesterSlashing, BeaconBlock, BeaconBlockHeader, + Crosslink, Deposit, DepositData, Eth1Data, @@ -174,14 +176,17 @@ def build_attestation_data(state, slot, shard): crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks return AttestationData( - shard=shard, beacon_block_root=block_root, source_epoch=justified_epoch, source_root=justified_block_root, target_epoch=slot_to_epoch(slot), target_root=epoch_boundary_root, - crosslink_data_root=spec.ZERO_HASH, - previous_crosslink_root=hash_tree_root(crosslinks[shard]), + crosslink=Crosslink( + shard=shard, + epoch=min(slot_to_epoch(slot), crosslinks[shard].epoch + MAX_EPOCHS_PER_CROSSLINK), + crosslink_data_root=spec.ZERO_HASH, + previous_crosslink_root=hash_tree_root(crosslinks[shard]), + ), ) @@ -288,7 +293,7 @@ def get_valid_attestation(state, slot=None): attestation_data = build_attestation_data(state, slot, shard) - crosslink_committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.shard) + crosslink_committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard) committee_size = len(crosslink_committee) bitfield_length = (committee_size + 7) // 8 @@ -381,7 +386,7 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0) def fill_aggregate_attestation(state, attestation): - crosslink_committee = get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.shard) + crosslink_committee = get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard) for i in range(len(crosslink_committee)): attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i) From b15105e1cbb4a597fea233b16eac23ae911570a6 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Mon, 6 May 2019 17:34:03 +0100 Subject: [PATCH 17/28] Address Danny's comment --- specs/core/0_beacon-chain.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 156a2523d..0bd85478e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1298,22 +1298,17 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat ```python def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]: - shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] - candidate_crosslinks = [ - c for c in [a.data.crosslink for a in shard_attestations] - if hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c)) - ] - if len(candidate_crosslinks) == 0: - return Crosslink(), [] - - def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]: - return [a for a in shard_attestations if a.data.crosslink == crosslink] - # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) - winning_crosslink = max(candidate_crosslinks, key=lambda crosslink: ( - get_attesting_balance(state, get_attestations_for(crosslink)), crosslink.crosslink_data_root + attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] + crosslinks = list(filter( + lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c)), + [a.data.crosslink for a in attestations] )) - - return winning_crosslink, get_unslashed_attesting_indices(state, get_attestations_for(winning_crosslink)) + # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) + winning_crosslink = max(crosslinks, key=lambda c: ( + get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.crosslink_data_root + ), default=Crosslink()) + winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink] + return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations) ``` #### Justification and finalization From 8b1a2edb7c650e0e0205e11c44f44a2a578ce392 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Mon, 6 May 2019 17:53:49 +0100 Subject: [PATCH 18/28] Fix genesis bug --- specs/core/0_beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9aed2001a..04ab38e18 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -767,7 +767,7 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: ```python def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot: committee_count = get_epoch_committee_count(state, data.target_epoch) - offset = (data.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT + offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT return get_epoch_start_slot(data.target_epoch) + offset // (committee_count // SLOTS_PER_EPOCH) ``` @@ -1705,14 +1705,14 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ) assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state)) - if data.target_epoch == get_previous_epoch(state): - ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state)) - previous_crosslink = state.previous_crosslinks[data.crosslink.shard] - state.previous_epoch_attestations.append(pending_attestation) if data.target_epoch == get_current_epoch(state): ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state)) previous_crosslink = state.current_crosslinks[data.crosslink.shard] state.current_epoch_attestations.append(pending_attestation) + else: + ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state)) + previous_crosslink = state.previous_crosslinks[data.crosslink.shard] + state.previous_epoch_attestations.append(pending_attestation) # Check FFG data, crosslink data, and signature assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch) From f4db9ebae00338ba336943a08271012099bcef1d Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Mon, 6 May 2019 18:26:14 +0100 Subject: [PATCH 19/28] Renamings triggered by HW comment --- specs/core/0_beacon-chain.md | 26 +++++++++---------- specs/core/0_fork-choice.md | 2 +- specs/core/1_custody-game.md | 14 +++++----- specs/core/1_shard-data-chains.md | 16 ++++++------ specs/validator/0_beacon-chain-validator.md | 6 ++--- .../test_process_attestation.py | 2 +- .../test_process_block_header.py | 4 +-- test_libs/pyspec/tests/helpers.py | 12 ++++----- test_libs/pyspec/tests/test_sanity.py | 6 ++--- 9 files changed, 44 insertions(+), 44 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 04ab38e18..afca9eb1b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -287,9 +287,9 @@ The types are defined topologically to aid in facilitating an executable version # Epoch number 'epoch': 'uint64', # Root of the previous crosslink - 'previous_crosslink_root': 'bytes32', + 'parent_crosslink_root': 'bytes32', # Root of the crosslinked shard data since the previous crosslink - 'crosslink_data_root': 'bytes32', + 'data_root': 'bytes32', } ``` @@ -369,7 +369,7 @@ The types are defined topologically to aid in facilitating an executable version ```python { 'slot': 'uint64', - 'previous_block_root': 'bytes32', + 'parent_block_root': 'bytes32', 'state_root': 'bytes32', 'block_body_root': 'bytes32', 'signature': 'bytes96', @@ -536,7 +536,7 @@ The types are defined topologically to aid in facilitating an executable version { # Header 'slot': 'uint64', - 'previous_block_root': 'bytes32', + 'parent_block_root': 'bytes32', 'state_root': 'bytes32', 'body': BeaconBlockBody, 'signature': 'bytes96', @@ -1306,12 +1306,12 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]: attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] crosslinks = list(filter( - lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c)), + lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_crosslink_root, hash_tree_root(c)), [a.data.crosslink for a in attestations] )) # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) winning_crosslink = max(crosslinks, key=lambda c: ( - get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.crosslink_data_root + get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root ), default=Crosslink()) winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink] return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations) @@ -1586,11 +1586,11 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: # Verify that the slots match assert block.slot == state.slot # Verify that the parent matches - assert block.previous_block_root == signing_root(state.latest_block_header) + assert block.parent_block_root == signing_root(state.latest_block_header) # Save current block as the new latest block state.latest_block_header = BeaconBlockHeader( slot=block.slot, - previous_block_root=block.previous_block_root, + parent_block_root=block.parent_block_root, block_body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed @@ -1707,18 +1707,18 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state)) if data.target_epoch == get_current_epoch(state): ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state)) - previous_crosslink = state.current_crosslinks[data.crosslink.shard] + parent_crosslink = state.current_crosslinks[data.crosslink.shard] state.current_epoch_attestations.append(pending_attestation) else: ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state)) - previous_crosslink = state.previous_crosslinks[data.crosslink.shard] + parent_crosslink = state.previous_crosslinks[data.crosslink.shard] state.previous_epoch_attestations.append(pending_attestation) # Check FFG data, crosslink data, and signature assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch) - assert data.crosslink.epoch == min(data.target_epoch, previous_crosslink.epoch + MAX_EPOCHS_PER_CROSSLINK) - assert data.crosslink.previous_crosslink_root == hash_tree_root(previous_crosslink) - assert data.crosslink.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] + assert data.crosslink.epoch == min(data.target_epoch, parent_crosslink.epoch + MAX_EPOCHS_PER_CROSSLINK) + assert data.crosslink.parent_crosslink_root == hash_tree_root(parent_crosslink) + assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1] assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) ``` diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 549e9e207..891120de8 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -38,7 +38,7 @@ All terminology, constants, functions, and protocol mechanics defined in the [Ph Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Clients download and process blocks and maintain a view of what is the current "canonical chain", terminating at the current "head". For a beacon block, `block`, to be processed by a node, the following conditions must be met: -* The parent block with root `block.previous_block_root` has been processed and accepted. +* The parent block with root `block.parent_block_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. * The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index e259b82a5..a030c577b 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -147,7 +147,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether 'challenger_index': ValidatorIndex, 'responder_index': ValidatorIndex, 'deadline': Epoch, - 'crosslink_data_root': Hash, + 'data_root': Hash, 'depth': 'uint64', 'chunk_index': 'uint64', } @@ -161,7 +161,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether 'challenger_index': ValidatorIndex, 'responder_index': ValidatorIndex, 'deadline': Epoch, - 'crosslink_data_root': Hash, + 'data_root': Hash, 'chunk_count': 'uint64', 'chunk_bits_merkle_root': Hash, 'responder_key': BLSSignature, @@ -474,7 +474,7 @@ def process_chunk_challenge(state: BeaconState, # Verify the challenge is not a duplicate for record in state.custody_chunk_challenge_records: assert ( - record.crosslink_data_root != challenge.attestation.data.crosslink.crosslink_data_root or + record.data_root != challenge.attestation.data.crosslink.data_root or record.chunk_index != challenge.chunk_index ) # Verify depth @@ -486,7 +486,7 @@ def process_chunk_challenge(state: BeaconState, challenger_index=get_beacon_proposer_index(state), responder_index=challenge.responder_index deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE, - crosslink_data_root=challenge.attestation.data.crosslink.crosslink_data_root, + data_root=challenge.attestation.data.crosslink.data_root, depth=depth, chunk_index=challenge.chunk_index, ) @@ -564,7 +564,7 @@ def process_bit_challenge(state: BeaconState, challenger_index=challenge.challenger_index, responder_index=challenge.responder_index, deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE, - crosslink_data_root=challenge.attestation.data.crosslink.crosslink_data_root, + data_root=challenge.attestation.data.crosslink.data_root, chunk_count=chunk_count, chunk_bits_merkle_root=merkle_root(pad_to_power_of_2((challenge.chunk_bits))), responder_key=challenge.responder_key, @@ -610,7 +610,7 @@ def process_chunk_challenge_response(state: BeaconState, branch=response.data_branch, depth=challenge.depth, index=response.chunk_index, - root=challenge.crosslink_data_root, + root=challenge.data_root, ) # Clear the challenge records = state.custody_chunk_challenge_records @@ -632,7 +632,7 @@ def process_bit_challenge_response(state: BeaconState, branch=response.data_branch, depth=math.log2(next_power_of_two(challenge.chunk_count)), index=response.chunk_index, - root=challenge.crosslink_data_root, + root=challenge.data_root, ) # Verify the chunk bit leaf matches the challenge data assert verify_merkle_branch( diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 9fbc69692..316bd0068 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -78,7 +78,7 @@ This document describes the shard data layer and the shard fork choice rule in P 'slot': Slot, 'shard': Shard, 'beacon_chain_root': Hash, - 'previous_block_root': Hash, + 'parent_block_root': Hash, 'data': ShardBlockBody, 'state_root': Hash, 'attestations': [ShardAttestation], @@ -93,7 +93,7 @@ This document describes the shard data layer and the shard fork choice rule in P 'slot': Slot, 'shard': Shard, 'beacon_chain_root': Hash, - 'previous_block_root': Hash, + 'parent_block_root': Hash, 'body_root': Hash, 'state_root': Hash, 'attestations': [ShardAttestation], @@ -201,7 +201,7 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader: slot: block.slot, shard: block.shard, beacon_chain_root: block.beacon_chain_root, - previous_block_root: block.previous_block_root, + parent_block_root: block.parent_block_root, body_root: hash_tree_root(block.body), state_root: block.state_root, attestations: block.attestations, @@ -296,11 +296,11 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], # Check parent block if block.slot == PHASE_1_GENESIS_SLOT: - assert candidate.previous_block_root == ZERO_HASH + assert candidate.parent_block_root == ZERO_HASH else: parent_block = next( block for block in valid_shard_blocks if - signing_root(block) == candidate.previous_block_root + signing_root(block) == candidate.parent_block_root , None) assert parent_block != None assert parent_block.shard == block.shard @@ -378,11 +378,11 @@ def is_valid_beacon_attestation(shard: Shard, # Check previous attestation if candidate.data.previous_crosslink.epoch <= PHASE_1_GENESIS_EPOCH: - assert candidate.data.previous_crosslink.crosslink_data_root == ZERO_HASH + assert candidate.data.previous_crosslink.data_root == ZERO_HASH else: previous_attestation = next( attestation for attestation in valid_attestations if - attestation.data.crosslink.crosslink_data_root == candidate.data.previous_crosslink.crosslink_data_root + attestation.data.crosslink.data_root == candidate.data.previous_crosslink.data_root , None) assert previous_attestation != None assert candidate.data.previous_attestation.epoch < slot_to_epoch(candidate.data.slot) @@ -393,7 +393,7 @@ def is_valid_beacon_attestation(shard: Shard, blocks = [] for slot in range(start_epoch * SLOTS_PER_EPOCH, end_epoch * SLOTS_PER_EPOCH): blocks.append(shard_blocks[slot]) - assert candidate.data.crosslink.crosslink_data_root == compute_crosslink_data_root(blocks) + assert candidate.data.crosslink.data_root == compute_crosslink_data_root(blocks) return True ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index cfe6a8f1f..dae9ac85f 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -152,7 +152,7 @@ Set `block.slot = slot` where `slot` is the current slot at which the validator ##### Parent root -Set `block.previous_block_root = signing_root(parent)`. +Set `block.parent_block_root = signing_root(parent)`. ##### State root @@ -275,11 +275,11 @@ Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associ ##### Previous crosslink root -Set `attestation_data.previous_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`. +Set `attestation_data.parent_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`. ##### Crosslink data root -Set `attestation_data.crosslink.crosslink_data_root = ZERO_HASH`. +Set `attestation_data.crosslink.data_root = ZERO_HASH`. *Note*: This is a stub for Phase 0. diff --git a/test_libs/pyspec/tests/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py index 24cafc275..fcbc68fe9 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -113,7 +113,7 @@ def test_non_zero_crosslink_data_root(state): attestation = get_valid_attestation(state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.data.crosslink.crosslink_data_root = b'\x42' * 32 + attestation.data.crosslink.data_root = b'\x42' * 32 pre_state, post_state = run_attestation_processing(state, attestation, False) diff --git a/test_libs/pyspec/tests/block_processing/test_process_block_header.py b/test_libs/pyspec/tests/block_processing/test_process_block_header.py index b35b0a9c1..e4fa516b1 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/tests/block_processing/test_process_block_header.py @@ -53,9 +53,9 @@ def test_invalid_slot(state): return pre_state, block, None -def test_invalid_previous_block_root(state): +def test_invalid_parent_block_root(state): block = build_empty_block_for_next_slot(state) - block.previous_block_root = b'\12' * 32 # invalid prev root + block.parent_block_root = b'\12' * 32 # invalid prev root pre_state, post_state = run_block_header_processing(state, block, valid=False) return pre_state, block, None diff --git a/test_libs/pyspec/tests/helpers.py b/test_libs/pyspec/tests/helpers.py index 1ea299453..e7ee826db 100644 --- a/test_libs/pyspec/tests/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -128,7 +128,7 @@ def build_empty_block_for_next_slot(state): previous_block_header = deepcopy(state.latest_block_header) if previous_block_header.state_root == spec.ZERO_HASH: previous_block_header.state_root = state.hash_tree_root() - empty_block.previous_block_root = signing_root(previous_block_header) + empty_block.parent_block_root = signing_root(previous_block_header) return empty_block @@ -155,7 +155,7 @@ def build_attestation_data(state, slot, shard): assert state.slot >= slot if slot == state.slot: - block_root = build_empty_block_for_next_slot(state).previous_block_root + block_root = build_empty_block_for_next_slot(state).parent_block_root else: block_root = get_block_root_at_slot(state, slot) @@ -184,8 +184,8 @@ def build_attestation_data(state, slot, shard): crosslink=Crosslink( shard=shard, epoch=min(slot_to_epoch(slot), crosslinks[shard].epoch + MAX_EPOCHS_PER_CROSSLINK), - crosslink_data_root=spec.ZERO_HASH, - previous_crosslink_root=hash_tree_root(crosslinks[shard]), + data_root=spec.ZERO_HASH, + parent_crosslink_root=hash_tree_root(crosslinks[shard]), ), ) @@ -240,12 +240,12 @@ def get_valid_proposer_slashing(state): header_1 = BeaconBlockHeader( slot=slot, - previous_block_root=ZERO_HASH, + parent_block_root=ZERO_HASH, state_root=ZERO_HASH, block_body_root=ZERO_HASH, ) header_2 = deepcopy(header_1) - header_2.previous_block_root = b'\x02' * 32 + header_2.parent_block_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index 1b4d20f4c..83ba9cc11 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -68,7 +68,7 @@ def test_empty_block_transition(state): state_transition(test_state, block) assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1 - assert get_block_root_at_slot(test_state, state.slot) == block.previous_block_root + assert get_block_root_at_slot(test_state, state.slot) == block.parent_block_root return state, [block], test_state @@ -82,7 +82,7 @@ def test_skipped_slots(state): assert test_state.slot == block.slot for slot in range(state.slot, test_state.slot): - assert get_block_root_at_slot(test_state, slot) == block.previous_block_root + assert get_block_root_at_slot(test_state, slot) == block.parent_block_root return state, [block], test_state @@ -96,7 +96,7 @@ def test_empty_epoch_transition(state): assert test_state.slot == block.slot for slot in range(state.slot, test_state.slot): - assert get_block_root_at_slot(test_state, slot) == block.previous_block_root + assert get_block_root_at_slot(test_state, slot) == block.parent_block_root return state, [block], test_state From ea60fb632c3a07d2dae29b6ca8d37cea204bc6e7 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Mon, 6 May 2019 20:49:46 +0100 Subject: [PATCH 20/28] More renaming --- specs/core/0_beacon-chain.md | 14 +++++++------- specs/core/0_fork-choice.md | 2 +- specs/core/1_shard-data-chains.md | 10 +++++----- specs/validator/0_beacon-chain-validator.md | 4 ++-- .../block_processing/test_process_block_header.py | 2 +- test_libs/pyspec/tests/helpers.py | 12 ++++++------ test_libs/pyspec/tests/test_sanity.py | 6 +++--- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index afca9eb1b..ccba4c817 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -287,7 +287,7 @@ The types are defined topologically to aid in facilitating an executable version # Epoch number 'epoch': 'uint64', # Root of the previous crosslink - 'parent_crosslink_root': 'bytes32', + 'parent_root': 'bytes32', # Root of the crosslinked shard data since the previous crosslink 'data_root': 'bytes32', } @@ -369,7 +369,7 @@ The types are defined topologically to aid in facilitating an executable version ```python { 'slot': 'uint64', - 'parent_block_root': 'bytes32', + 'parent_root': 'bytes32', 'state_root': 'bytes32', 'block_body_root': 'bytes32', 'signature': 'bytes96', @@ -536,7 +536,7 @@ The types are defined topologically to aid in facilitating an executable version { # Header 'slot': 'uint64', - 'parent_block_root': 'bytes32', + 'parent_root': 'bytes32', 'state_root': 'bytes32', 'body': BeaconBlockBody, 'signature': 'bytes96', @@ -1306,7 +1306,7 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]: attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard] crosslinks = list(filter( - lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_crosslink_root, hash_tree_root(c)), + lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)), [a.data.crosslink for a in attestations] )) # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically) @@ -1586,11 +1586,11 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: # Verify that the slots match assert block.slot == state.slot # Verify that the parent matches - assert block.parent_block_root == signing_root(state.latest_block_header) + assert block.parent_root == signing_root(state.latest_block_header) # Save current block as the new latest block state.latest_block_header = BeaconBlockHeader( slot=block.slot, - parent_block_root=block.parent_block_root, + parent_root=block.parent_root, block_body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed @@ -1717,7 +1717,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # Check FFG data, crosslink data, and signature assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch) assert data.crosslink.epoch == min(data.target_epoch, parent_crosslink.epoch + MAX_EPOCHS_PER_CROSSLINK) - assert data.crosslink.parent_crosslink_root == hash_tree_root(parent_crosslink) + assert data.crosslink.parent_root == hash_tree_root(parent_crosslink) assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1] assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) ``` diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 891120de8..91c3e27ee 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -38,7 +38,7 @@ All terminology, constants, functions, and protocol mechanics defined in the [Ph Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Clients download and process blocks and maintain a view of what is the current "canonical chain", terminating at the current "head". For a beacon block, `block`, to be processed by a node, the following conditions must be met: -* The parent block with root `block.parent_block_root` has been processed and accepted. +* The parent block with root `block.parent_root` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. * The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 316bd0068..ca59c3ebc 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -78,7 +78,7 @@ This document describes the shard data layer and the shard fork choice rule in P 'slot': Slot, 'shard': Shard, 'beacon_chain_root': Hash, - 'parent_block_root': Hash, + 'parent_root': Hash, 'data': ShardBlockBody, 'state_root': Hash, 'attestations': [ShardAttestation], @@ -93,7 +93,7 @@ This document describes the shard data layer and the shard fork choice rule in P 'slot': Slot, 'shard': Shard, 'beacon_chain_root': Hash, - 'parent_block_root': Hash, + 'parent_root': Hash, 'body_root': Hash, 'state_root': Hash, 'attestations': [ShardAttestation], @@ -201,7 +201,7 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader: slot: block.slot, shard: block.shard, beacon_chain_root: block.beacon_chain_root, - parent_block_root: block.parent_block_root, + parent_root: block.parent_root, body_root: hash_tree_root(block.body), state_root: block.state_root, attestations: block.attestations, @@ -296,11 +296,11 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], # Check parent block if block.slot == PHASE_1_GENESIS_SLOT: - assert candidate.parent_block_root == ZERO_HASH + assert candidate.parent_root == ZERO_HASH else: parent_block = next( block for block in valid_shard_blocks if - signing_root(block) == candidate.parent_block_root + signing_root(block) == candidate.parent_root , None) assert parent_block != None assert parent_block.shard == block.shard diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index dae9ac85f..a0173ebe9 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -152,7 +152,7 @@ Set `block.slot = slot` where `slot` is the current slot at which the validator ##### Parent root -Set `block.parent_block_root = signing_root(parent)`. +Set `block.parent_root = signing_root(parent)`. ##### State root @@ -275,7 +275,7 @@ Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associ ##### Previous crosslink root -Set `attestation_data.parent_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`. +Set `attestation_data.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`. ##### Crosslink data root diff --git a/test_libs/pyspec/tests/block_processing/test_process_block_header.py b/test_libs/pyspec/tests/block_processing/test_process_block_header.py index e4fa516b1..36c729821 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/tests/block_processing/test_process_block_header.py @@ -55,7 +55,7 @@ def test_invalid_slot(state): def test_invalid_parent_block_root(state): block = build_empty_block_for_next_slot(state) - block.parent_block_root = b'\12' * 32 # invalid prev root + block.parent_root = b'\12' * 32 # invalid prev root pre_state, post_state = run_block_header_processing(state, block, valid=False) return pre_state, block, None diff --git a/test_libs/pyspec/tests/helpers.py b/test_libs/pyspec/tests/helpers.py index e7ee826db..202488576 100644 --- a/test_libs/pyspec/tests/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -128,7 +128,7 @@ def build_empty_block_for_next_slot(state): previous_block_header = deepcopy(state.latest_block_header) if previous_block_header.state_root == spec.ZERO_HASH: previous_block_header.state_root = state.hash_tree_root() - empty_block.parent_block_root = signing_root(previous_block_header) + empty_block.parent_root = signing_root(previous_block_header) return empty_block @@ -155,7 +155,7 @@ def build_attestation_data(state, slot, shard): assert state.slot >= slot if slot == state.slot: - block_root = build_empty_block_for_next_slot(state).parent_block_root + block_root = build_empty_block_for_next_slot(state).parent_root else: block_root = get_block_root_at_slot(state, slot) @@ -185,7 +185,7 @@ def build_attestation_data(state, slot, shard): shard=shard, epoch=min(slot_to_epoch(slot), crosslinks[shard].epoch + MAX_EPOCHS_PER_CROSSLINK), data_root=spec.ZERO_HASH, - parent_crosslink_root=hash_tree_root(crosslinks[shard]), + parent_root=hash_tree_root(crosslinks[shard]), ), ) @@ -240,12 +240,12 @@ def get_valid_proposer_slashing(state): header_1 = BeaconBlockHeader( slot=slot, - parent_block_root=ZERO_HASH, + parent_root=ZERO_HASH, state_root=ZERO_HASH, - block_body_root=ZERO_HASH, + body_root=ZERO_HASH, ) header_2 = deepcopy(header_1) - header_2.parent_block_root = b'\x02' * 32 + header_2.parent_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index 83ba9cc11..0cb1b9be3 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -68,7 +68,7 @@ def test_empty_block_transition(state): state_transition(test_state, block) assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1 - assert get_block_root_at_slot(test_state, state.slot) == block.parent_block_root + assert get_block_root_at_slot(test_state, state.slot) == block.parent_root return state, [block], test_state @@ -82,7 +82,7 @@ def test_skipped_slots(state): assert test_state.slot == block.slot for slot in range(state.slot, test_state.slot): - assert get_block_root_at_slot(test_state, slot) == block.parent_block_root + assert get_block_root_at_slot(test_state, slot) == block.parent_root return state, [block], test_state @@ -96,7 +96,7 @@ def test_empty_epoch_transition(state): assert test_state.slot == block.slot for slot in range(state.slot, test_state.slot): - assert get_block_root_at_slot(test_state, slot) == block.parent_block_root + assert get_block_root_at_slot(test_state, slot) == block.parent_root return state, [block], test_state From fc1239c0ff8fda299d4a5e5a532bb2203e130446 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 6 May 2019 18:50:20 -0700 Subject: [PATCH 21/28] Add clarity around merkleize on a single chunk --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 834f56645..c02d7fbed 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -111,7 +111,7 @@ Because serialization is an injective function (i.e. two distinct objects of the We first define helper functions: * `pack`: Given ordered objects of the same basic type, serialize them, pack them into `BYTES_PER_CHUNK`-byte chunks, right-pad the last chunk with zero bytes, and return the chunks. -* `merkleize`: Given ordered `BYTES_PER_CHUNK`-byte chunks, if necessary append zero chunks so that the number of chunks is a power of two, Merkleize the chunks, and return the root. +* `merkleize`: Given ordered `BYTES_PER_CHUNK`-byte chunks, if necessary append zero chunks so that the number of chunks is a power of two, Merkleize the chunks, and return the root. Note that `merkleize` on a single chunk is simply that chunk, i.e. the identity when the number of chunks is one. * `mix_in_length`: Given a Merkle root `root` and a length `length` (`"uint256"` little-endian serialization) return `hash(root + length)`. We now define Merkleization `hash_tree_root(value)` of an object `value` recursively: From 964e55cd4a8b8bfecbd70e279c13e69c6c977e22 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 7 May 2019 08:52:56 +0100 Subject: [PATCH 22/28] block_body_root => body_root --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ccba4c817..9f80fe173 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -371,7 +371,7 @@ The types are defined topologically to aid in facilitating an executable version 'slot': 'uint64', 'parent_root': 'bytes32', 'state_root': 'bytes32', - 'block_body_root': 'bytes32', + 'body_root': 'bytes32', 'signature': 'bytes96', } ``` @@ -1591,7 +1591,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: state.latest_block_header = BeaconBlockHeader( slot=block.slot, parent_root=block.parent_root, - block_body_root=hash_tree_root(block.body), + body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed proposer = state.validator_registry[get_beacon_proposer_index(state)] From b1520ea96766e34397c161cea3518e7e7f0104be Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 7 May 2019 10:33:51 +0100 Subject: [PATCH 23/28] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dd2d3d1a6..f0798f587 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -75,7 +75,7 @@ - [`compute_committee`](#compute_committee) - [`get_crosslink_committee`](#get_crosslink_committee) - [`get_attesting_indices`](#get_attesting_indices) - - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) + - [`int_to_bytes`](#int_to_bytes) - [`bytes_to_int`](#bytes_to_int) - [`get_total_balance`](#get_total_balance) - [`get_domain`](#get_domain) @@ -194,8 +194,8 @@ These configurations are updated for releases, but may be out of sync during `de | `GENESIS_SLOT` | `0` | | `GENESIS_EPOCH` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | -| `ZERO_HASH` | `int_to_bytes32(0)` | -| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` | +| `ZERO_HASH` | `int_to_bytes(0, length=32)` | +| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes(0, length=1)` | ### Time parameters @@ -830,7 +830,7 @@ def generate_seed(state: BeaconState, return hash( get_randao_mix(state, epoch + LATEST_RANDAO_MIXES_LENGTH - MIN_SEED_LOOKAHEAD) + get_active_index_root(state, epoch) + - int_to_bytes32(epoch) + int_to_bytes(epoch, length=32) ) ``` @@ -851,7 +851,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: i = 0 while True: candidate_index = first_committee[(epoch + i) % len(first_committee)] - random_byte = hash(seed + int_to_bytes8(i // 32))[i % 32] + random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] effective_balance = state.validator_registry[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -888,10 +888,10 @@ def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Bytes32) - # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 for round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % index_count + pivot = bytes_to_int(hash(seed + int_to_bytes(round, length=1))[0:8]) % index_count flip = (pivot - index) % index_count position = max(index, flip) - source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) + source = hash(seed + int_to_bytes(round, length=1) + int_to_bytes(position // 256, length=4)) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -934,9 +934,12 @@ def get_attesting_indices(state: BeaconState, return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1]) ``` -### `int_to_bytes1`, `int_to_bytes2`, ... +### `int_to_bytes` -`int_to_bytes1(x): return x.to_bytes(1, 'little')`, `int_to_bytes2(x): return x.to_bytes(2, 'little')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. +```python +def int_to_bytes(integer: int, length: int) -> bytes: + return integer.to_bytes(length, 'little') +``` ### `bytes_to_int` @@ -966,7 +969,7 @@ def get_domain(state: BeaconState, """ epoch = get_current_epoch(state) if message_epoch is None else message_epoch fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return bytes_to_int(fork_version + int_to_bytes4(domain_type)) + return bytes_to_int(fork_version + int_to_bytes(domain_type, length=4)) ``` ### `get_bitfield_bit` From 22b06d581dcdc84ca3cfedd9e21af529557d14d6 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 7 May 2019 10:57:41 +0100 Subject: [PATCH 24/28] Update instances of int_to_bytes --- scripts/phase0/build_spec.py | 8 ++------ specs/core/0_beacon-chain.md | 4 ++-- specs/core/1_shard-data-chains.md | 2 +- specs/light_client/sync_protocol.md | 2 +- test_generators/shuffling/main.py | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/scripts/phase0/build_spec.py b/scripts/phase0/build_spec.py index da5845951..28e5049ae 100644 --- a/scripts/phase0/build_spec.py +++ b/scripts/phase0/build_spec.py @@ -15,15 +15,11 @@ from typing import ( from eth2spec.utils.minimal_ssz import * from eth2spec.utils.bls_stub import * -""") - for i in (1, 2, 3, 4, 8, 32, 48, 96): - code_lines.append("def int_to_bytes%d(x): return x.to_bytes(%d, 'little')" % (i, i)) - - code_lines.append(""" - # stub, will get overwritten by real var SLOTS_PER_EPOCH = 64 +def int_to_bytes(integer: int, length: int) -> bytes: + return integer.to_bytes(length, 'little') Slot = NewType('Slot', int) # uint64 Epoch = NewType('Epoch', int) # uint64 diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f0798f587..1e8170036 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -195,7 +195,7 @@ These configurations are updated for releases, but may be out of sync during `de | `GENESIS_EPOCH` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | | `ZERO_HASH` | `int_to_bytes(0, length=32)` | -| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes(0, length=1)` | +| `BLS_WITHDRAWAL_PREFIX` | `0` | ### Time parameters @@ -1840,7 +1840,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: # Verify that the pubkey is valid assert ( state.validator_registry[transfer.sender].withdrawal_credentials == - BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:] + int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:] ) # Verify that the signature is valid assert bls_verify(transfer.pubkey, signing_root(transfer), transfer.signature, get_domain(state, DOMAIN_TRANSFER)) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 14eb51193..90ea30841 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -180,7 +180,7 @@ def get_shard_proposer_index(state: BeaconState, slot: Slot) -> ValidatorIndex: # Randomly shift persistent committee persistent_committee = get_persistent_committee(state, shard, slot) - seed = hash(state.current_shuffling_seed + int_to_bytes8(shard) + int_to_bytes8(slot)) + seed = hash(state.current_shuffling_seed + int_to_bytes(shard, length=8) + int_to_bytes(slot, length=8)) random_index = bytes_to_int(seed[0:8]) % len(persistent_committee) persistent_committee = persistent_committee[random_index:] + persistent_committee[:random_index] diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index f6e3d2265..8501c5869 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -146,7 +146,7 @@ def compute_committee(header: BeaconBlockHeader, ] def get_switchover_epoch(index): return ( - bytes_to_int(hash(validator_memory.earlier_period_data.seed + int_to_bytes3(index))[0:8]) % + bytes_to_int(hash(validator_memory.earlier_period_data.seed + int_to_bytes(index, length=3))[0:8]) % PERSISTENT_COMMITTEE_PERIOD ) diff --git a/test_generators/shuffling/main.py b/test_generators/shuffling/main.py index 2c4faeb8f..9ca3e2d36 100644 --- a/test_generators/shuffling/main.py +++ b/test_generators/shuffling/main.py @@ -15,7 +15,7 @@ def shuffling_case(seed: spec.Bytes32, count: int): @to_tuple def shuffling_test_cases(): - for seed in [spec.hash(spec.int_to_bytes4(seed_init_value)) for seed_init_value in range(30)]: + for seed in [spec.hash(spec.int_to_bytes(seed_init_value, length=4)) for seed_init_value in range(30)]: for count in [0, 1, 2, 3, 5, 10, 33, 100, 1000]: yield shuffling_case(seed, count) From ecc6429b9eaef80b13ee8f9f72daa76930f4ad25 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 7 May 2019 15:01:23 +0100 Subject: [PATCH 25/28] Address Danny's comment --- scripts/phase0/build_spec.py | 3 --- specs/core/0_beacon-chain.md | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/phase0/build_spec.py b/scripts/phase0/build_spec.py index 28e5049ae..e3c431a4a 100644 --- a/scripts/phase0/build_spec.py +++ b/scripts/phase0/build_spec.py @@ -18,9 +18,6 @@ from eth2spec.utils.bls_stub import * # stub, will get overwritten by real var SLOTS_PER_EPOCH = 64 -def int_to_bytes(integer: int, length: int) -> bytes: - return integer.to_bytes(length, 'little') - Slot = NewType('Slot', int) # uint64 Epoch = NewType('Epoch', int) # uint64 Shard = NewType('Shard', int) # uint64 diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1e8170036..a26a4e979 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -194,7 +194,7 @@ These configurations are updated for releases, but may be out of sync during `de | `GENESIS_SLOT` | `0` | | `GENESIS_EPOCH` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | -| `ZERO_HASH` | `int_to_bytes(0, length=32)` | +| `ZERO_HASH` | `b'\x00' * 32` | | `BLS_WITHDRAWAL_PREFIX` | `0` | ### Time parameters From 62c44ffce3068b0e9d9146c3e4ca30deb68583eb Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 7 May 2019 17:34:19 +0100 Subject: [PATCH 26/28] Refactor to validate_indexed_attestation --- specs/core/0_beacon-chain.md | 54 +++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 963c2ac44..27c7fd461 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -82,7 +82,7 @@ - [`get_bitfield_bit`](#get_bitfield_bit) - [`verify_bitfield`](#verify_bitfield) - [`convert_to_indexed`](#convert_to_indexed) - - [`verify_indexed_attestation`](#verify_indexed_attestation) + - [`validate_indexed_attestation`](#validate_indexed_attestation) - [`is_slashable_attestation_data`](#is_slashable_attestation_data) - [`integer_squareroot`](#integer_squareroot) - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) @@ -1013,38 +1013,36 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA ) ``` -### `verify_indexed_attestation` +### `validate_indexed_attestation` ```python -def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: +def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None: """ Verify validity of ``indexed_attestation``. """ bit_0_indices = indexed_attestation.custody_bit_0_indices bit_1_indices = indexed_attestation.custody_bit_1_indices - return ( - # Verify no index has custody bit equal to 1 [to be removed in phase 1] - len(bit_1_indices) == 0 and - # Verify max number of indices - len(bit_0_indices) + len(bit_1_indices) <= MAX_INDICES_PER_ATTESTATION and - # Verify index sets are disjoint - len(set(bit_0_indices).intersection(bit_1_indices)) == 0 and - # Verify indices are sorted - bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices) and - # Verify aggregate signature - bls_verify_multiple( - pubkeys=[ - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_0_indices]), - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_1_indices]), - ], - message_hashes=[ - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), - ], - signature=indexed_attestation.signature, - domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target_epoch), - ) + # Verify no index has custody bit equal to 1 [to be removed in phase 1] + assert len(bit_1_indices) == 0 + # Verify max number of indices + assert len(bit_0_indices) + len(bit_1_indices) <= MAX_INDICES_PER_ATTESTATION + # Verify index sets are disjoint + assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0 + # Verify indices are sorted + assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices) + # Verify aggregate signature + assert bls_verify_multiple( + pubkeys=[ + bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_0_indices]), + bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_1_indices]), + ], + message_hashes=[ + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), + hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), + ], + signature=indexed_attestation.signature, + domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target_epoch), ) ``` @@ -1669,8 +1667,8 @@ def process_attester_slashing(state: BeaconState, attestation_1 = attester_slashing.attestation_1 attestation_2 = attester_slashing.attestation_2 assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) - assert verify_indexed_attestation(state, attestation_1) - assert verify_indexed_attestation(state, attestation_2) + validate_indexed_attestation(state, attestation_1) + validate_indexed_attestation(state, attestation_2) slashed_any = False attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices @@ -1707,7 +1705,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] # Check signature and bitfields - assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) + validate_indexed_attestation(state, convert_to_indexed(state, attestation)) # Cache pending attestation pending_attestation = PendingAttestation( From 13d2ee696939e192534c0278eb271fe53510caf7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 7 May 2019 11:49:45 -0600 Subject: [PATCH 27/28] cleanup validator guide for crosslinks --- specs/validator/0_beacon-chain-validator.md | 53 +++++++-------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index a0173ebe9..a95052b86 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -37,14 +37,9 @@ - [Voluntary exits](#voluntary-exits) - [Attestations](#attestations-1) - [Attestation data](#attestation-data) - - [Slot](#slot-1) - - [Beacon block root](#beacon-block-root) - - [Source epoch](#source-epoch) - - [Source root](#source-root) - - [Target root](#target-root) - - [Shard](#shard) - - [Previous crosslink root](#previous-crosslink-root) - - [Crosslink data root](#crosslink-data-root) + - [LMD GHOST vote](#lmd-ghost-vote) + - [FFG vote](#ffg-vote) + - [Crosslink vote](#crosslink-vote) - [Construct attestation](#construct-attestation) - [Data](#data) - [Aggregation bitfield](#aggregation-bitfield) @@ -245,43 +240,29 @@ First the validator should construct `attestation_data`, an [`AttestationData`]( * Let `head_block` be the result of running the fork choice during the assigned slot. * Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot. -##### Slot - -Set `attestation_data.slot = head_state.slot`. - -##### Beacon block root +##### LMD GHOST vote Set `attestation_data.beacon_block_root = signing_root(head_block)`. -##### Source epoch +##### FFG vote -Set `attestation_data.source_epoch = head_state.justified_epoch`. +* Set `attestation_data.source_epoch = head_state.justified_epoch`. +* Set `attestation_data.source_root = head_state.current_justified_root`. +* Set `attestation_data.target_epoch = get_current_epoch(head_state)` +* Set `attestation_data.target_root = signing_root(epoch_boundary_block)` where `epoch_boundary_block` is the block at the most recent epoch boundary. -##### Source root - -Set `attestation_data.source_root = head_state.current_justified_root`. - -##### Target root - -Set `attestation_data.target_root = signing_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary. - -*Note*: This can be looked up in the state using: +*Note*: `epoch_boundary_block` can be looked up in the state using: * Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`. -* Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`. +* Let `epoch_boundary_block = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`. -##### Shard +##### Crosslink vote -Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`. +Construct `attestation_data.crosslink` via the following -##### Previous crosslink root - -Set `attestation_data.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`. - -##### Crosslink data root - -Set `attestation_data.crosslink.data_root = ZERO_HASH`. - -*Note*: This is a stub for Phase 0. +* Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`. +* Set `attestation_data.crosslink.epoch = min(attestation_data.target_epoch, head_state.current_crosslinks[shard].epoch + MAX_EPOCHS_PER_CROSSLINK)`. +* Set `attestation_data.crosslink.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`. +* Set `attestation_data.crosslink.data_root = ZERO_HASH`. *Note*: This is a stub for Phase 0. #### Construct attestation From 513c44bd3d19c70da61359376dc440fc86bddc23 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 8 May 2019 08:38:14 -0600 Subject: [PATCH 28/28] add back in empty attestation test --- .../block_processing/test_process_attestation.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test_libs/pyspec/tests/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py index 165f0c84a..c986cc4c8 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -142,3 +142,14 @@ def test_non_empty_custody_bitfield(state): pre_state, post_state = run_attestation_processing(state, attestation, False) return pre_state, attestation, post_state + + +def test_empty_aggregation_bitfield(state): + attestation = get_valid_attestation(state) + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield) + + pre_state, post_state = run_attestation_processing(state, attestation) + + return pre_state, attestation, post_state