public BlsPublicKey BlsAggregatePublicKeys(IEnumerable<BlsPublicKey> publicKeys)
 {
     // TKS: added an extension here as an example to discuss - I have been avoiding passing IEnumerable
     // for the performance reasons - to avoid multiple runs
     // and opted for passing either arrays or lists and keep it consistent
     // it sometimes / very rarely has an issue of having to cast list to an array
     // but usually we have a full control over the flow so it ends up being much better
     // what do you think?
     
     // TODO: [SG] yes. Change IEnumerable to IList in most places to avoid double-enumeration.
     
     Span<byte> publicKeysSpan = new Span<byte>(new byte[publicKeys.Count() * BlsPublicKey.Length]);
     int publicKeysSpanIndex = 0;
     foreach (BlsPublicKey publicKey in publicKeys)
     {
         publicKey.AsSpan().CopyTo(publicKeysSpan.Slice(publicKeysSpanIndex));
         publicKeysSpanIndex += BlsPublicKey.Length;
     }
     using BLS signatureAlgorithm = SignatureAlgorithmFactory(new BLSParameters());
     byte[] aggregatePublicKey = new byte[BlsPublicKey.Length];
     bool success = signatureAlgorithm.TryAggregatePublicKeys(publicKeysSpan, aggregatePublicKey, out int bytesWritten);
     if (!success || bytesWritten != BlsPublicKey.Length)
     {
         throw new Exception("Error generating aggregate public key.");
     }
     return new BlsPublicKey(aggregatePublicKey);
 }
