From c58410e6ce9904c6619cd925b64fbd04c00b9a89 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 06:48:46 -0600 Subject: [PATCH] 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`