From e0867c030f65e18781fddf6295b879d958aecb88 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Feb 2019 22:34:10 +0800 Subject: [PATCH 01/40] Fix typo --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d93cb21d7..1404f5190 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1660,7 +1660,7 @@ Below are the processing steps that happen at every slot. #### Block roots -* Let `previous_block_root` be the `tree_hash_root` of the previous beacon block processed in the chain. +* Let `previous_block_root` be the `hash_tree_root` of the previous beacon block processed in the chain. * Set `state.latest_block_roots[(state.slot - 1) % LATEST_BLOCK_ROOTS_LENGTH] = previous_block_root`. * If `state.slot % LATEST_BLOCK_ROOTS_LENGTH == 0` append `merkle_root(state.latest_block_roots)` to `state.batched_block_roots`. From 378e1ba9a681be900e86fd87e9729c657aceb8cf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 2 Feb 2019 02:06:53 +0800 Subject: [PATCH 02/40] Misc fixes of `get_next_epoch_crosslink_committees` --- specs/core/0_beacon-chain.md | 2 +- specs/validator/0_beacon-chain-validator.md | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d93cb21d7..dbc8db0e9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -833,7 +833,7 @@ def get_next_epoch_committee_count(state: BeaconState) -> int: ```python def get_crosslink_committees_at_slot(state: BeaconState, slot: SlotNumber, - registry_change=False: bool) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: + registry_change: bool=False) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: """ Return the list of ``(committee, shard)`` tuples for the ``slot``. diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index b01e7a3af..3df3d758d 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -345,15 +345,23 @@ Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is determi ```python def get_next_epoch_crosslink_committees(state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[ValidatorIndex], ShardNumber]: + validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) potential_committees = [] - for validator_registry in [False, True]: + for registry_change in [False, True]: for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): - shard_committees = get_crosslink_committees_at_slot(state, slot, validator_registry) - selected_committees = [committee for committee in shard_committees if validator_index in committee[0]] + crosslink_committees = get_crosslink_committees_at_slot( + state, + slot, + registry_change=registry_change, + ) + selected_committees = [ + committee # type: Tuple[List[ValidatorIndex], ShardNumber] + for committee in crosslink_committees + if validator_index in committee[0] + ] if len(selected_committees) > 0: potential_assignments.append(selected_committees) break From 2a32e7f6653959da1f21e2812426cb9d87baf24a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 2 Feb 2019 16:41:59 -0800 Subject: [PATCH 03/40] convert int_to_bytes to little endian --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index caaac363f..f67afd8a6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -710,8 +710,8 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: if remaining == 1: break - # Read 3-bytes of `source` as a 24-bit big-endian integer. - sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'big') + # Read 3-bytes of `source` as a 24-bit little-endian integer. + sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'little') # Sample values greater than or equal to `sample_max` will cause # modulo bias when mapped into the `remaining` range. @@ -1015,7 +1015,7 @@ def is_power_of_two(value: int) -> bool: ### `int_to_bytes1`, `int_to_bytes2`, ... -`int_to_bytes1(x): return x.to_bytes(1, 'big')`, `int_to_bytes2(x): return x.to_bytes(2, 'big')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. +`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. ### `get_effective_balance` From c58410e6ce9904c6619cd925b64fbd04c00b9a89 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 06:48:46 -0600 Subject: [PATCH 04/40] Introduce swap-or-not shuffle See #563 for discussion. --- specs/core/0_beacon-chain.md | 61 +++++++++--------------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 44b88d126..3cb1b459f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,53 +697,22 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: - """ - Return the shuffled ``values`` with ``seed`` as entropy. - """ - values_count = len(values) + indices = list(range(len(values))) + for round in range(90): + hashvalues = b''.join([ + hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) + for i in range((n + 255) // 256) + ]) + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n + + def permute(pos): + flip = (pivot - pos) % n + maxpos = max(pos, flip) + bit = (hashvalues[maxpos // 8] >> (maxpos % 8)) % 2 + return flip if bit else pos - # Entropy is consumed from the seed in 3-byte (24 bit) chunks. - rand_bytes = 3 - # The highest possible result of the RNG. - rand_max = 2 ** (rand_bytes * 8) - 1 - - # The range of the RNG places an upper-bound on the size of the list that - # may be shuffled. It is a logic error to supply an oversized list. - assert values_count < rand_max - - output = [x for x in values] - source = seed - index = 0 - while index < values_count - 1: - # Re-hash the `source` to obtain a new pattern of bytes. - source = hash(source) - # Iterate through the `source` bytes in 3-byte chunks. - for position in range(0, 32 - (32 % rand_bytes), rand_bytes): - # Determine the number of indices remaining in `values` and exit - # once the last index is reached. - remaining = values_count - index - if remaining == 1: - break - - # Read 3-bytes of `source` as a 24-bit big-endian integer. - sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'big') - - # Sample values greater than or equal to `sample_max` will cause - # modulo bias when mapped into the `remaining` range. - sample_max = rand_max - rand_max % remaining - - # Perform a swap if the consumed entropy will not cause modulo bias. - if sample_from_source < sample_max: - # Select a replacement index for the current index. - replacement_position = (sample_from_source % remaining) + index - # Swap the current index with the replacement index. - output[index], output[replacement_position] = output[replacement_position], output[index] - index += 1 - else: - # The sample causes modulo bias. A new sample should be read. - pass - - return output + indices = [permute(v) for v in indices] + return [v[index] for index in indices] ``` ### `split` From 37b41a2ce645b644a56c473cba2ded3a49689139 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Feb 2019 18:33:11 -0600 Subject: [PATCH 05/40] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3cb1b459f..5471d76a1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -704,7 +704,6 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: for i in range((n + 255) // 256) ]) pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n - def permute(pos): flip = (pivot - pos) % n maxpos = max(pos, flip) From 4ec721f3b7d4459c3db601d9a3bd53511a1dfd31 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 6 Feb 2019 18:33:22 -0600 Subject: [PATCH 06/40] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 5471d76a1..2e453b061 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,6 +697,13 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: + """ + Return the shuffled ``values`` with ``seed`` as entropy. + + Utilizes 'swap or not' shuffling found in + https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf + See the 'generalized domain' algorithm on page 3. + """ indices = list(range(len(values))) for round in range(90): hashvalues = b''.join([ From 6a5b7540da501c976d1e0ec76b44725bb96f7f37 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Feb 2019 18:33:29 -0600 Subject: [PATCH 07/40] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2e453b061..1426bb4e8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -718,7 +718,7 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: return flip if bit else pos indices = [permute(v) for v in indices] - return [v[index] for index in indices] + return [values[index] for index in indices] ``` ### `split` From 47b00f38dda4336ada69b166f8f288b840c628d3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 18:34:05 -0600 Subject: [PATCH 08/40] n -> len(values) --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1426bb4e8..60bacba63 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -708,7 +708,7 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: for round in range(90): hashvalues = b''.join([ hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) - for i in range((n + 255) // 256) + for i in range((len(values) + 255) // 256) ]) pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n def permute(pos): From b3db7b03942d2c666952fb94fe01774d15089105 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 6 Feb 2019 20:32:05 -0800 Subject: [PATCH 09/40] big to little in shuffle --- 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 60bacba63..641ad02ca 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -707,10 +707,10 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: indices = list(range(len(values))) for round in range(90): hashvalues = b''.join([ - hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) + hash(seed + round.to_bytes(1, 'little') + i.to_bytes(4, 'little')) for i in range((len(values) + 255) // 256) ]) - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % n def permute(pos): flip = (pivot - pos) % n maxpos = max(pos, flip) From 65255e53c46ac8d2ee520f2845e9869193ae783c Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 23:29:24 -0600 Subject: [PATCH 10/40] shuffle -> get_permuted_index --- specs/core/0_beacon-chain.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 641ad02ca..dd58482b3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) + - [`get_permuted_index`](#get_permuted_index) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,32 +693,27 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `shuffle` +### `get_permuted_index` ```python -def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ - Return the shuffled ``values`` with ``seed`` as entropy. + Return a pseudorandom permutation of `0...list_size-1` with ``seed`` as entropy. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ - indices = list(range(len(values))) for round in range(90): - hashvalues = b''.join([ - hash(seed + round.to_bytes(1, 'little') + i.to_bytes(4, 'little')) - for i in range((len(values) + 255) // 256) - ]) - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % n - def permute(pos): - flip = (pivot - pos) % n - maxpos = max(pos, flip) - bit = (hashvalues[maxpos // 8] >> (maxpos % 8)) % 2 - return flip if bit else pos - - indices = [permute(v) for v in indices] - return [values[index] for index in indices] + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % list_size + + flip = (pivot - index) % list_size + hash_pos = max(index, flip) + h = hash(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) + byte = h[(hash_pos % 256) // 8] + bit = (byte >> (hash_pos % 8)) % 2 + index = flip if bit else index + return index ``` ### `split` @@ -768,7 +763,10 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle - shuffled_active_validator_indices = shuffle(active_validator_indices, seed) + shuffled_active_validator_indices = [ + active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] + for i in active_validator_indices + ] # Split the shuffled list into committees_per_epoch pieces return split(shuffled_active_validator_indices, committees_per_epoch) From 92514716fb77de05637a560ec3f62a0ed5209dc6 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 23:32:20 -0600 Subject: [PATCH 11/40] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dd58482b3..2c0fce8c5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -698,7 +698,7 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ - Return a pseudorandom permutation of `0...list_size-1` with ``seed`` as entropy. + Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf From 70e482be28a10ca0d37de34fb3a0b5cd94108ad3 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 7 Feb 2019 19:14:58 +0800 Subject: [PATCH 12/40] Add vbuterin's optimization and some formatting --- specs/core/0_beacon-chain.md | 44 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2c0fce8c5..3da520300 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`get_permuted_index`](#get_permuted_index) + - [`shuffle`](#shuffle) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,27 +693,38 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `get_permuted_index` +### `shuffle` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: +def shuffle(list_size: int, seed: Bytes32) -> List[int]: """ - Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. - + Return shuffled indices in a pseudorandom permutation `0...list_size-1` with ``seed`` as entropy. + Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ + indices = list(range(list_size)) + power_of_two_numbers = [1, 2, 4, 8, 16, 32, 64, 128] for round in range(90): - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % list_size - - flip = (pivot - index) % list_size - hash_pos = max(index, flip) - h = hash(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) - byte = h[(hash_pos % 256) // 8] - bit = (byte >> (hash_pos % 8)) % 2 - index = flip if bit else index - return index + hash_bytes = b''.join([ + hash(seed + int_to_bytes1(round) + int_to_bytes4(i)) + for i in range((list_size + 255) // 256) + ]) + + pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size + for i in range(list_size): + flip = (pivot - indices[i]) % list_size + hash_position = indices[i] if indices[i] > flip else flip + byte = hash_bytes[hash_position // 8] + mask = power_of_two_numbers[hash_position % 8] + if byte & mask: + indices[i] = flip + else: + # not swap + pass + + return indices ``` ### `split` @@ -763,9 +774,10 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle + shuffled_indices = shuffle(len(active_validator_indices), seed) shuffled_active_validator_indices = [ - active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] - for i in active_validator_indices + active_validator_indices[i] + for i in shuffle(len(active_validator_indices), seed) ] # Split the shuffled list into committees_per_epoch pieces From aa9f9fc9be337ad072e855e5a4132735eea7636b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 7 Feb 2019 19:18:39 +0800 Subject: [PATCH 13/40] amend --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3da520300..6a4ac6ce5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -774,7 +774,6 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle - shuffled_indices = shuffle(len(active_validator_indices), seed) shuffled_active_validator_indices = [ active_validator_indices[i] for i in shuffle(len(active_validator_indices), seed) From 3f3472087af00ddb54210feb7f82cc7f6e22d966 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 10:37:01 -0800 Subject: [PATCH 14/40] change message to message_hash in bls spec addresses #572 --- specs/bls_signature.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/specs/bls_signature.md b/specs/bls_signature.md index fd9bae58e..b0490b7ae 100644 --- a/specs/bls_signature.md +++ b/specs/bls_signature.md @@ -69,10 +69,10 @@ We require: G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -def hash_to_G2(message: bytes32, domain: uint64) -> [uint384]: +def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]: # Initial candidate x coordinate - x_re = int.from_bytes(hash(message + bytes8(domain) + b'\x01'), 'big') - x_im = int.from_bytes(hash(message + bytes8(domain) + b'\x02'), 'big') + x_re = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x01'), 'big') + x_im = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x02'), 'big') x_coordinate = Fq2([x_re, x_im]) # x = x_re + i * x_im # Test candidate y coordinates until a one is found @@ -128,17 +128,17 @@ g = Fq2([g_x, g_y]) ### `bls_verify` -Let `bls_verify(pubkey: Bytes48, message: Bytes32, signature: Bytes96, domain: uint64) -> bool`: +Let `bls_verify(pubkey: Bytes48, message_hash: Bytes32, signature: Bytes96, domain: uint64) -> bool`: * Verify that `pubkey` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify that `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)`. +* Verify that `e(pubkey, hash_to_G2(message_hash, domain)) == e(g, signature)`. ### `bls_verify_multiple` -Let `bls_verify_multiple(pubkeys: List[Bytes48], messages: List[Bytes32], signature: Bytes96, domain: uint64) -> bool`: +Let `bls_verify_multiple(pubkeys: List[Bytes48], message_hashes: List[Bytes32], signature: Bytes96, domain: uint64) -> bool`: * Verify that each `pubkey` in `pubkeys` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify that `len(pubkeys)` equals `len(messages)` and denote the length `L`. -* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(g, signature)`. +* Verify that `len(pubkeys)` equals `len(message_hashes)` and denote the length `L`. +* Verify that `e(pubkeys[0], hash_to_G2(message_hashes[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(message_hashes[L-1], domain)) == e(g, signature)`. From d4901be1984e44a61d410bda9d4592caa25cf470 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 11:48:36 -0700 Subject: [PATCH 15/40] get_next_epoch_committee_assignments returns slot and is_proposer --- specs/validator/0_beacon-chain-validator.md | 26 +++++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 3df3d758d..093f29f5b 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -344,12 +344,22 @@ Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is determi `get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committees in the next epoch for a given `validator_index`. This function returns a list of 2 shard committee tuples. ```python -def get_next_epoch_crosslink_committees(state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: +def get_next_epoch_committee_assignments( + state: BeaconState, + validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]]: + """ + Return a list of the two possible committee assignments for ``validator_index`` at the next epoch. + Possible committee ``assignment`` is of the form (List[ValidatorIndex], ShardNumber, SlotNumber, bool). + * ``assignment[0]`` is the list of validators in the committee + * ``assignment[1]`` is the shard to which the committee is assigned + * ``assignment[2]`` is the slot at which the committee is assigned + * ``assignment[3]`` is a bool signalling if the validator is expected to propose + a beacon block at the assigned slot. + """ current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) - potential_committees = [] + potential_assignments = [] for registry_change in [False, True]: for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): crosslink_committees = get_crosslink_committees_at_slot( @@ -363,13 +373,19 @@ def get_next_epoch_crosslink_committees(state: BeaconState, if validator_index in committee[0] ] if len(selected_committees) > 0: - potential_assignments.append(selected_committees) + assignment = selected_committees[0] + assignment += (slot,) + first_committee_at_slot = crosslink_committees[0] + is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index + assignment += (is_proposer,) + + potential_assignments.append(assignment) break return potential_assignments ``` -`get_next_epoch_crosslink_committees` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignments` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing From e4f5efadb79dcfe6f83b71a3bf339fd11b224f0a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 04:03:13 +0800 Subject: [PATCH 16/40] Fix `first_committee_at_slot` --- specs/validator/0_beacon-chain-validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 093f29f5b..4d8d5eadf 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -368,14 +368,14 @@ def get_next_epoch_committee_assignments( registry_change=registry_change, ) selected_committees = [ - committee # type: Tuple[List[ValidatorIndex], ShardNumber] + committee # Tuple[List[ValidatorIndex], ShardNumber] for committee in crosslink_committees if validator_index in committee[0] ] if len(selected_committees) > 0: assignment = selected_committees[0] assignment += (slot,) - first_committee_at_slot = crosslink_committees[0] + first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index assignment += (is_proposer,) From 859bf6248467eb8f5a23c28ac721f605185ccfaf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:08:25 +0800 Subject: [PATCH 17/40] Revert and refactor --- specs/core/0_beacon-chain.md | 41 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6a4ac6ce5..38d233b23 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) + - [`get_permuted_index`](#get_permuted_index) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,38 +693,29 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `shuffle` +### `get_permuted_index` ```python -def shuffle(list_size: int, seed: Bytes32) -> List[int]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> List[int]: """ - Return shuffled indices in a pseudorandom permutation `0...list_size-1` with ``seed`` as entropy. + Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. + + Note that ``round_count`` is ``90`` in protocol and parameterized for the shuffling tests. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ - indices = list(range(list_size)) - power_of_two_numbers = [1, 2, 4, 8, 16, 32, 64, 128] - for round in range(90): - hash_bytes = b''.join([ - hash(seed + int_to_bytes1(round) + int_to_bytes4(i)) - for i in range((list_size + 255) // 256) - ]) - + for round in range(round_count): pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size - for i in range(list_size): - flip = (pivot - indices[i]) % list_size - hash_position = indices[i] if indices[i] > flip else flip - byte = hash_bytes[hash_position // 8] - mask = power_of_two_numbers[hash_position % 8] - if byte & mask: - indices[i] = flip - else: - # not swap - pass + flip = (pivot - index) % list_size + position = max(index, flip) + source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) + byte = source[(position % 256) // 8] + bit = (byte >> (position % 8)) % 2 + index = flip if bit else index - return indices + return index ``` ### `split` @@ -775,8 +766,8 @@ def get_shuffling(seed: Bytes32, # Shuffle shuffled_active_validator_indices = [ - active_validator_indices[i] - for i in shuffle(len(active_validator_indices), seed) + active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] + for i in active_validator_indices ] # Split the shuffled list into committees_per_epoch pieces From cf7ebe9ad31d1b4831f8a26353ac70c1fd60d27c Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:09:41 +0100 Subject: [PATCH 18/40] `message` to `message_hash` in the rest of the spec (followup https://github.com/ethereum/eth2.0-specs/pull/580) --- specs/core/0_beacon-chain.md | 280 ++++++++++---------- specs/validator/0_beacon-chain-validator.md | 102 +++---- 2 files changed, 191 insertions(+), 191 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b0433ce04..e6cf019f4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -6,139 +6,139 @@ - [Ethereum 2.0 Phase 0 -- The Beacon Chain](#ethereum-20-phase-0----the-beacon-chain) - - [Table of contents](#table-of-contents) - - [Introduction](#introduction) - - [Notation](#notation) - - [Terminology](#terminology) - - [Constants](#constants) - - [Misc](#misc) - - [Deposit contract](#deposit-contract) - - [Gwei values](#gwei-values) - - [Initial values](#initial-values) - - [Time parameters](#time-parameters) - - [State list lengths](#state-list-lengths) - - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - - [Max operations per block](#max-operations-per-block) - - [Signature domains](#signature-domains) - - [Data structures](#data-structures) - - [Beacon chain operations](#beacon-chain-operations) - - [Proposer slashings](#proposer-slashings) - - [`ProposerSlashing`](#proposerslashing) - - [Attester slashings](#attester-slashings) - - [`AttesterSlashing`](#attesterslashing) - - [`SlashableAttestation`](#slashableattestation) - - [Attestations](#attestations) - - [`Attestation`](#attestation) - - [`AttestationData`](#attestationdata) - - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [Deposits](#deposits) - - [`Deposit`](#deposit) - - [`DepositData`](#depositdata) - - [`DepositInput`](#depositinput) - - [Exits](#exits) - - [`Exit`](#exit) - - [Beacon chain blocks](#beacon-chain-blocks) - - [`BeaconBlock`](#beaconblock) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ProposalSignedData`](#proposalsigneddata) - - [Beacon chain state](#beacon-chain-state) - - [`BeaconState`](#beaconstate) - - [`Validator`](#validator) - - [`Crosslink`](#crosslink) - - [`PendingAttestation`](#pendingattestation) - - [`Fork`](#fork) - - [`Eth1Data`](#eth1data) - - [`Eth1DataVote`](#eth1datavote) - - [Custom Types](#custom-types) - - [Helper functions](#helper-functions) - - [`hash`](#hash) - - [`hash_tree_root`](#hash_tree_root) - - [`slot_to_epoch`](#slot_to_epoch) - - [`get_previous_epoch`](#get_previous_epoch) - - [`get_current_epoch`](#get_current_epoch) - - [`get_epoch_start_slot`](#get_epoch_start_slot) - - [`is_active_validator`](#is_active_validator) - - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) - - [`split`](#split) - - [`get_epoch_committee_count`](#get_epoch_committee_count) - - [`get_shuffling`](#get_shuffling) - - [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count) - - [`get_current_epoch_committee_count`](#get_current_epoch_committee_count) - - [`get_next_epoch_committee_count`](#get_next_epoch_committee_count) - - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) - - [`get_block_root`](#get_block_root) - - [`get_randao_mix`](#get_randao_mix) - - [`get_active_index_root`](#get_active_index_root) - - [`generate_seed`](#generate_seed) - - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - - [`merkle_root`](#merkle_root) - - [`get_attestation_participants`](#get_attestation_participants) - - [`is_power_of_two`](#is_power_of_two) - - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) - - [`get_effective_balance`](#get_effective_balance) - - [`get_total_balance`](#get_total_balance) - - [`get_fork_version`](#get_fork_version) - - [`get_domain`](#get_domain) - - [`get_bitfield_bit`](#get_bitfield_bit) - - [`verify_bitfield`](#verify_bitfield) - - [`verify_slashable_attestation`](#verify_slashable_attestation) - - [`is_double_vote`](#is_double_vote) - - [`is_surround_vote`](#is_surround_vote) - - [`integer_squareroot`](#integer_squareroot) - - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) - - [`bls_verify`](#bls_verify) - - [`bls_verify_multiple`](#bls_verify_multiple) - - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - - [`validate_proof_of_possession`](#validate_proof_of_possession) - - [`process_deposit`](#process_deposit) - - [Routines for updating validator status](#routines-for-updating-validator-status) - - [`activate_validator`](#activate_validator) - - [`initiate_validator_exit`](#initiate_validator_exit) - - [`exit_validator`](#exit_validator) - - [`penalize_validator`](#penalize_validator) - - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) - - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - - [Deposit arguments](#deposit-arguments) - - [Withdrawal credentials](#withdrawal-credentials) - - [`Deposit` logs](#deposit-logs) - - [`ChainStart` log](#chainstart-log) - - [Vyper code](#vyper-code) - - [On startup](#on-startup) - - [Beacon chain processing](#beacon-chain-processing) - - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Per-slot processing](#per-slot-processing) - - [Slot](#slot) - - [Block roots](#block-roots) - - [Per-block processing](#per-block-processing) - - [Slot](#slot-1) - - [Proposer signature](#proposer-signature) - - [RANDAO](#randao) - - [Eth1 data](#eth1-data) - - [Operations](#operations) - - [Proposer slashings](#proposer-slashings-1) - - [Attester slashings](#attester-slashings-1) - - [Attestations](#attestations-1) - - [Deposits](#deposits-1) - - [Exits](#exits-1) - - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) - - [Eth1 data](#eth1-data-1) - - [Justification](#justification) - - [Crosslinks](#crosslinks) - - [Rewards and penalties](#rewards-and-penalties) - - [Justification and finalization](#justification-and-finalization) - - [Attestation inclusion](#attestation-inclusion) - - [Crosslinks](#crosslinks-1) - - [Ejections](#ejections) - - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) - - [Final updates](#final-updates) - - [State root verification](#state-root-verification) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Notation](#notation) + - [Terminology](#terminology) + - [Constants](#constants) + - [Misc](#misc) + - [Deposit contract](#deposit-contract) + - [Gwei values](#gwei-values) + - [Initial values](#initial-values) + - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) + - [Reward and penalty quotients](#reward-and-penalty-quotients) + - [Status flags](#status-flags) + - [Max operations per block](#max-operations-per-block) + - [Signature domains](#signature-domains) + - [Data structures](#data-structures) + - [Beacon chain operations](#beacon-chain-operations) + - [Proposer slashings](#proposer-slashings) + - [`ProposerSlashing`](#proposerslashing) + - [Attester slashings](#attester-slashings) + - [`AttesterSlashing`](#attesterslashing) + - [`SlashableAttestation`](#slashableattestation) + - [Attestations](#attestations) + - [`Attestation`](#attestation) + - [`AttestationData`](#attestationdata) + - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) + - [Deposits](#deposits) + - [`Deposit`](#deposit) + - [`DepositData`](#depositdata) + - [`DepositInput`](#depositinput) + - [Exits](#exits) + - [`Exit`](#exit) + - [Beacon chain blocks](#beacon-chain-blocks) + - [`BeaconBlock`](#beaconblock) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ProposalSignedData`](#proposalsigneddata) + - [Beacon chain state](#beacon-chain-state) + - [`BeaconState`](#beaconstate) + - [`Validator`](#validator) + - [`Crosslink`](#crosslink) + - [`PendingAttestation`](#pendingattestation) + - [`Fork`](#fork) + - [`Eth1Data`](#eth1data) + - [`Eth1DataVote`](#eth1datavote) + - [Custom Types](#custom-types) + - [Helper functions](#helper-functions) + - [`hash`](#hash) + - [`hash_tree_root`](#hashtreeroot) + - [`slot_to_epoch`](#slottoepoch) + - [`get_previous_epoch`](#getpreviousepoch) + - [`get_current_epoch`](#getcurrentepoch) + - [`get_epoch_start_slot`](#getepochstartslot) + - [`is_active_validator`](#isactivevalidator) + - [`get_active_validator_indices`](#getactivevalidatorindices) + - [`shuffle`](#shuffle) + - [`split`](#split) + - [`get_epoch_committee_count`](#getepochcommitteecount) + - [`get_shuffling`](#getshuffling) + - [`get_previous_epoch_committee_count`](#getpreviousepochcommitteecount) + - [`get_current_epoch_committee_count`](#getcurrentepochcommitteecount) + - [`get_next_epoch_committee_count`](#getnextepochcommitteecount) + - [`get_crosslink_committees_at_slot`](#getcrosslinkcommitteesatslot) + - [`get_block_root`](#getblockroot) + - [`get_randao_mix`](#getrandaomix) + - [`get_active_index_root`](#getactiveindexroot) + - [`generate_seed`](#generateseed) + - [`get_beacon_proposer_index`](#getbeaconproposerindex) + - [`merkle_root`](#merkleroot) + - [`get_attestation_participants`](#getattestationparticipants) + - [`is_power_of_two`](#ispoweroftwo) + - [`int_to_bytes1`, `int_to_bytes2`, ...](#inttobytes1-inttobytes2) + - [`get_effective_balance`](#geteffectivebalance) + - [`get_total_balance`](#gettotalbalance) + - [`get_fork_version`](#getforkversion) + - [`get_domain`](#getdomain) + - [`get_bitfield_bit`](#getbitfieldbit) + - [`verify_bitfield`](#verifybitfield) + - [`verify_slashable_attestation`](#verifyslashableattestation) + - [`is_double_vote`](#isdoublevote) + - [`is_surround_vote`](#issurroundvote) + - [`integer_squareroot`](#integersquareroot) + - [`get_entry_exit_effect_epoch`](#getentryexiteffectepoch) + - [`bls_verify`](#blsverify) + - [`bls_verify_multiple`](#blsverifymultiple) + - [`bls_aggregate_pubkeys`](#blsaggregatepubkeys) + - [`validate_proof_of_possession`](#validateproofofpossession) + - [`process_deposit`](#processdeposit) + - [Routines for updating validator status](#routines-for-updating-validator-status) + - [`activate_validator`](#activatevalidator) + - [`initiate_validator_exit`](#initiatevalidatorexit) + - [`exit_validator`](#exitvalidator) + - [`penalize_validator`](#penalizevalidator) + - [`prepare_validator_for_withdrawal`](#preparevalidatorforwithdrawal) + - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) + - [Deposit arguments](#deposit-arguments) + - [Withdrawal credentials](#withdrawal-credentials) + - [`Deposit` logs](#deposit-logs) + - [`ChainStart` log](#chainstart-log) + - [Vyper code](#vyper-code) + - [On startup](#on-startup) + - [Beacon chain processing](#beacon-chain-processing) + - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) + - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Per-slot processing](#per-slot-processing) + - [Slot](#slot) + - [Block roots](#block-roots) + - [Per-block processing](#per-block-processing) + - [Slot](#slot-1) + - [Proposer signature](#proposer-signature) + - [RANDAO](#randao) + - [Eth1 data](#eth1-data) + - [Operations](#operations) + - [Proposer slashings](#proposer-slashings-1) + - [Attester slashings](#attester-slashings-1) + - [Attestations](#attestations-1) + - [Deposits](#deposits-1) + - [Exits](#exits-1) + - [Per-epoch processing](#per-epoch-processing) + - [Helpers](#helpers) + - [Eth1 data](#eth1-data-1) + - [Justification](#justification) + - [Crosslinks](#crosslinks) + - [Rewards and penalties](#rewards-and-penalties) + - [Justification and finalization](#justification-and-finalization) + - [Attestation inclusion](#attestation-inclusion) + - [Crosslinks](#crosslinks-1) + - [Ejections](#ejections) + - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Final updates](#final-updates) + - [State root verification](#state-root-verification) - [References](#references) - - [Normative](#normative) - - [Informative](#informative) + - [Normative](#normative) + - [Informative](#informative) - [Copyright](#copyright) @@ -1052,7 +1052,7 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: ### `get_total_balance` ```python -def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: +def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: """ Return the combined effective balance of an array of validators. """ @@ -1150,7 +1150,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas 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]), ], - messages=[ + message_hashes=[ hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b0)), hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)), ], @@ -1245,7 +1245,7 @@ def validate_proof_of_possession(state: BeaconState, return bls_verify( pubkey=pubkey, - message=hash_tree_root(proof_of_possession_data), + message_hash=hash_tree_root(proof_of_possession_data), signature=proof_of_possession, domain=get_domain( state.fork, @@ -1633,12 +1633,12 @@ Below are the processing steps that happen at every `block`. * Let `block_without_signature_root` be the `hash_tree_root` of `block` where `block.signature` is set to `EMPTY_SIGNATURE`. * Let `proposal_root = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_without_signature_root))`. -* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message_hash=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. #### RANDAO * Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. * Set `state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = xor(get_randao_mix(state, get_current_epoch(state)), hash(block.randao_reveal))`. #### Eth1 data @@ -1659,8 +1659,8 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. * Verify that `proposer.penalized_epoch > get_current_epoch(state)`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. ##### Attester slashings @@ -1772,7 +1772,7 @@ For each `exit` in `block.body.exits`: * Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. * Verify that `get_current_epoch(state) >= exit.epoch`. * Let `exit_message = hash_tree_root(Exit(epoch=exit.epoch, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. +* Verify that `bls_verify(pubkey=validator.pubkey, message_hash=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. ### Per-epoch processing diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 4d8d5eadf..b8db2117c 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -7,53 +7,53 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Ethereum 2.0 Phase 0 -- Honest Validator](#ethereum-20-phase-0----honest-validator) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Prerequisites](#prerequisites) - - [Constants](#constants) - - [Misc](#misc) - - [Becoming a validator](#becoming-a-validator) - - [Initialization](#initialization) - - [BLS public key](#bls-public-key) - - [BLS withdrawal key](#bls-withdrawal-key) - - [Submit deposit](#submit-deposit) - - [Process deposit](#process-deposit) - - [Validator index](#validator-index) - - [Activation](#activation) - - [Beacon chain responsibilities](#beacon-chain-responsibilities) - - [Block proposal](#block-proposal) - - [Block header](#block-header) - - [Slot](#slot) - - [Parent root](#parent-root) - - [State root](#state-root) - - [Randao reveal](#randao-reveal) - - [Eth1 Data](#eth1-data) - - [Signature](#signature) - - [Block body](#block-body) - - [Proposer slashings](#proposer-slashings) - - [Attester slashings](#attester-slashings) - - [Attestations](#attestations) - - [Deposits](#deposits) - - [Exits](#exits) - - [Attestations](#attestations-1) - - [Attestation data](#attestation-data) - - [Slot](#slot-1) - - [Shard](#shard) - - [Beacon block root](#beacon-block-root) - - [Epoch boundary root](#epoch-boundary-root) - - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) - - [Justified epoch](#justified-epoch) - - [Justified block root](#justified-block-root) - - [Construct attestation](#construct-attestation) - - [Data](#data) - - [Aggregation bitfield](#aggregation-bitfield) - - [Custody bitfield](#custody-bitfield) - - [Aggregate signature](#aggregate-signature) - - [Responsibility lookahead](#responsibility-lookahead) - - [How to avoid slashing](#how-to-avoid-slashing) - - [Proposer slashing](#proposer-slashing) - - [Attester slashing](#attester-slashing) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Prerequisites](#prerequisites) + - [Constants](#constants) + - [Misc](#misc) + - [Becoming a validator](#becoming-a-validator) + - [Initialization](#initialization) + - [BLS public key](#bls-public-key) + - [BLS withdrawal key](#bls-withdrawal-key) + - [Submit deposit](#submit-deposit) + - [Process deposit](#process-deposit) + - [Validator index](#validator-index) + - [Activation](#activation) + - [Beacon chain responsibilities](#beacon-chain-responsibilities) + - [Block proposal](#block-proposal) + - [Block header](#block-header) + - [Slot](#slot) + - [Parent root](#parent-root) + - [State root](#state-root) + - [Randao reveal](#randao-reveal) + - [Eth1 Data](#eth1-data) + - [Signature](#signature) + - [Block body](#block-body) + - [Proposer slashings](#proposer-slashings) + - [Attester slashings](#attester-slashings) + - [Attestations](#attestations) + - [Deposits](#deposits) + - [Exits](#exits) + - [Attestations](#attestations-1) + - [Attestation data](#attestation-data) + - [Slot](#slot-1) + - [Shard](#shard) + - [Beacon block root](#beacon-block-root) + - [Epoch boundary root](#epoch-boundary-root) + - [Shard block root](#shard-block-root) + - [Latest crosslink root](#latest-crosslink-root) + - [Justified epoch](#justified-epoch) + - [Justified block root](#justified-block-root) + - [Construct attestation](#construct-attestation) + - [Data](#data) + - [Aggregation bitfield](#aggregation-bitfield) + - [Custody bitfield](#custody-bitfield) + - [Aggregate signature](#aggregate-signature) + - [Responsibility lookahead](#responsibility-lookahead) + - [How to avoid slashing](#how-to-avoid-slashing) + - [Proposer slashing](#proposer-slashing) + - [Attester slashing](#attester-slashing) @@ -95,7 +95,7 @@ The validator constructs their `withdrawal_credentials` via the following: ### Submit deposit -In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. +In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. To submit a deposit: @@ -166,7 +166,7 @@ Set `block.randao_reveal = epoch_signature` where `epoch_signature` is defined a ```python epoch_signature = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=int_to_bytes32(slot_to_epoch(block.slot)), + message_hash=int_to_bytes32(slot_to_epoch(block.slot)), domain=get_domain( fork=fork, # `fork` is the fork object at the slot `block.slot` epoch=slot_to_epoch(block.slot), @@ -205,7 +205,7 @@ proposal_root = hash_tree_root(proposal_data) signed_proposal_data = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=proposal_root, + message_hash=proposal_root, domain=get_domain( fork=fork, # `fork` is the fork object at the slot `block.slot` epoch=slot_to_epoch(block.slot), @@ -321,7 +321,7 @@ attestation_message_to_sign = hash_tree_root(attestation_data_and_custody_bit) signed_attestation_data = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=attestation_message_to_sign, + message_hash=attestation_message_to_sign, domain=get_domain( fork=fork, # `fork` is the fork object at the slot, `attestation_data.slot` epoch=slot_to_epoch(attestation_data.slot), From 911e4f104bde97f4361e78c7e14c9d09d6c6b5da Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:12:58 +0800 Subject: [PATCH 19/40] Add `bytes_to_int` --- specs/core/0_beacon-chain.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 38d233b23..c7103b79e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -76,6 +76,7 @@ - [`get_attestation_participants`](#get_attestation_participants) - [`is_power_of_two`](#is_power_of_two) - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) + - [`bytes_to_int`](#bytes_to_int) - [`get_effective_balance`](#get_effective_balance) - [`get_total_balance`](#get_total_balance) - [`get_fork_version`](#get_fork_version) @@ -707,7 +708,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: i See the 'generalized domain' algorithm on page 3. """ for round in range(round_count): - pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size + pivot = bytes_to_int(hash(seed + int_to_bytes1(round))) % list_size flip = (pivot - index) % list_size position = max(index, flip) source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) @@ -1007,6 +1008,13 @@ def is_power_of_two(value: int) -> bool: `int_to_bytes1(x): return x.to_bytes(1, 'big')`, `int_to_bytes2(x): return x.to_bytes(2, 'big')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. +### `bytes_to_int` + +```python +def bytes_to_int(data: bytes) -> int: + return int.from_bytes(data, 'little') +``` + ### `get_effective_balance` ```python From 89b9894328b6a2060c3fbe61d2079ac400ca1208 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:15:42 +0800 Subject: [PATCH 20/40] Fix type hinting --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c7103b79e..7167b93a6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,7 +697,7 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ### `get_permuted_index` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> List[int]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. From fd3d4a5105db74016483c1d1abbf81c932889b7f Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:15:55 +0100 Subject: [PATCH 21/40] Don't change TOC --- specs/core/0_beacon-chain.md | 264 +++++++++++++++++------------------ 1 file changed, 132 insertions(+), 132 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e6cf019f4..d4e90a57b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -6,139 +6,139 @@ - [Ethereum 2.0 Phase 0 -- The Beacon Chain](#ethereum-20-phase-0----the-beacon-chain) - - [Table of contents](#table-of-contents) - - [Introduction](#introduction) - - [Notation](#notation) - - [Terminology](#terminology) - - [Constants](#constants) - - [Misc](#misc) - - [Deposit contract](#deposit-contract) - - [Gwei values](#gwei-values) - - [Initial values](#initial-values) - - [Time parameters](#time-parameters) - - [State list lengths](#state-list-lengths) - - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - - [Max operations per block](#max-operations-per-block) - - [Signature domains](#signature-domains) - - [Data structures](#data-structures) - - [Beacon chain operations](#beacon-chain-operations) - - [Proposer slashings](#proposer-slashings) - - [`ProposerSlashing`](#proposerslashing) - - [Attester slashings](#attester-slashings) - - [`AttesterSlashing`](#attesterslashing) - - [`SlashableAttestation`](#slashableattestation) - - [Attestations](#attestations) - - [`Attestation`](#attestation) - - [`AttestationData`](#attestationdata) - - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [Deposits](#deposits) - - [`Deposit`](#deposit) - - [`DepositData`](#depositdata) - - [`DepositInput`](#depositinput) - - [Exits](#exits) - - [`Exit`](#exit) - - [Beacon chain blocks](#beacon-chain-blocks) - - [`BeaconBlock`](#beaconblock) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ProposalSignedData`](#proposalsigneddata) - - [Beacon chain state](#beacon-chain-state) - - [`BeaconState`](#beaconstate) - - [`Validator`](#validator) - - [`Crosslink`](#crosslink) - - [`PendingAttestation`](#pendingattestation) - - [`Fork`](#fork) - - [`Eth1Data`](#eth1data) - - [`Eth1DataVote`](#eth1datavote) - - [Custom Types](#custom-types) - - [Helper functions](#helper-functions) - - [`hash`](#hash) - - [`hash_tree_root`](#hashtreeroot) - - [`slot_to_epoch`](#slottoepoch) - - [`get_previous_epoch`](#getpreviousepoch) - - [`get_current_epoch`](#getcurrentepoch) - - [`get_epoch_start_slot`](#getepochstartslot) - - [`is_active_validator`](#isactivevalidator) - - [`get_active_validator_indices`](#getactivevalidatorindices) - - [`shuffle`](#shuffle) - - [`split`](#split) - - [`get_epoch_committee_count`](#getepochcommitteecount) - - [`get_shuffling`](#getshuffling) - - [`get_previous_epoch_committee_count`](#getpreviousepochcommitteecount) - - [`get_current_epoch_committee_count`](#getcurrentepochcommitteecount) - - [`get_next_epoch_committee_count`](#getnextepochcommitteecount) - - [`get_crosslink_committees_at_slot`](#getcrosslinkcommitteesatslot) - - [`get_block_root`](#getblockroot) - - [`get_randao_mix`](#getrandaomix) - - [`get_active_index_root`](#getactiveindexroot) - - [`generate_seed`](#generateseed) - - [`get_beacon_proposer_index`](#getbeaconproposerindex) - - [`merkle_root`](#merkleroot) - - [`get_attestation_participants`](#getattestationparticipants) - - [`is_power_of_two`](#ispoweroftwo) - - [`int_to_bytes1`, `int_to_bytes2`, ...](#inttobytes1-inttobytes2) - - [`get_effective_balance`](#geteffectivebalance) - - [`get_total_balance`](#gettotalbalance) - - [`get_fork_version`](#getforkversion) - - [`get_domain`](#getdomain) - - [`get_bitfield_bit`](#getbitfieldbit) - - [`verify_bitfield`](#verifybitfield) - - [`verify_slashable_attestation`](#verifyslashableattestation) - - [`is_double_vote`](#isdoublevote) - - [`is_surround_vote`](#issurroundvote) - - [`integer_squareroot`](#integersquareroot) - - [`get_entry_exit_effect_epoch`](#getentryexiteffectepoch) - - [`bls_verify`](#blsverify) - - [`bls_verify_multiple`](#blsverifymultiple) - - [`bls_aggregate_pubkeys`](#blsaggregatepubkeys) - - [`validate_proof_of_possession`](#validateproofofpossession) - - [`process_deposit`](#processdeposit) - - [Routines for updating validator status](#routines-for-updating-validator-status) - - [`activate_validator`](#activatevalidator) - - [`initiate_validator_exit`](#initiatevalidatorexit) - - [`exit_validator`](#exitvalidator) - - [`penalize_validator`](#penalizevalidator) - - [`prepare_validator_for_withdrawal`](#preparevalidatorforwithdrawal) - - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - - [Deposit arguments](#deposit-arguments) - - [Withdrawal credentials](#withdrawal-credentials) - - [`Deposit` logs](#deposit-logs) - - [`ChainStart` log](#chainstart-log) - - [Vyper code](#vyper-code) - - [On startup](#on-startup) - - [Beacon chain processing](#beacon-chain-processing) - - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Per-slot processing](#per-slot-processing) - - [Slot](#slot) - - [Block roots](#block-roots) - - [Per-block processing](#per-block-processing) - - [Slot](#slot-1) - - [Proposer signature](#proposer-signature) - - [RANDAO](#randao) - - [Eth1 data](#eth1-data) - - [Operations](#operations) - - [Proposer slashings](#proposer-slashings-1) - - [Attester slashings](#attester-slashings-1) - - [Attestations](#attestations-1) - - [Deposits](#deposits-1) - - [Exits](#exits-1) - - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) - - [Eth1 data](#eth1-data-1) - - [Justification](#justification) - - [Crosslinks](#crosslinks) - - [Rewards and penalties](#rewards-and-penalties) - - [Justification and finalization](#justification-and-finalization) - - [Attestation inclusion](#attestation-inclusion) - - [Crosslinks](#crosslinks-1) - - [Ejections](#ejections) - - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) - - [Final updates](#final-updates) - - [State root verification](#state-root-verification) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Notation](#notation) + - [Terminology](#terminology) + - [Constants](#constants) + - [Misc](#misc) + - [Deposit contract](#deposit-contract) + - [Gwei values](#gwei-values) + - [Initial values](#initial-values) + - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) + - [Reward and penalty quotients](#reward-and-penalty-quotients) + - [Status flags](#status-flags) + - [Max operations per block](#max-operations-per-block) + - [Signature domains](#signature-domains) + - [Data structures](#data-structures) + - [Beacon chain operations](#beacon-chain-operations) + - [Proposer slashings](#proposer-slashings) + - [`ProposerSlashing`](#proposerslashing) + - [Attester slashings](#attester-slashings) + - [`AttesterSlashing`](#attesterslashing) + - [`SlashableAttestation`](#slashableattestation) + - [Attestations](#attestations) + - [`Attestation`](#attestation) + - [`AttestationData`](#attestationdata) + - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) + - [Deposits](#deposits) + - [`Deposit`](#deposit) + - [`DepositData`](#depositdata) + - [`DepositInput`](#depositinput) + - [Exits](#exits) + - [`Exit`](#exit) + - [Beacon chain blocks](#beacon-chain-blocks) + - [`BeaconBlock`](#beaconblock) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ProposalSignedData`](#proposalsigneddata) + - [Beacon chain state](#beacon-chain-state) + - [`BeaconState`](#beaconstate) + - [`Validator`](#validator) + - [`Crosslink`](#crosslink) + - [`PendingAttestation`](#pendingattestation) + - [`Fork`](#fork) + - [`Eth1Data`](#eth1data) + - [`Eth1DataVote`](#eth1datavote) + - [Custom Types](#custom-types) + - [Helper functions](#helper-functions) + - [`hash`](#hash) + - [`hash_tree_root`](#hash_tree_root) + - [`slot_to_epoch`](#slot_to_epoch) + - [`get_previous_epoch`](#get_previous_epoch) + - [`get_current_epoch`](#get_current_epoch) + - [`get_epoch_start_slot`](#get_epoch_start_slot) + - [`is_active_validator`](#is_active_validator) + - [`get_active_validator_indices`](#get_active_validator_indices) + - [`shuffle`](#shuffle) + - [`split`](#split) + - [`get_epoch_committee_count`](#get_epoch_committee_count) + - [`get_shuffling`](#get_shuffling) + - [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count) + - [`get_current_epoch_committee_count`](#get_current_epoch_committee_count) + - [`get_next_epoch_committee_count`](#get_next_epoch_committee_count) + - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) + - [`get_block_root`](#get_block_root) + - [`get_randao_mix`](#get_randao_mix) + - [`get_active_index_root`](#get_active_index_root) + - [`generate_seed`](#generate_seed) + - [`get_beacon_proposer_index`](#get_beacon_proposer_index) + - [`merkle_root`](#merkle_root) + - [`get_attestation_participants`](#get_attestation_participants) + - [`is_power_of_two`](#is_power_of_two) + - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) + - [`get_effective_balance`](#get_effective_balance) + - [`get_total_balance`](#get_total_balance) + - [`get_fork_version`](#get_fork_version) + - [`get_domain`](#get_domain) + - [`get_bitfield_bit`](#get_bitfield_bit) + - [`verify_bitfield`](#verify_bitfield) + - [`verify_slashable_attestation`](#verify_slashable_attestation) + - [`is_double_vote`](#is_double_vote) + - [`is_surround_vote`](#is_surround_vote) + - [`integer_squareroot`](#integer_squareroot) + - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) + - [`bls_verify`](#bls_verify) + - [`bls_verify_multiple`](#bls_verify_multiple) + - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) + - [`validate_proof_of_possession`](#validate_proof_of_possession) + - [`process_deposit`](#process_deposit) + - [Routines for updating validator status](#routines-for-updating-validator-status) + - [`activate_validator`](#activate_validator) + - [`initiate_validator_exit`](#initiate_validator_exit) + - [`exit_validator`](#exit_validator) + - [`penalize_validator`](#penalize_validator) + - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) + - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) + - [Deposit arguments](#deposit-arguments) + - [Withdrawal credentials](#withdrawal-credentials) + - [`Deposit` logs](#deposit-logs) + - [`ChainStart` log](#chainstart-log) + - [Vyper code](#vyper-code) + - [On startup](#on-startup) + - [Beacon chain processing](#beacon-chain-processing) + - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) + - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Per-slot processing](#per-slot-processing) + - [Slot](#slot) + - [Block roots](#block-roots) + - [Per-block processing](#per-block-processing) + - [Slot](#slot-1) + - [Proposer signature](#proposer-signature) + - [RANDAO](#randao) + - [Eth1 data](#eth1-data) + - [Operations](#operations) + - [Proposer slashings](#proposer-slashings-1) + - [Attester slashings](#attester-slashings-1) + - [Attestations](#attestations-1) + - [Deposits](#deposits-1) + - [Exits](#exits-1) + - [Per-epoch processing](#per-epoch-processing) + - [Helpers](#helpers) + - [Eth1 data](#eth1-data-1) + - [Justification](#justification) + - [Crosslinks](#crosslinks) + - [Rewards and penalties](#rewards-and-penalties) + - [Justification and finalization](#justification-and-finalization) + - [Attestation inclusion](#attestation-inclusion) + - [Crosslinks](#crosslinks-1) + - [Ejections](#ejections) + - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Final updates](#final-updates) + - [State root verification](#state-root-verification) - [References](#references) - - [Normative](#normative) - - [Informative](#informative) + - [Normative](#normative) + - [Informative](#informative) - [Copyright](#copyright) From ca098f8cfa5c0ab0daa38c784ee4e563fd1c9fdf Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:19:04 +0100 Subject: [PATCH 22/40] Prevent changing another TOC --- specs/validator/0_beacon-chain-validator.md | 94 ++++++++++----------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index b8db2117c..744df690f 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -7,53 +7,53 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Ethereum 2.0 Phase 0 -- Honest Validator](#ethereum-20-phase-0----honest-validator) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Prerequisites](#prerequisites) - - [Constants](#constants) - - [Misc](#misc) - - [Becoming a validator](#becoming-a-validator) - - [Initialization](#initialization) - - [BLS public key](#bls-public-key) - - [BLS withdrawal key](#bls-withdrawal-key) - - [Submit deposit](#submit-deposit) - - [Process deposit](#process-deposit) - - [Validator index](#validator-index) - - [Activation](#activation) - - [Beacon chain responsibilities](#beacon-chain-responsibilities) - - [Block proposal](#block-proposal) - - [Block header](#block-header) - - [Slot](#slot) - - [Parent root](#parent-root) - - [State root](#state-root) - - [Randao reveal](#randao-reveal) - - [Eth1 Data](#eth1-data) - - [Signature](#signature) - - [Block body](#block-body) - - [Proposer slashings](#proposer-slashings) - - [Attester slashings](#attester-slashings) - - [Attestations](#attestations) - - [Deposits](#deposits) - - [Exits](#exits) - - [Attestations](#attestations-1) - - [Attestation data](#attestation-data) - - [Slot](#slot-1) - - [Shard](#shard) - - [Beacon block root](#beacon-block-root) - - [Epoch boundary root](#epoch-boundary-root) - - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) - - [Justified epoch](#justified-epoch) - - [Justified block root](#justified-block-root) - - [Construct attestation](#construct-attestation) - - [Data](#data) - - [Aggregation bitfield](#aggregation-bitfield) - - [Custody bitfield](#custody-bitfield) - - [Aggregate signature](#aggregate-signature) - - [Responsibility lookahead](#responsibility-lookahead) - - [How to avoid slashing](#how-to-avoid-slashing) - - [Proposer slashing](#proposer-slashing) - - [Attester slashing](#attester-slashing) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Prerequisites](#prerequisites) + - [Constants](#constants) + - [Misc](#misc) + - [Becoming a validator](#becoming-a-validator) + - [Initialization](#initialization) + - [BLS public key](#bls-public-key) + - [BLS withdrawal key](#bls-withdrawal-key) + - [Submit deposit](#submit-deposit) + - [Process deposit](#process-deposit) + - [Validator index](#validator-index) + - [Activation](#activation) + - [Beacon chain responsibilities](#beacon-chain-responsibilities) + - [Block proposal](#block-proposal) + - [Block header](#block-header) + - [Slot](#slot) + - [Parent root](#parent-root) + - [State root](#state-root) + - [Randao reveal](#randao-reveal) + - [Eth1 Data](#eth1-data) + - [Signature](#signature) + - [Block body](#block-body) + - [Proposer slashings](#proposer-slashings) + - [Attester slashings](#attester-slashings) + - [Attestations](#attestations) + - [Deposits](#deposits) + - [Exits](#exits) + - [Attestations](#attestations-1) + - [Attestation data](#attestation-data) + - [Slot](#slot-1) + - [Shard](#shard) + - [Beacon block root](#beacon-block-root) + - [Epoch boundary root](#epoch-boundary-root) + - [Shard block root](#shard-block-root) + - [Latest crosslink root](#latest-crosslink-root) + - [Justified epoch](#justified-epoch) + - [Justified block root](#justified-block-root) + - [Construct attestation](#construct-attestation) + - [Data](#data) + - [Aggregation bitfield](#aggregation-bitfield) + - [Custody bitfield](#custody-bitfield) + - [Aggregate signature](#aggregate-signature) + - [Responsibility lookahead](#responsibility-lookahead) + - [How to avoid slashing](#how-to-avoid-slashing) + - [Proposer slashing](#proposer-slashing) + - [Attester slashing](#attester-slashing) From 086df84bdc6805d1c0ad1e1c4ff953fa1bd3802b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 7 Feb 2019 19:34:54 -0600 Subject: [PATCH 23/40] Attestation data contains latest crosslink, not just latest crosslink data The reason to do this is that it makes it calculable from inside an attestation how many epochs the attestation spans over, which is needed for proof of custody reasons. It's a relatively small change and so arguably easier to do now than to do as a patch in phase 1. Note that this changes the meaning of latest_crosslink.epoch, from the epoch when the latest crosslink was included to the epoch that the latest crosslink was for. This affects the line: * `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state))]` (that is, for every shard in the current committees) But this may actually make it _more_ correct, as it means that in the case where >512 shards are processed per epoch, and so a committee from the previous epoch could get finalized in the current epoch, that would no longer count toward every shard having received a "new" crosslink. --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f9c155c53..b98bdf3b8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -362,8 +362,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'epoch_boundary_root': 'bytes32', # Shard block's hash of root 'shard_block_root': 'bytes32', - # Last crosslink's hash of root - 'latest_crosslink_root': 'bytes32', + # Last crosslink + 'latest_crosslink': Crosslink, # Last justified epoch in the beacon state 'justified_epoch': 'uint64', # Hash of the last justified beacon block @@ -1688,7 +1688,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. -* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[attestation.data.shard].shard_block_root`. +* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))` * Verify bitfields and aggregate signature: ```python @@ -1860,7 +1860,7 @@ Finally, update the following: For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: -* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. +* Set `state.latest_crosslinks[shard] = Crosslink(epoch=slot_to_epoch(slot), shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. #### Rewards and penalties From f797826ee29767ef3133a97ec1f28d347553ede7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 21:51:56 -0600 Subject: [PATCH 24/40] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7167b93a6..50daad513 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -708,7 +708,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: i See the 'generalized domain' algorithm on page 3. """ for round in range(round_count): - pivot = bytes_to_int(hash(seed + int_to_bytes1(round))) % list_size + pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size flip = (pivot - index) % list_size position = max(index, flip) source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) From 1c6ccac8fc1f02079f49bf9fd715b64764c304e0 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 7 Feb 2019 21:55:33 -0600 Subject: [PATCH 25/40] SHUFFLE_ROUND_COUNT as global constant --- specs/core/0_beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 50daad513..a7a21683c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -185,6 +185,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | votes | | `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | +| `SHUFFLE_ROUND_COUNT` | 90 | - | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -697,17 +698,15 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ### `get_permuted_index` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> int: +def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. - Note that ``round_count`` is ``90`` in protocol and parameterized for the shuffling tests. - Utilizes 'swap or not' shuffling found in 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(round_count): + for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size flip = (pivot - index) % list_size position = max(index, flip) From f0cbacb828baa6ed454bea5d5efe52b0a73b67f5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 20:22:28 -0800 Subject: [PATCH 26/40] add missing . --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b98bdf3b8..f66839349 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1688,7 +1688,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. -* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))` +* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))`. * Verify bitfields and aggregate signature: ```python From 6d9581281dba8c8d2cf127ddd07e6202759c405c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 21:25:47 -0700 Subject: [PATCH 27/40] change latest_crosslink_root to latest_crosslink in validator guide --- specs/validator/0_beacon-chain-validator.md | 6 +- wire-api.md | 93 +++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 wire-api.md diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 744df690f..d1246c6fa 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -42,7 +42,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Beacon block root](#beacon-block-root) - [Epoch boundary root](#epoch-boundary-root) - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) + - [Latest crosslink](#latest-crosslink) - [Justified epoch](#justified-epoch) - [Justified block root](#justified-block-root) - [Construct attestation](#construct-attestation) @@ -270,9 +270,9 @@ Set `attestation_data.shard_block_root = ZERO_HASH`. _Note:_ This is a stub for phase 0. -##### Latest crosslink root +##### Latest crosslink -Set `attestation_data.latest_crosslink_root = state.latest_crosslinks[shard].shard_block_root` where `state` is the beacon state at `head` and `shard` is the validator's assigned shard. +Set `attestation_data.latest_crosslink = state.latest_crosslinks[shard]` where `state` is the beacon state at `head` and `shard` is the validator's assigned shard. ##### Justified epoch diff --git a/wire-api.md b/wire-api.md new file mode 100644 index 000000000..0eff650bd --- /dev/null +++ b/wire-api.md @@ -0,0 +1,93 @@ +# Phase 0 Wire API [WIP] + +This is the minimal wire API required for Phase 0 of Eth2.0. Note that this is _not_ the wire protocol but the interface right above. Once we settle on the API required, we can specify the underlying protocol. + +All API methods are specified as the plural `list` version, assuming that if singular objects are sent or requested that the input will just be a list of length 1. + +"Bad form" is any action that is not explicitly against the protocol but is not in the best interest of one's peers or the protocol in general. Messages/requests that are considered bad form may reduce the reputation of the sending node and may result in being dropped. + +## Network topology + +Ethereum 2.0 network topology consists of a pubsub mapping of peers to "topics". These topics along with peer mappings effectively form subnets. + +The primary topics of core protocol consideration are: +* `beacon`: All messages for the beacon chain are mapped to topic `beacon`. +* `shard-{number}` for all integers, `number` in `range(SHARD_SUBNET_COUNT)`: Messages for a given shard defined by `shard_number` are mapped to topic `shard-{shard_number % SHARD_SUBNET_COUNT}`. + +We use `discv5` to discover peers of select topics, and we use `gossipsub`, a libp2p routing protocol, to route messages of a particular topic to the subnet in question. + +Note: attempting to broadcast or request messages about a topic not subscribed to by the peer is considered bad form. For example, running `send_attestations(attestations)` where one or more of the attestations have `attestation.data.shard == 5` to a peer not subscribed to `shard-5` might result in that peer dropping the node. + +## Dependencies + +This document depends on: +* [SSZ spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) +* [Phase 0 spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md) + +## API + +### Sync + +The following is a basic sync protocol akin to eth1.0. _This is very likely to change pending input from those intimately familiar with the pain points of 1.0 sync_. + +`status` message is sent in the initial handshake between two peers. After handshake and status exchange, the peer with higher `latest_finalized_epoch` or, if epochs are equal, the higher `best_slot` sends a list of `beacon_block_roots` via `send_beacon_block_roots`. + +Status handshake fields: +* `protocol_version` +* `network_id` +* `latest_finalized_root` +* `latest_finalized_epoch` +* `best_root` +* `best_slot` + +### Beacon Blocks + +Supported pubsub topics: +* `beacon` + +The following definitions are used in the API: +* `block_header`: a serialized `BeaconBlock` in which `BeaconBlock.body` is the `hash_tree_root` of the associated `BeaconBlockBody`. +* `block_body`: a serialied `BeaconBlockBody`. +* `block_root`: the `hash_tree_root` of a `BeaconBlock`. + +API: +* `send_beacon_block_roots(block_roots)`: Sends list of `block_roots` to peer. +* `send_beacon_block_headers(block_headers)`: Sends list of `block_headers` to peer. +* `request_beacon_block_headers(block_roots)`: Requests the associated `block_headers` for the given `block_roots` from peer. +* `send_beacon_block_bodies(block_bodies)`: Sends list of `block_bodies` to peer. +* `request_beacon_block_bodies(block_roots)`: Requests the associated `block_bodies` for the given `block_roots` from peer. + +Notes: +* It is assumed that both the associated `BeaconBlock` and `BeaconBlockBody` can be looked up via `block_root`. + +### Attestations + +Supported pubsub topics: +* `beacon` +* all `shard-{number}` topics + +The following definitions are used in the API: +* `attestation`: a serialized `Attestation` with full serialized `AttestationData` for `Attestation.data`. + +API: +* `send_attestations(attestations)`: Sends list of `attestations` to peer. + +Notes: +* It is expected that an attestation is only broadcast to either `beacon` topic or `shard-{attestation.data.shard}` topic. Broadcasting to mismatched shard topics is considered bad form. +* It is expected that only aggregate attestations are broadcast to the `beacon` topic. Repeated broadcasting of attestations with a signle signer to the `beacon` topic is considered bad form. +* There is a shard subnet design decision here. Due to the likelihood of `attestation.data` to be highly repeated across a committee during a given slot, it could be valuable to just pass the `attestation` with a `root` in the `attestation.data` field. If the recipient does not already have an `AttestationData` for the received `root`, then the recipient would explicitly request the root. This reduces the total data passed by 184 bytes in the case that the recipient has already received the `attestation.data` but increases the rounds of communication when they haven't. +* We do not currently specify a getter method for an attestation by its `root`. Due to the diverse ways attestations might both be aggregated and stored, it is not feasible to reliably lookup via a `root`. The attestations that a client cares about are (1) those that made it on-chain into a `BeaconBlock` and (2) the most recent set of attestations being actively broadcast on the wire. We might provide a `request_attestations(slot)` or `request_attestations(epoch)` but do not provide it in this minimal API specification. + +### Exits + +Supported pubsub topics: +* `beacon` + +The following definitions are used in the API: +* `exit`: a serialized `Exit`. + +API: +* `send_exit(exit)`: Sends `exit` to peer. + +Notes: +* We do not specify a getter for an exit by its `root`. Standard usage is for \ No newline at end of file From f9eaab1d044e2da9854217099b4bb8325388740f Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 8 Feb 2019 18:04:32 +0300 Subject: [PATCH 28/40] Silently skip deposits with invalid proof in process_deposit --- specs/core/0_beacon-chain.md | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 21a991c42..c08343ef9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1252,36 +1252,37 @@ def process_deposit(state: BeaconState, Note that this function mutates ``state``. """ # Validate the given `proof_of_possession` - assert validate_proof_of_possession( + valid_proof = validate_proof_of_possession( state, pubkey, proof_of_possession, withdrawal_credentials, ) - validator_pubkeys = [v.pubkey for v in state.validator_registry] - - if pubkey not in validator_pubkeys: - # Add new validator - validator = Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawal_epoch=FAR_FUTURE_EPOCH, - penalized_epoch=FAR_FUTURE_EPOCH, - status_flags=0, - ) - - # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. - state.validator_registry.append(validator) - state.validator_balances.append(amount) - else: - # Increase balance by deposit amount - index = validator_pubkeys.index(pubkey) - assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - - state.validator_balances[index] += amount + if valid_proof: + validator_pubkeys = [v.pubkey for v in state.validator_registry] + + if pubkey not in validator_pubkeys: + # Add new validator + validator = Validator( + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawal_epoch=FAR_FUTURE_EPOCH, + penalized_epoch=FAR_FUTURE_EPOCH, + status_flags=0, + ) + + # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. + state.validator_registry.append(validator) + state.validator_balances.append(amount) + else: + # Increase balance by deposit amount + index = validator_pubkeys.index(pubkey) + assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials + + state.validator_balances[index] += amount ``` ### Routines for updating validator status From 92471046a1879361d5c87443da0890db0e7d7846 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 08:28:41 -0700 Subject: [PATCH 29/40] remove wire protocol doc --- wire-api.md | 93 ----------------------------------------------------- 1 file changed, 93 deletions(-) delete mode 100644 wire-api.md diff --git a/wire-api.md b/wire-api.md deleted file mode 100644 index 0eff650bd..000000000 --- a/wire-api.md +++ /dev/null @@ -1,93 +0,0 @@ -# Phase 0 Wire API [WIP] - -This is the minimal wire API required for Phase 0 of Eth2.0. Note that this is _not_ the wire protocol but the interface right above. Once we settle on the API required, we can specify the underlying protocol. - -All API methods are specified as the plural `list` version, assuming that if singular objects are sent or requested that the input will just be a list of length 1. - -"Bad form" is any action that is not explicitly against the protocol but is not in the best interest of one's peers or the protocol in general. Messages/requests that are considered bad form may reduce the reputation of the sending node and may result in being dropped. - -## Network topology - -Ethereum 2.0 network topology consists of a pubsub mapping of peers to "topics". These topics along with peer mappings effectively form subnets. - -The primary topics of core protocol consideration are: -* `beacon`: All messages for the beacon chain are mapped to topic `beacon`. -* `shard-{number}` for all integers, `number` in `range(SHARD_SUBNET_COUNT)`: Messages for a given shard defined by `shard_number` are mapped to topic `shard-{shard_number % SHARD_SUBNET_COUNT}`. - -We use `discv5` to discover peers of select topics, and we use `gossipsub`, a libp2p routing protocol, to route messages of a particular topic to the subnet in question. - -Note: attempting to broadcast or request messages about a topic not subscribed to by the peer is considered bad form. For example, running `send_attestations(attestations)` where one or more of the attestations have `attestation.data.shard == 5` to a peer not subscribed to `shard-5` might result in that peer dropping the node. - -## Dependencies - -This document depends on: -* [SSZ spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) -* [Phase 0 spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md) - -## API - -### Sync - -The following is a basic sync protocol akin to eth1.0. _This is very likely to change pending input from those intimately familiar with the pain points of 1.0 sync_. - -`status` message is sent in the initial handshake between two peers. After handshake and status exchange, the peer with higher `latest_finalized_epoch` or, if epochs are equal, the higher `best_slot` sends a list of `beacon_block_roots` via `send_beacon_block_roots`. - -Status handshake fields: -* `protocol_version` -* `network_id` -* `latest_finalized_root` -* `latest_finalized_epoch` -* `best_root` -* `best_slot` - -### Beacon Blocks - -Supported pubsub topics: -* `beacon` - -The following definitions are used in the API: -* `block_header`: a serialized `BeaconBlock` in which `BeaconBlock.body` is the `hash_tree_root` of the associated `BeaconBlockBody`. -* `block_body`: a serialied `BeaconBlockBody`. -* `block_root`: the `hash_tree_root` of a `BeaconBlock`. - -API: -* `send_beacon_block_roots(block_roots)`: Sends list of `block_roots` to peer. -* `send_beacon_block_headers(block_headers)`: Sends list of `block_headers` to peer. -* `request_beacon_block_headers(block_roots)`: Requests the associated `block_headers` for the given `block_roots` from peer. -* `send_beacon_block_bodies(block_bodies)`: Sends list of `block_bodies` to peer. -* `request_beacon_block_bodies(block_roots)`: Requests the associated `block_bodies` for the given `block_roots` from peer. - -Notes: -* It is assumed that both the associated `BeaconBlock` and `BeaconBlockBody` can be looked up via `block_root`. - -### Attestations - -Supported pubsub topics: -* `beacon` -* all `shard-{number}` topics - -The following definitions are used in the API: -* `attestation`: a serialized `Attestation` with full serialized `AttestationData` for `Attestation.data`. - -API: -* `send_attestations(attestations)`: Sends list of `attestations` to peer. - -Notes: -* It is expected that an attestation is only broadcast to either `beacon` topic or `shard-{attestation.data.shard}` topic. Broadcasting to mismatched shard topics is considered bad form. -* It is expected that only aggregate attestations are broadcast to the `beacon` topic. Repeated broadcasting of attestations with a signle signer to the `beacon` topic is considered bad form. -* There is a shard subnet design decision here. Due to the likelihood of `attestation.data` to be highly repeated across a committee during a given slot, it could be valuable to just pass the `attestation` with a `root` in the `attestation.data` field. If the recipient does not already have an `AttestationData` for the received `root`, then the recipient would explicitly request the root. This reduces the total data passed by 184 bytes in the case that the recipient has already received the `attestation.data` but increases the rounds of communication when they haven't. -* We do not currently specify a getter method for an attestation by its `root`. Due to the diverse ways attestations might both be aggregated and stored, it is not feasible to reliably lookup via a `root`. The attestations that a client cares about are (1) those that made it on-chain into a `BeaconBlock` and (2) the most recent set of attestations being actively broadcast on the wire. We might provide a `request_attestations(slot)` or `request_attestations(epoch)` but do not provide it in this minimal API specification. - -### Exits - -Supported pubsub topics: -* `beacon` - -The following definitions are used in the API: -* `exit`: a serialized `Exit`. - -API: -* `send_exit(exit)`: Sends `exit` to peer. - -Notes: -* We do not specify a getter for an exit by its `root`. Standard usage is for \ No newline at end of file From 334d47714df2b3f41b82ad2adea2aa6636369c3e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 10:35:57 -0700 Subject: [PATCH 30/40] fix a couple of nitpicks before release --- specs/core/0_beacon-chain.md | 7 ++++--- specs/validator/0_beacon-chain-validator.md | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 21a991c42..ebc12324c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -658,9 +658,10 @@ def get_previous_epoch(state: BeaconState) -> EpochNumber: Return the previous epoch of the given ``state``. If the current epoch is ``GENESIS_EPOCH``, return ``GENESIS_EPOCH``. """ - if slot_to_epoch(state.slot) > GENESIS_EPOCH: - return slot_to_epoch(state.slot) - 1 - return slot_to_epoch(state.slot) + current_epoch = get_current_epoch(state) + if current_epoch == GENESIS_EPOCH: + return GENESIS_EPOCH + return current_epoch - 1 ``` ### `get_current_epoch` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index d1246c6fa..5b8a93af9 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -385,7 +385,7 @@ def get_next_epoch_committee_assignments( return potential_assignments ``` -`get_next_epoch_committee_assignments` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignments` should be called at the start of each epoch to get potential assignments for the next epoch (slots during `current_epoch + 1`). A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing From 663d38e9c8382e110022de6fcc4ca191ecadd40e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 11:54:17 -0700 Subject: [PATCH 31/40] simplify get_next_epoch_committee_assignment by adding registry_change arg --- specs/validator/0_beacon-chain-validator.md | 54 ++++++++++----------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 5b8a93af9..93c3fb408 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -341,15 +341,16 @@ There are three possibilities for the shuffling at the next epoch: Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is deterministic based upon `epochs_since_last_registry_update`. -`get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committees in the next epoch for a given `validator_index`. This function returns a list of 2 shard committee tuples. +`get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committee assignments in the next epoch for a given `validator_index` and `registry_change`. ```python -def get_next_epoch_committee_assignments( +def get_next_epoch_committee_assignment( state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]]: + validator_index: ValidatorIndex, + registry_change: bool) -> Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]: """ - Return a list of the two possible committee assignments for ``validator_index`` at the next epoch. - Possible committee ``assignment`` is of the form (List[ValidatorIndex], ShardNumber, SlotNumber, bool). + Return the committee assignment in the next epoch for ``validator_index`` and ``registry_change``. + ``assignment`` returned is a tuple of the following form: * ``assignment[0]`` is the list of validators in the committee * ``assignment[1]`` is the shard to which the committee is assigned * ``assignment[2]`` is the slot at which the committee is assigned @@ -359,33 +360,28 @@ def get_next_epoch_committee_assignments( current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) - potential_assignments = [] - for registry_change in [False, True]: - for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): - crosslink_committees = get_crosslink_committees_at_slot( - state, - slot, - registry_change=registry_change, - ) - selected_committees = [ - committee # Tuple[List[ValidatorIndex], ShardNumber] - for committee in crosslink_committees - if validator_index in committee[0] - ] - if len(selected_committees) > 0: - assignment = selected_committees[0] - assignment += (slot,) - first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] - is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index - assignment += (is_proposer,) + for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): + crosslink_committees = get_crosslink_committees_at_slot( + state, + slot, + registry_change=registry_change, + ) + selected_committees = [ + committee # Tuple[List[ValidatorIndex], ShardNumber] + for committee in crosslink_committees + if validator_index in committee[0] + ] + if len(selected_committees) > 0: + validators = selected_committees[0][0] + shard = selected_committees[0][1] + first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] + is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index - potential_assignments.append(assignment) - break - - return potential_assignments + assignment = (validators, shard, slot, is_proposer) + return assignment ``` -`get_next_epoch_committee_assignments` should be called at the start of each epoch to get potential assignments for the next epoch (slots during `current_epoch + 1`). A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (slots during `current_epoch + 1`). A validator should always plan for assignments from both values of `registry_change` unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing From 057364a7d4552334060256679b89f43dd38dec10 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 19:37:34 -0600 Subject: [PATCH 32/40] Add mandatory deposit index ordering Co-requisite with #589 --- specs/core/0_beacon-chain.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ebc12324c..78388cad2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -526,6 +526,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, 'eth1_data_votes': [Eth1DataVote], + 'deposit_count': 'uint64' } ``` @@ -1717,6 +1718,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: * Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be 8 bytes for `deposit_data.amount` followed by 8 bytes for `deposit_data.timestamp` and then the `DepositInput` bytes. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. +* Verify that `deposit.index == state.deposit_index`. * Verify that `verify_merkle_branch(hash(serialized_deposit_data), deposit.branch, DEPOSIT_CONTRACT_TREE_DEPTH, deposit.index, state.latest_eth1_data.deposit_root)` is `True`. ```python @@ -1745,6 +1747,8 @@ process_deposit( ) ``` +* Set `state.deposit_index += 1`. + ##### Exits Verify that `len(block.body.exits) <= MAX_EXITS`. From 36d5120deb20d24546c8877aea212b6e961df0e3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 19:38:09 -0600 Subject: [PATCH 33/40] Initialize deposit index --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 78388cad2..a7e2b6286 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1479,6 +1479,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=[], + deposit_index=0 ) # Process initial deposits From 019fe8953e6aabd2176f93ab00b96904966882ca Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 8 Feb 2019 22:02:55 -0600 Subject: [PATCH 34/40] Fixed as per @djrtwo's comments --- 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 a7e2b6286..da2cce788 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -526,7 +526,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, 'eth1_data_votes': [Eth1DataVote], - 'deposit_count': 'uint64' + 'deposit_index': 'uint64' } ``` @@ -1479,7 +1479,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=[], - deposit_index=0 + deposit_index=len(initial_validator_deposits) ) # Process initial deposits From 0157aa039cbef094facb32d35aa5c5feb1297325 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 9 Feb 2019 11:10:03 +0300 Subject: [PATCH 35/40] Change var name to avoid confusing the reader into thinking the `valid_proof` variable contains a valid proof as opposed to just being a bool. Co-Authored-By: Nashatyrev --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c08343ef9..604bab0f0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1252,7 +1252,7 @@ def process_deposit(state: BeaconState, Note that this function mutates ``state``. """ # Validate the given `proof_of_possession` - valid_proof = validate_proof_of_possession( + proof_is_valid = validate_proof_of_possession( state, pubkey, proof_of_possession, From e48010b77b9604104500e44e2100909d4575e0be Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 9 Feb 2019 11:10:54 +0300 Subject: [PATCH 36/40] Change var name to avoid confusing the reader into thinking the `valid_proof` variable contains a valid proof as opposed to just being a bool. Co-Authored-By: Nashatyrev --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 604bab0f0..03fa7c801 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1259,7 +1259,7 @@ def process_deposit(state: BeaconState, withdrawal_credentials, ) - if valid_proof: + if proof_is_valid: validator_pubkeys = [v.pubkey for v in state.validator_registry] if pubkey not in validator_pubkeys: From 7886d9618606cfad88c5c8f9df2b99feecb1b79a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 9 Feb 2019 07:21:38 -0800 Subject: [PATCH 37/40] change proof_is_valid to exit condition --- specs/core/0_beacon-chain.md | 50 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 03fa7c801..d53ebc901 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1259,30 +1259,32 @@ def process_deposit(state: BeaconState, withdrawal_credentials, ) - if proof_is_valid: - validator_pubkeys = [v.pubkey for v in state.validator_registry] - - if pubkey not in validator_pubkeys: - # Add new validator - validator = Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawal_epoch=FAR_FUTURE_EPOCH, - penalized_epoch=FAR_FUTURE_EPOCH, - status_flags=0, - ) - - # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. - state.validator_registry.append(validator) - state.validator_balances.append(amount) - else: - # Increase balance by deposit amount - index = validator_pubkeys.index(pubkey) - assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - - state.validator_balances[index] += amount + if not proof_is_valid: + return + + validator_pubkeys = [v.pubkey for v in state.validator_registry] + + if pubkey not in validator_pubkeys: + # Add new validator + validator = Validator( + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawal_epoch=FAR_FUTURE_EPOCH, + penalized_epoch=FAR_FUTURE_EPOCH, + status_flags=0, + ) + + # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. + state.validator_registry.append(validator) + state.validator_balances.append(amount) + else: + # Increase balance by deposit amount + index = validator_pubkeys.index(pubkey) + assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials + + state.validator_balances[index] += amount ``` ### Routines for updating validator status From abed5ffdaef901c49dcc51eeb4b1df6d6d7d8b38 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 10:16:22 -0600 Subject: [PATCH 38/40] Updated phase 1: fork choice rule (#586) Adds the crosslink committee to the fork choice rule. This is useful because it means that even if a proposal committee is byzantine and attempts to prevent a crosslink via a "balance attack" (alternating between chain A and chain B being the canonical chain), the crosslink committee can force the equilibrium to flip to one side or the other. --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cabe2934e..a8d19ddd3 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -121,4 +121,4 @@ Verify that the `shard_block_combined_data_root` is the output of these function ### Shard block fork choice rule -The fork choice rule for any shard is LMD GHOST using the validators currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). +The fork choice rule for any shard is LMD GHOST using the shard chain attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). From 1f97206dcf843613b43f6b99a32dc29cb9cb7bcf Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 10 Feb 2019 10:17:21 -0600 Subject: [PATCH 39/40] Updated phase 1: commitments (#579) See #338 and #529 for discussion. --- specs/core/1_shard-data-chains.md | 40 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a8d19ddd3..352b89b40 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -20,6 +20,7 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md |------------------------|-----------------|-------|---------------| | `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | | `SHARD_BLOCK_SIZE` | 2**14 (= 16384) | bytes | | +| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | | ### Flags, domains, etc. @@ -98,26 +99,39 @@ A node should sign a crosslink only if the following conditions hold. **If a nod First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis). -Second, we verify the `shard_block_combined_data_root`. Let `h` be the slot _immediately after_ the slot of the shard block included by the last crosslink, and `h+n-1` be the slot number of the block directly referenced by the current `shard_block_root`. Let `B[i]` be the block at slot `h+i` in the shard chain. Let `bodies[0] .... bodies[n-1]` be the bodies of these blocks and `roots[0] ... roots[n-1]` the data roots. If there is a missing slot in the shard chain at position `h+i`, then `bodies[i] == b'\x00' * shard_block_maxbytes(state[i])` and `roots[i]` be the Merkle root of the empty data. Define `compute_merkle_root` be a simple Merkle root calculating function that takes as input a list of objects, where the list's length must be an exact power of two. We define the function for computing the combined data root as follows: +Second, we verify the `shard_chain_commitment`. +* Let `start_slot = state.latest_crosslinks[shard].epoch * EPOCH_LENGTH + EPOCH_LENGTH - CROSSLINK_LOOKBACK`. +* Let `end_slot = attestation.data.slot - attestation.data.slot % EPOCH_LENGTH - CROSSLINK_LOOKBACK`. +* Let `length = end_slot - start_slot`, `headers[0] .... headers[length-1]` be the serialized block headers in the canonical shard chain from the verifer's point of view (note that this implies that `headers` and `bodies` have been checked for validity). +* Let `bodies[0] ... bodies[length-1]` be the bodies of the blocks. +* Note: If there is a missing slot, then the header and body are the same as that of the block at the most recent slot that has a block. + +We define two helpers: ```python -ZERO_ROOT = merkle_root(bytes([0] * SHARD_BLOCK_SIZE)) - -def mk_combined_data_root(roots): - data = roots + [ZERO_ROOT for _ in range(len(roots), next_power_of_2(len(roots)))] - return compute_merkle_root(data) +def pad_to_power_of_2(values: List[bytes]) -> List[bytes]: + while not is_power_of_two(len(values)): + values = values + [SHARD_BLOCK_SIZE] + return values ``` -This outputs the root of a tree of the data roots, with the data roots all adjusted to have the same height if needed. The tree can also be viewed as a tree of all of the underlying data concatenated together, appropriately padded. Here is an equivalent definition that uses bodies instead of roots [TODO: check equivalence]: - ```python -def mk_combined_data_root(depths, bodies): - data = b''.join(bodies) - data += bytes([0] * (next_power_of_2(len(data)) - len(data)) - return compute_merkle_root([data[pos:pos+SHARD_CHUNK_SIZE] for pos in range(0, len(data), SHARD_CHUNK_SIZE)]) +def merkle_root_of_bytes(data: bytes) -> bytes: + return merkle_root([data[i:i+32] for i in range(0, len(data), 32)]) ``` -Verify that the `shard_block_combined_data_root` is the output of these functions. +We define the function for computing the commitment as follows: + +```python +def compute_commitment(headers: List[ShardBlock], bodies: List[bytes]) -> Bytes32: + return hash( + merkle_root(pad_to_power_of_2([merkle_root_of_bytes(zpad(serialize(h), SHARD_BLOCK_SIZE)) for h in headers])), + merkle_root(pad_to_power_of_2([merkle_root_of_bytes(h) for h in bodies])) + ) +``` + +The `shard_chain_commitment` is only valid if it equals `compute_commitment(headers, bodies)`. + ### Shard block fork choice rule From 2944a7ddfc67ededa4c3ad6f6a4f28b5a18b35ac Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sun, 10 Feb 2019 17:19:12 +0100 Subject: [PATCH 40/40] Renamed `Helpers` -> `Variables`, seems to make more sense (#560) --- 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 78d24cde3..3c89cf1a9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -125,7 +125,7 @@ - [Deposits](#deposits-1) - [Exits](#exits-1) - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) + - [Helper variables](#helper-variables) - [Eth1 data](#eth1-data-1) - [Justification](#justification) - [Crosslinks](#crosslinks) @@ -1770,7 +1770,7 @@ For each `exit` in `block.body.exits`: The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. -#### Helpers +#### Helper variables * Let `current_epoch = get_current_epoch(state)`. * Let `previous_epoch = get_previous_epoch(state)`.