Example #2
0
        public IList <BlsPublicKey> GetPublicKeys()
        {
            if (_publicKeyToBls.Count == 0)
            {
                QuickStartParameters _quickStartParameters = _quickStartParameterOptions.CurrentValue;

                ulong endIndex = _quickStartParameters.ValidatorStartIndex + _quickStartParameters.NumberOfValidators;
                for (ulong validatorIndex = _quickStartParameters.ValidatorStartIndex;
                     validatorIndex < endIndex;
                     validatorIndex++)
                {
                    byte[] privateKey = GeneratePrivateKey(validatorIndex);

                    BLSParameters blsParameters = new BLSParameters()
                    {
                        PrivateKey = privateKey
                    };
                    BLS    bls            = BLS.Create(blsParameters);
                    byte[] publicKeyBytes = new byte[BlsPublicKey.Length];
                    bls.TryExportBlsPublicKey(publicKeyBytes, out int publicKeyBytesWritten);
                    BlsPublicKey publicKey = new BlsPublicKey(publicKeyBytes);

                    // Cache the BLS class, for easy signing
                    _publicKeyToBls[publicKey] = bls;
                }
            }

            return(_publicKeyToBls.Keys.ToList());
        }
        public bool BlsVerifyMultiple(IEnumerable <BlsPublicKey> publicKeys, IEnumerable <Hash32> messageHashes, BlsSignature signature, Domain domain)
        {
            int count = publicKeys.Count();

            Span <byte> publicKeysSpan      = new Span <byte>(new byte[count * BlsPublicKey.Length]);
            int         publicKeysSpanIndex = 0;

            foreach (BlsPublicKey publicKey in publicKeys)
            {
                publicKey.AsSpan().CopyTo(publicKeysSpan.Slice(publicKeysSpanIndex));
                publicKeysSpanIndex += BlsPublicKey.Length;
            }

            Span <byte> messageHashesSpan      = new Span <byte>(new byte[count * Hash32.Length]);
            int         messageHashesSpanIndex = 0;

            foreach (Hash32 messageHash in messageHashes)
            {
                messageHash.AsSpan().CopyTo(messageHashesSpan.Slice(messageHashesSpanIndex));
                messageHashesSpanIndex += Hash32.Length;
            }

            using BLS signatureAlgorithm = SignatureAlgorithmFactory(new BLSParameters());
            return(signatureAlgorithm.VerifyAggregate(publicKeysSpan, messageHashesSpan, signature.AsSpan(), domain.AsSpan()));
        }
        public bool BlsAggregateVerify(IList <BlsPublicKey> publicKeys, IList <Root> signingRoots, BlsSignature signature)
        {
            int count = publicKeys.Count();

            Span <byte> publicKeysSpan      = new Span <byte>(new byte[count * BlsPublicKey.Length]);
            int         publicKeysSpanIndex = 0;

            foreach (BlsPublicKey publicKey in publicKeys)
            {
                publicKey.AsSpan().CopyTo(publicKeysSpan.Slice(publicKeysSpanIndex));
                publicKeysSpanIndex += BlsPublicKey.Length;
            }

            Span <byte> signingRootsSpan      = new Span <byte>(new byte[count * Root.Length]);
            int         signingRootsSpanIndex = 0;

            foreach (Root signingRoot in signingRoots)
            {
                signingRoot.AsSpan().CopyTo(signingRootsSpan.Slice(signingRootsSpanIndex));
                signingRootsSpanIndex += Root.Length;
            }

            using BLS signatureAlgorithm = SignatureAlgorithmFactory(new BLSParameters());
            return(signatureAlgorithm.AggregateVerifyHashes(publicKeysSpan, signingRootsSpan, signature.AsSpan()));
        }
        public bool BlsVerify(BlsPublicKey publicKey, Root signingRoot, BlsSignature signature)
        {
            BLSParameters blsParameters = new BLSParameters()
            {
                PublicKey = publicKey.AsSpan().ToArray()
            };

            using BLS signatureAlgorithm = SignatureAlgorithmFactory(blsParameters);
            return(signatureAlgorithm.VerifyHash(signingRoot.AsSpan(), signature.AsSpan()));
        }
        public bool BlsVerify(BlsPublicKey publicKey, Hash32 messageHash, BlsSignature signature, Domain domain)
        {
            BLSParameters blsParameters = new BLSParameters()
            {
                PublicKey = publicKey.AsSpan().ToArray()
            };

            using BLS signatureAlgorithm = SignatureAlgorithmFactory(blsParameters);
            return(signatureAlgorithm.VerifyHash(messageHash.AsSpan(), signature.AsSpan(), domain.AsSpan()));
        }
        public CryptographyService(ChainConstants chainConstants,
                                   IOptionsMonitor <MiscellaneousParameters> miscellaneousParameterOptions,
                                   IOptionsMonitor <TimeParameters> timeParameterOptions,
                                   IOptionsMonitor <StateListLengths> stateListLengthOptions,
                                   IOptionsMonitor <MaxOperationsPerBlock> maxOperationsPerBlockOptions)
        {
            _chainConstants = chainConstants;
            _miscellaneousParameterOptions = miscellaneousParameterOptions;
            _timeParameterOptions          = timeParameterOptions;
            _stateListLengthOptions        = stateListLengthOptions;
            _maxOperationsPerBlockOptions  = maxOperationsPerBlockOptions;

            MiscellaneousParameters miscellaneousParameters = miscellaneousParameterOptions.CurrentValue;
            TimeParameters          timeParameters          = timeParameterOptions.CurrentValue;
            StateListLengths        stateListLengths        = stateListLengthOptions.CurrentValue;
            MaxOperationsPerBlock   maxOperationsPerBlock   = maxOperationsPerBlockOptions.CurrentValue;

            BLSParameters blsParameters = new BLSParameters();

            _bls = BLS.Create(blsParameters);

            Nethermind.Ssz.Ssz.Init(
                chainConstants.DepositContractTreeDepth,
                chainConstants.JustificationBitsLength,
                miscellaneousParameters.MaximumValidatorsPerCommittee,
                timeParameters.SlotsPerEpoch,
                timeParameters.SlotsPerEth1VotingPeriod,
                timeParameters.SlotsPerHistoricalRoot,
                stateListLengths.EpochsPerHistoricalVector,
                stateListLengths.EpochsPerSlashingsVector,
                stateListLengths.HistoricalRootsLimit,
                stateListLengths.ValidatorRegistryLimit,
                maxOperationsPerBlock.MaximumProposerSlashings,
                maxOperationsPerBlock.MaximumAttesterSlashings,
                maxOperationsPerBlock.MaximumAttestations,
                maxOperationsPerBlock.MaximumDeposits,
                maxOperationsPerBlock.MaximumVoluntaryExits);

            Nethermind.Ssz.Ssz.Init(
                chainConstants.DepositContractTreeDepth,
                chainConstants.JustificationBitsLength,
                miscellaneousParameters.MaximumValidatorsPerCommittee,
                timeParameters.SlotsPerEpoch,
                timeParameters.SlotsPerEth1VotingPeriod,
                timeParameters.SlotsPerHistoricalRoot,
                stateListLengths.EpochsPerHistoricalVector,
                stateListLengths.EpochsPerSlashingsVector,
                stateListLengths.HistoricalRootsLimit,
                stateListLengths.ValidatorRegistryLimit,
                maxOperationsPerBlock.MaximumProposerSlashings,
                maxOperationsPerBlock.MaximumAttesterSlashings,
                maxOperationsPerBlock.MaximumAttestations,
                maxOperationsPerBlock.MaximumDeposits,
                maxOperationsPerBlock.MaximumVoluntaryExits);
        }
