/// <summary> /// Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). /// </summary> public ValidatorIndex ComputeShuffledIndex(ValidatorIndex index, ulong indexCount, Hash32 seed) { if (index >= indexCount) { throw new ArgumentOutOfRangeException(nameof(index), index, $"Index should be less than indexCount {indexCount}"); } // Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) // See the 'generalized domain' algorithm on page 3 Span <byte> pivotHashInput = new Span <byte>(new byte[33]); seed.AsSpan().CopyTo(pivotHashInput); Span <byte> sourceHashInput = new Span <byte>(new byte[37]); seed.AsSpan().CopyTo(sourceHashInput); for (int currentRound = 0; currentRound < _miscellaneousParameterOptions.CurrentValue.ShuffleRoundCount; currentRound++) { byte roundByte = (byte)(currentRound & 0xFF); pivotHashInput[32] = roundByte; Hash32 pivotHash = _cryptographyService.Hash(pivotHashInput); byte[] pivotBytes = pivotHash.AsSpan().Slice(0, 8).ToArray(); if (!BitConverter.IsLittleEndian) { pivotBytes = pivotBytes.Reverse().ToArray(); } ValidatorIndex pivot = BitConverter.ToUInt64(pivotBytes.ToArray()) % indexCount; ValidatorIndex flip = (pivot + indexCount - index) % indexCount; ValidatorIndex position = ValidatorIndex.Max(index, flip); sourceHashInput[32] = roundByte; byte[] positionBytes = BitConverter.GetBytes((uint)position / 256); if (!BitConverter.IsLittleEndian) { positionBytes = positionBytes.Reverse().ToArray(); } positionBytes.CopyTo(sourceHashInput.Slice(33)); Hash32 source = _cryptographyService.Hash(sourceHashInput.ToArray()); byte flipByte = source.AsSpan((int)((uint)position % 256 / 8), 1)[0]; int flipBit = (flipByte >> (int)(position % 8)) % 2; if (flipBit == 1) { index = flip; } } return(index); }
/// <summary> /// Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). /// </summary> public ValidatorIndex ComputeShuffledIndex(ValidatorIndex index, ulong indexCount, Bytes32 seed) { if (index >= indexCount) { throw new ArgumentOutOfRangeException(nameof(index), index, $"Index should be less than indexCount {indexCount}"); } // Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) // See the 'generalized domain' algorithm on page 3 Span <byte> pivotHashInput = stackalloc byte[33]; seed.AsSpan().CopyTo(pivotHashInput); Span <byte> sourceHashInput = stackalloc byte[37]; seed.AsSpan().CopyTo(sourceHashInput); for (int currentRound = 0; currentRound < _miscellaneousParameterOptions.CurrentValue.ShuffleRoundCount; currentRound++) { byte roundByte = (byte)(currentRound & 0xFF); pivotHashInput[32] = roundByte; Bytes32 pivotHash = _cryptographyService.Hash(pivotHashInput); ReadOnlySpan <byte> pivotBytes = pivotHash.AsSpan().Slice(0, 8); ValidatorIndex pivot = BinaryPrimitives.ReadUInt64LittleEndian(pivotBytes) % indexCount; ValidatorIndex flip = (pivot + indexCount - index) % indexCount; ValidatorIndex position = ValidatorIndex.Max(index, flip); sourceHashInput[32] = roundByte; BinaryPrimitives.WriteUInt32LittleEndian(sourceHashInput.Slice(33), (uint)position / 256); Bytes32 source = _cryptographyService.Hash(sourceHashInput.ToArray()); byte flipByte = source.AsSpan()[(int)((position % 256) / 8)]; int flipBit = (flipByte >> (int)(position % 8)) % 2; if (flipBit == 1) { index = flip; } } return(index); }