/// <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); }
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."); } Hash32 hash32 = _cryptographyService.Hash(input); ReadOnlySpan <byte> hash = hash32.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()); }
public Hash32 Hash(Hash32 a, Hash32 b) { Span<byte> input = new Span<byte>(new byte[Hash32.Length * 2]); a.AsSpan().CopyTo(input); b.AsSpan().CopyTo(input.Slice(Hash32.Length)); return Hash(input); }
/// <summary> /// Return from ``indices`` a random index sampled by effective balance. /// </summary> public ValidatorIndex ComputeProposerIndex(BeaconState state, IList<ValidatorIndex> indices, Hash32 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); Hash32 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++; } }
public bool BlsVerify(BlsPublicKey publicKey, Hash32 messageHash, BlsSignature signature, Domain domain) { var blsParameters = new BLSParameters() { PublicKey = publicKey.AsSpan().ToArray() }; using var signatureAlgorithm = SignatureAlgorithmFactory(blsParameters); return(signatureAlgorithm.VerifyHash(messageHash.AsSpan(), signature.AsSpan(), domain.AsSpan())); }
public static BlsSignature BlsSign(Hash32 messageHash, byte[] privateKey, Domain domain) { var parameters = new BLSParameters() { PrivateKey = privateKey }; using var signingAlgorithm = SignatureAlgorithmFactory(parameters); var destination = new Span <byte>(new byte[96]); var success = signingAlgorithm.TrySignHash(messageHash.AsSpan(), destination, out var bytesWritten, domain.AsSpan().ToArray()); return(new BlsSignature(destination)); }
/// <summary> /// Return the seed at ``epoch``. /// </summary> public Hash32 GetSeed(BeaconState state, Epoch epoch, DomainType domainType) { Epoch mixEpoch = (Epoch)(epoch + _stateListLengthOptions.CurrentValue.EpochsPerHistoricalVector - _timeParameterOptions.CurrentValue.MinimumSeedLookahead - 1UL); // # Avoid underflow Hash32 mix = GetRandaoMix(state, mixEpoch); Span <byte> seedHashInput = stackalloc byte[DomainType.Length + sizeof(ulong) + Hash32.Length]; domainType.AsSpan().CopyTo(seedHashInput); BinaryPrimitives.WriteUInt64LittleEndian(seedHashInput.Slice(DomainType.Length), epoch); mix.AsSpan().CopyTo(seedHashInput.Slice(DomainType.Length + sizeof(ulong))); Hash32 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]; Hash32 initialSeed = GetSeed(state, epoch, _signatureDomainOptions.CurrentValue.BeaconProposer); initialSeed.AsSpan().CopyTo(seedBytes); BinaryPrimitives.WriteUInt64LittleEndian(seedBytes.Slice(32), (ulong)state.Slot); Hash32 seed = _cryptographyService.Hash(seedBytes); IList <ValidatorIndex> indices = GetActiveValidatorIndices(state, epoch); ValidatorIndex proposerIndex = _beaconChainUtility.ComputeProposerIndex(state, indices, seed); return(proposerIndex); }
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); Hash32 depositRoot = _cryptographyService.Hash(votingPeriodBytes); ulong depositCount = state.Eth1DepositIndex; Hash32 blockHash = _cryptographyService.Hash(depositRoot.AsSpan()); Eth1Data eth1Data = new Eth1Data(depositRoot, depositCount, blockHash); return(eth1Data); }
public BlsSignature SignHashWithDomain(BlsPublicKey blsPublicKey, Hash32 hash, Domain domain) { if (_publicKeyToBls.Count == 0) { var keyCount = GetPublicKeys().Count(); } BLS bls = _publicKeyToBls[blsPublicKey]; Span <byte> destination = stackalloc byte[BlsSignature.Length]; bool success = bls.TrySignHash(hash.AsSpan(), destination, out int bytesWritten, domain.AsSpan()); if (!success || bytesWritten != BlsSignature.Length) { throw new Exception($"Failure signing hash {hash}, domain {domain} for public key {blsPublicKey}."); } BlsSignature blsSignature = new BlsSignature(destination.ToArray()); return(blsSignature); }
/// <summary> /// Return from ``indices`` a random index sampled by effective balance. /// </summary> public ValidatorIndex ComputeProposerIndex(BeaconState state, IList <ValidatorIndex> indices, Hash32 seed) { if (!indices.Any()) { throw new ArgumentException("Indices can not be empty", nameof(indices)); } const ulong maxRandomByte = (1 << 8) - 1; var indexCount = (ulong)indices.Count(); var index = (ulong)0; while (true) { var initialValidatorIndex = new ValidatorIndex(index % indexCount); var shuffledIndex = ComputeShuffledIndex(initialValidatorIndex, indexCount, seed); var candidateIndex = indices[(int)(ulong)shuffledIndex]; var randomInputBytes = new Span <byte>(new byte[40]); seed.AsSpan().CopyTo(randomInputBytes); BitConverter.TryWriteBytes(randomInputBytes.Slice(32), index / 32); if (!BitConverter.IsLittleEndian) { randomInputBytes.Slice(32).Reverse(); } var randomHash = _cryptographyService.Hash(randomInputBytes); var random = randomHash.AsSpan()[(int)(index % 32)]; var effectiveBalance = state.Validators[(int)(ulong)candidateIndex].EffectiveBalance; if ((effectiveBalance * maxRandomByte) >= (_gweiValueOptions.CurrentValue.MaximumEffectiveBalance * random)) { return(candidateIndex); } index++; } }
public static SszBasicVector ToSszBasicVector(this Hash32 item) { return(new SszBasicVector(item.AsSpan())); }
public void QuickStartGenesis() { QuickStartParameters quickStartParameters = _quickStartParameterOptions.CurrentValue; if (_logger.IsWarn()) { Log.MockedQuickStart(_logger, quickStartParameters.GenesisTime, quickStartParameters.ValidatorCount, null); } GweiValues gweiValues = _gweiValueOptions.CurrentValue; InitialValues initialValues = _initialValueOptions.CurrentValue; SignatureDomains signatureDomains = _signatureDomainOptions.CurrentValue; // Fixed amount Gwei amount = gweiValues.MaximumEffectiveBalance; // Build deposits List <DepositData> depositDataList = new List <DepositData>(); List <Deposit> deposits = new List <Deposit>(); for (ulong validatorIndex = 0uL; validatorIndex < quickStartParameters.ValidatorCount; validatorIndex++) { byte[] privateKey = GeneratePrivateKey(validatorIndex); // Public Key BLSParameters blsParameters = new BLSParameters() { PrivateKey = privateKey }; using BLS bls = BLS.Create(blsParameters); byte[] publicKeyBytes = new byte[BlsPublicKey.Length]; bls.TryExportBLSPublicKey(publicKeyBytes, out int publicKeyBytesWritten); BlsPublicKey publicKey = new BlsPublicKey(publicKeyBytes); // Withdrawal Credentials byte[] withdrawalCredentialBytes = _cryptographyService.Hash(publicKey.AsSpan()).AsSpan().ToArray(); withdrawalCredentialBytes[0] = initialValues.BlsWithdrawalPrefix; Hash32 withdrawalCredentials = new Hash32(withdrawalCredentialBytes); // Build deposit data DepositData depositData = new DepositData(publicKey, withdrawalCredentials, amount); // Sign deposit data Hash32 depositDataSigningRoot = _cryptographyService.SigningRoot(depositData); Domain domain = _beaconChainUtility.ComputeDomain(signatureDomains.Deposit); byte[] destination = new byte[96]; bls.TrySignHash(depositDataSigningRoot.AsSpan(), destination, out int bytesWritten, domain.AsSpan()); BlsSignature depositDataSignature = new BlsSignature(destination); depositData.SetSignature(depositDataSignature); // Deposit // TODO: This seems a very inefficient way (copied from tests) as it recalculates the merkle tree each time // (you only need to add one node) // TODO: Add some tests around quick start, then improve int index = depositDataList.Count; depositDataList.Add(depositData); //int depositDataLength = (ulong) 1 << _chainConstants.DepositContractTreeDepth; Hash32 root = _cryptographyService.HashTreeRoot(depositDataList); IEnumerable <Hash32> allLeaves = depositDataList.Select(x => _cryptographyService.HashTreeRoot(x)); IList <IList <Hash32> > tree = CalculateMerkleTreeFromLeaves(allLeaves); IList <Hash32> merkleProof = GetMerkleProof(tree, index, 32); List <Hash32> proof = new List <Hash32>(merkleProof); Span <byte> indexBytes = new Span <byte>(new byte[32]); BitConverter.TryWriteBytes(indexBytes, (ulong)index + 1); if (!BitConverter.IsLittleEndian) { indexBytes.Slice(0, 8).Reverse(); } Hash32 indexHash = new Hash32(indexBytes); proof.Add(indexHash); Hash32 leaf = _cryptographyService.HashTreeRoot(depositData); _beaconChainUtility.IsValidMerkleBranch(leaf, proof, _chainConstants.DepositContractTreeDepth + 1, (ulong)index, root); Deposit deposit = new Deposit(proof, depositData); if (_logger.IsEnabled(LogLevel.Debug)) { LogDebug.QuickStartAddValidator(_logger, validatorIndex, publicKey.ToString().Substring(0, 12), null); } deposits.Add(deposit); } BeaconState genesisState = _beaconChain.InitializeBeaconStateFromEth1(quickStartParameters.Eth1BlockHash, quickStartParameters.Eth1Timestamp, deposits); // We use the state directly, and don't test IsValid genesisState.SetGenesisTime(quickStartParameters.GenesisTime); IStore store = _forkChoice.GetGenesisStore(genesisState); if (_logger.IsEnabled(LogLevel.Debug)) { LogDebug.QuickStartStoreCreated(_logger, store.GenesisTime, null); } }