Example #8
0
        private DepositData BuildAndSignDepositData(ulong validatorIndex, Gwei amount, SignatureDomains signatureDomains)
        {
            InitialValues initialValues = _initialValueOptions.CurrentValue;

            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
            Bytes32 withdrawalCredentials = _crypto.Hash(publicKey.AsSpan());

            withdrawalCredentials.Unwrap()[0] = initialValues.BlsWithdrawalPrefix;

            // Build deposit data
            DepositData depositData = new DepositData(publicKey, withdrawalCredentials, amount, BlsSignature.Zero);

            // Sign deposit data
            Domain         domain         = _beaconChainUtility.ComputeDomain(signatureDomains.Deposit);
            DepositMessage depositMessage = new DepositMessage(
                depositData.PublicKey,
                depositData.WithdrawalCredentials,
                depositData.Amount);

            Root depositMessageRoot     = _crypto.HashTreeRoot(depositMessage);
            Root depositDataSigningRoot = _beaconChainUtility.ComputeSigningRoot(depositMessageRoot, domain);

            byte[] signatureBytes = new byte[96];
            bls.TrySignData(depositDataSigningRoot.AsSpan(), signatureBytes, out int bytesWritten);

            BlsSignature depositDataSignature = new BlsSignature(signatureBytes);

            depositData.SetSignature(depositDataSignature);

            if (_logger.IsEnabled(LogLevel.Debug))
            {
                LogDebug.QuickStartAddValidator(_logger, validatorIndex, publicKey.ToString().Substring(0, 12),
                                                null);
            }

            return(depositData);
        }
Example #9
0
        public BlsSignature SignRoot(BlsPublicKey blsPublicKey, Root root)
        {
            if (_publicKeyToBls.Count == 0)
            {
                var keyCount = GetPublicKeys().Count();
            }

            BLS bls = _publicKeyToBls[blsPublicKey];

            byte[] destination = new byte[BlsSignature.Length];
            bool   success     = bls.TrySignData(root.AsSpan(), destination, out int bytesWritten);

            if (!success || bytesWritten != BlsSignature.Length)
            {
                throw new Exception($"Failure signing hash {root} for public key {blsPublicKey}.");
            }
            BlsSignature blsSignature = new BlsSignature(destination);

            return(blsSignature);
        }
Example #10
0
        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);
        }
