/// <summary> /// Return from ``indices`` a random index sampled by effective balance. /// </summary> public ValidatorIndex ComputeProposerIndex(BeaconState state, IList <ValidatorIndex> indices, Bytes32 seed) { if (!indices.Any()) { throw new ArgumentException("Indices can not be empty", nameof(indices)); } ulong indexCount = (ulong)indices.Count; ValidatorIndex index = 0UL; Span <byte> randomInputBytes = stackalloc byte[40]; seed.AsSpan().CopyTo(randomInputBytes); while (true) { ValidatorIndex initialValidatorIndex = (ValidatorIndex)(index % indexCount); ValidatorIndex shuffledIndex = ComputeShuffledIndex(initialValidatorIndex, indexCount, seed); ValidatorIndex candidateIndex = indices[(int)shuffledIndex]; BinaryPrimitives.WriteUInt64LittleEndian(randomInputBytes.Slice(32), index / 32); Bytes32 randomHash = _cryptographyService.Hash(randomInputBytes); byte random = randomHash.AsSpan()[(int)(index % 32)]; Gwei effectiveBalance = state.Validators[(int)candidateIndex].EffectiveBalance; if ((effectiveBalance * byte.MaxValue) >= (_gweiValueOptions.CurrentValue.MaximumEffectiveBalance * random)) { return(candidateIndex); } index++; } }
// FIXME: This is duplicate of beacon node, need to clean up public byte[] GeneratePrivateKey(ulong index) { Span <byte> input = new Span <byte>(new byte[32]); BigInteger bigIndex = new BigInteger(index); bool indexWriteSuccess = bigIndex.TryWriteBytes(input, out int indexBytesWritten, isUnsigned: true, isBigEndian: false); if (!indexWriteSuccess || indexBytesWritten == 0) { throw new Exception("Error getting input for quick start private key generation."); } Bytes32 bytes32 = Sha256.Compute(input); ReadOnlySpan <byte> hash = bytes32.AsSpan(); // Mocked start interop specifies to convert the hash as little endian (which is the default for BigInteger) BigInteger value = new BigInteger(hash.ToArray(), isUnsigned: true); BigInteger privateKey = value % s_curveOrder; // Note that the private key is an *unsigned*, *big endian* number // However, we want to pad the big endian on the left to get 32 bytes. // So, write as little endian (will pad to right), then reverse. // NOTE: Alternative, write to Span 64, and then slice based on bytesWritten to get the padding. Span <byte> privateKeySpan = new Span <byte>(new byte[32]); bool keyWriteSuccess = privateKey.TryWriteBytes(privateKeySpan, out int keyBytesWritten, isUnsigned: true, isBigEndian: false); if (!keyWriteSuccess) { throw new Exception("Error generating quick start private key."); } privateKeySpan.Reverse(); return(privateKeySpan.ToArray()); }
/// <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); }
public Bytes32 Hash(Bytes32 a, Bytes32 b) { Span <byte> input = new Span <byte>(new byte[Bytes32.Length * 2]); a.AsSpan().CopyTo(input); b.AsSpan().CopyTo(input.Slice(Bytes32.Length)); return(Hash(input)); }
public Eth1Data GetEth1DataStub(BeaconState state, Epoch currentEpoch) { TimeParameters timeParameters = _timeParameterOptions.CurrentValue; uint epochsPerPeriod = timeParameters.SlotsPerEth1VotingPeriod / timeParameters.SlotsPerEpoch; ulong votingPeriod = (ulong)currentEpoch / epochsPerPeriod; Span <byte> votingPeriodBytes = stackalloc byte[32]; BinaryPrimitives.WriteUInt64LittleEndian(votingPeriodBytes, votingPeriod); Bytes32 hashOfVotingPeriod = _cryptographyService.Hash(votingPeriodBytes); Root depositRoot = new Root(hashOfVotingPeriod.AsSpan()); ulong depositCount = state.Eth1DepositIndex; Bytes32 blockHash = _cryptographyService.Hash(hashOfVotingPeriod.AsSpan()); Eth1Data eth1Data = new Eth1Data(depositRoot, depositCount, blockHash); return(eth1Data); }
public static void Ize(out UInt256 root, Bytes32 value) { ReadOnlySpan <byte> readOnlyBytes = value.AsSpan(); unsafe { fixed(byte *buffer = &readOnlyBytes.GetPinnableReference()) { Span <byte> apiNeedsWriteableEvenThoughOnlyReading = new Span <byte>(buffer, readOnlyBytes.Length); UInt256.CreateFromLittleEndian(out root, apiNeedsWriteableEvenThoughOnlyReading); } } }
/// <summary> /// Return the seed at ``epoch``. /// </summary> public Bytes32 GetSeed(BeaconState state, Epoch epoch, DomainType domainType) { Epoch mixEpoch = (Epoch)(epoch + _stateListLengthOptions.CurrentValue.EpochsPerHistoricalVector - _timeParameterOptions.CurrentValue.MinimumSeedLookahead - 1UL); // # Avoid underflow Bytes32 mix = GetRandaoMix(state, mixEpoch); Span <byte> seedHashInput = stackalloc byte[DomainType.Length + sizeof(ulong) + Bytes32.Length]; domainType.AsSpan().CopyTo(seedHashInput); BinaryPrimitives.WriteUInt64LittleEndian(seedHashInput.Slice(DomainType.Length), epoch); mix.AsSpan().CopyTo(seedHashInput.Slice(DomainType.Length + sizeof(ulong))); Bytes32 seed = _cryptographyService.Hash(seedHashInput); return(seed); }
/// <summary> /// Return the beacon proposer index at the current slot. /// </summary> public ValidatorIndex GetBeaconProposerIndex(BeaconState state) { Epoch epoch = GetCurrentEpoch(state); Span <byte> seedBytes = stackalloc byte[40]; Bytes32 initialSeed = GetSeed(state, epoch, _signatureDomainOptions.CurrentValue.BeaconProposer); initialSeed.AsSpan().CopyTo(seedBytes); BinaryPrimitives.WriteUInt64LittleEndian(seedBytes.Slice(32), state.Slot); Bytes32 seed = _cryptographyService.Hash(seedBytes); IList <ValidatorIndex> indices = GetActiveValidatorIndices(state, epoch); ValidatorIndex proposerIndex = _beaconChainUtility.ComputeProposerIndex(state, indices, seed); return(proposerIndex); }
/// <summary> /// Check if 'leaf' at 'index' verifies against the Merkle 'root' and 'branch' /// </summary> public bool IsValidMerkleBranch(Bytes32 leaf, IReadOnlyList <Bytes32> branch, int depth, ulong index, Root root) { Bytes32 value = leaf; for (int testDepth = 0; testDepth < depth; testDepth++) { Bytes32 branchValue = branch[testDepth]; ulong indexAtDepth = index / ((ulong)1 << testDepth); if (indexAtDepth % 2 == 0) { // Branch on right value = _cryptographyService.Hash(value, branchValue); } else { // Branch on left value = _cryptographyService.Hash(branchValue, value); } } return(value.AsSpan().SequenceEqual(root.AsSpan())); }
public static SszElement ToSszBasicVector(this Bytes32 item) { return(new SszBasicVector(item.AsSpan())); }
public static void Encode(Span <byte> span, Bytes32 value) { Encode(span, value.AsSpan()); }