Example #11
0
        private static void EnsureKeys(TimeParameters timeParameters)
        {
            var keysRequired = (int)(ulong)timeParameters.SlotsPerEpoch * 16;

            if (_testKeys.Count == keysRequired)
            {
                return;
            }

            _testKeys.Clear();
            _privateKeys.Clear();
            _publicKeys.Clear();
            // Private key is ~255 bits (32 bytes) long
            for (var keyNumber = 0; keyNumber < keysRequired; keyNumber++)
            {
                var privateKeySpan = new Span <byte>(new byte[32]);
                // Key is big endian number, so write Int32 to last 4 bytes.
                BitConverter.TryWriteBytes(privateKeySpan.Slice(28), keyNumber + 1);
                if (BitConverter.IsLittleEndian)
                {
                    // And reverse if necessary
                    privateKeySpan.Slice(28).Reverse();
                }
                var privateKey = privateKeySpan.ToArray();

                var blsParameters = new BLSParameters()
                {
                    PrivateKey = privateKey
                };
                using var bls = BLS.Create(blsParameters);
                var publicKeyBytes = new byte[BlsPublicKey.Length];
                bls.TryExportBLSPublicKey(publicKeyBytes, out var bytesWritten);
                var publicKey = new BlsPublicKey(publicKeyBytes);

                _privateKeys.Add(privateKey);
                _publicKeys.Add(publicKey);
                _testKeys[publicKey] = privateKey;
            }
        }
        public BlsPublicKey BlsAggregatePublicKeys(IList <BlsPublicKey> publicKeys)
        {
            Span <byte> publicKeysSpan      = new Span <byte>(new byte[publicKeys.Count() * BlsPublicKey.Length]);
            int         publicKeysSpanIndex = 0;

            foreach (BlsPublicKey publicKey in publicKeys)
            {
                publicKey.AsSpan().CopyTo(publicKeysSpan.Slice(publicKeysSpanIndex));
                publicKeysSpanIndex += BlsPublicKey.Length;
            }

            using BLS signatureAlgorithm = SignatureAlgorithmFactory(new BLSParameters());
            byte[] aggregatePublicKey = new byte[BlsPublicKey.Length];
            bool   success            =
                signatureAlgorithm.TryAggregatePublicKeys(publicKeysSpan, aggregatePublicKey, out int bytesWritten);

            if (!success || bytesWritten != BlsPublicKey.Length)
            {
                throw new Exception("Error generating aggregate public key.");
            }

            return(new BlsPublicKey(aggregatePublicKey));
        }
Example #13
0
        private BlsSignature GetEpochSignature(IServiceProvider testServiceProvider, byte[] privateKey, ForkVersion forkVersion, Slot slot)
        {
            SignatureDomains   signatureDomains   = testServiceProvider.GetService <IOptions <SignatureDomains> >().Value;
            BeaconChainUtility beaconChainUtility = testServiceProvider.GetService <BeaconChainUtility>();
            var domain = beaconChainUtility.ComputeDomain(signatureDomains.Randao, forkVersion);

            var epoch = beaconChainUtility.ComputeEpochAtSlot(slot);

            var epochRoot = epoch.HashTreeRoot();

            BLSParameters parameters = new BLSParameters()
            {
                PrivateKey = privateKey
            };
            BLS bls         = BLS.Create(parameters);
            var destination = new Span <byte>(new byte[96]);

            bls.TrySignHash(epochRoot.AsSpan(), destination, out var bytesWritten, domain.AsSpan());

            var signature = new BlsSignature(destination.ToArray());

            return(signature);
        }
Example #14
0
        public void QuickStartGenesis()
        {
            var quickStartParameters = _quickStartParameterOptions.CurrentValue;

            _logger.LogWarning(0, "Mocked quick start with genesis time {GenesisTime} and {ValidatorCount} validators.",
                               quickStartParameters.GenesisTime, quickStartParameters.ValidatorCount);

            var miscellaneousParameters = _miscellaneousParameterOptions.CurrentValue;
            var gweiValues            = _gweiValueOptions.CurrentValue;
            var initialValues         = _initialValueOptions.CurrentValue;
            var timeParameters        = _timeParameterOptions.CurrentValue;
            var stateListLengths      = _stateListLengthOptions.CurrentValue;
            var maxOperationsPerBlock = _maxOperationsPerBlockOptions.CurrentValue;
            var signatureDomains      = _signatureDomainOptions.CurrentValue;

            // Fixed amount
            var amount = gweiValues.MaximumEffectiveBalance;

            // Build deposits
            var depositDataList = new List <DepositData>();
            var deposits        = new List <Deposit>();

            for (var validatorIndex = 0uL; validatorIndex < quickStartParameters.ValidatorCount; validatorIndex++)
            {
                var privateKey = GeneratePrivateKey(validatorIndex);

                // Public Key
                var blsParameters = new BLSParameters()
                {
                    PrivateKey = privateKey
                };
                using var bls = BLS.Create(blsParameters);
                var publicKeyBytes = new Span <byte>(new byte[BlsPublicKey.Length]);
                bls.TryExportBLSPublicKey(publicKeyBytes, out var publicKeybytesWritten);
                var publicKey = new BlsPublicKey(publicKeyBytes);

                // Withdrawal Credentials
                var withdrawalCredentialBytes = _cryptographyService.Hash(publicKey.AsSpan()).AsSpan().ToArray();
                withdrawalCredentialBytes[0] = initialValues.BlsWithdrawalPrefix;
                var withdrawalCredentials = new Hash32(withdrawalCredentialBytes);

                // Build deposit data
                var depositData = new DepositData(publicKey, withdrawalCredentials, amount);

                // Sign deposit data
                var depositDataSigningRoot = depositData.SigningRoot();
                var domain      = _beaconChainUtility.ComputeDomain(signatureDomains.Deposit);
                var destination = new Span <byte>(new byte[96]);
                bls.TrySignHash(depositDataSigningRoot.AsSpan(), destination, out var bytesWritten, domain.AsSpan());
                var 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

                var index = depositDataList.Count;
                depositDataList.Add(depositData);
                var root        = depositDataList.HashTreeRoot((ulong)1 << _chainConstants.DepositContractTreeDepth);
                var allLeaves   = depositDataList.Select(x => x.HashTreeRoot());
                var tree        = CalculateMerkleTreeFromLeaves(allLeaves);
                var merkleProof = GetMerkleProof(tree, index, 32);
                var proof       = new List <Hash32>(merkleProof);
                var indexBytes  = new Span <byte>(new byte[32]);
                BitConverter.TryWriteBytes(indexBytes, (ulong)index + 1);
                if (!BitConverter.IsLittleEndian)
                {
                    indexBytes.Slice(0, 8).Reverse();
                }
                var indexHash = new Hash32(indexBytes);
                proof.Add(indexHash);
                var leaf = depositData.HashTreeRoot();
                _beaconChainUtility.IsValidMerkleBranch(leaf, proof, _chainConstants.DepositContractTreeDepth + 1, (ulong)index, root);
                var deposit = new Deposit(proof, depositData);

                _logger.LogDebug("Quick start adding deposit for mocked validator {ValidatorIndex} with public key {PublicKey}.",
                                 validatorIndex, publicKey);

                deposits.Add(deposit);
            }

            var genesisState = _beaconChain.InitializeBeaconStateFromEth1(quickStartParameters.Eth1BlockHash, quickStartParameters.Eth1Timestamp, deposits);

            // We use the state directly, and don't test IsValid
            genesisState.SetGenesisTime(quickStartParameters.GenesisTime);
            var store = _forkChoice.GetGenesisStore(genesisState);

            _logger.LogDebug("Quick start genesis store created with genesis time {GenesisTime}.", store.GenesisTime);
        }
Example #15
0
        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);
            }
        }
        public Task <Eth1GenesisData> GetEth1GenesisDataAsync(CancellationToken cancellationToken)
        {
            QuickStartParameters quickStartParameters = _quickStartParameterOptions.CurrentValue;

            if (_logger.IsWarn())
            {
                Log.MockedQuickStart(_logger, quickStartParameters.GenesisTime, quickStartParameters.ValidatorCount,
                                     null);
            }

            GweiValues       gweiValues       = _gweiValueOptions.CurrentValue;
            InitialValues    initialValues    = _initialValueOptions.CurrentValue;
            TimeParameters   timeParameters   = _timeParameterOptions.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;
                Bytes32 withdrawalCredentials = new Bytes32(withdrawalCredentialBytes);

                // Build deposit data
                DepositData depositData = new DepositData(publicKey, withdrawalCredentials, amount, BlsSignature.Zero);

                // Sign deposit data
                Domain         domain         = _beaconChainUtility.ComputeDomain(signatureDomains.Deposit);
                DepositMessage depositMessage = new DepositMessage(depositData.PublicKey,
                                                                   depositData.WithdrawalCredentials, depositData.Amount);
                Root   depositMessageRoot     = _cryptographyService.HashTreeRoot(depositMessage);
                Root   depositDataSigningRoot = _beaconChainUtility.ComputeSigningRoot(depositMessageRoot, domain);
                byte[] destination            = new byte[96];
                bls.TrySignData(depositDataSigningRoot.AsSpan(), destination, out int bytesWritten);
                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;
                Root root = _cryptographyService.HashTreeRoot(depositDataList);
                IEnumerable <Bytes32> allLeaves = depositDataList.Select(x =>
                                                                         new Bytes32(_cryptographyService.HashTreeRoot((DepositData)x).AsSpan()));
                IList <IList <Bytes32> > tree = CalculateMerkleTreeFromLeaves(allLeaves);


                IList <Bytes32> merkleProof = GetMerkleProof(tree, index, 32);
                List <Bytes32>  proof       = new List <Bytes32>(merkleProof);

                byte[] indexBytes = new byte[32];
                BinaryPrimitives.WriteInt32LittleEndian(indexBytes, index + 1);
                Bytes32 indexHash = new Bytes32(indexBytes);
                proof.Add(indexHash);
                Bytes32 leaf = new Bytes32(_cryptographyService.HashTreeRoot(depositData).AsSpan());
                _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);
            }

            ulong eth1Timestamp = quickStartParameters.Eth1Timestamp;

            if (eth1Timestamp == 0)
            {
                eth1Timestamp = quickStartParameters.GenesisTime - (ulong)(1.5 * timeParameters.MinimumGenesisDelay);
            }
            else
            {
                ulong minimumEth1TimestampInclusive =
                    quickStartParameters.GenesisTime - 2 * timeParameters.MinimumGenesisDelay;
                ulong maximumEth1TimestampInclusive =
                    quickStartParameters.GenesisTime - timeParameters.MinimumGenesisDelay - 1;
                if (eth1Timestamp < minimumEth1TimestampInclusive)
                {
                    if (_logger.IsEnabled(LogLevel.Warning))
                    {
                        Log.QuickStartEth1TimestampTooLow(_logger, eth1Timestamp, quickStartParameters.GenesisTime,
                                                          minimumEth1TimestampInclusive, null);
                    }
                    eth1Timestamp = minimumEth1TimestampInclusive;
                }
                else if (eth1Timestamp > maximumEth1TimestampInclusive)
                {
                    if (_logger.IsEnabled(LogLevel.Warning))
                    {
                        Log.QuickStartEth1TimestampTooHigh(_logger, eth1Timestamp, quickStartParameters.GenesisTime,
                                                           maximumEth1TimestampInclusive, null);
                    }
                    eth1Timestamp = maximumEth1TimestampInclusive;
                }
            }

            var eth1GenesisData = new Eth1GenesisData(quickStartParameters.Eth1BlockHash, eth1Timestamp,
                                                      deposits);

            if (_logger.IsEnabled(LogLevel.Debug))
            {
                LogDebug.QuickStartGenesisDataCreated(_logger, eth1GenesisData.BlockHash, eth1GenesisData.Timestamp,
                                                      eth1GenesisData.Deposits.Count, null);
            }

            return(Task.FromResult(eth1GenesisData));
        }