Пример #1
0
        public bool Validate(BlockHeader header)
        {
            uint           epoch   = GetEpoch(header.Number);
            IEthashDataSet dataSet = _hintBasedCache.Get(epoch);

            if (dataSet == null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Ethash cache miss for block {header.ToString(BlockHeader.Format.Short)}");
                }
                _hintBasedCache.Hint(_hintBasedCacheUser, header.Number, header.Number);
                dataSet = _hintBasedCache.Get(epoch);
                if (dataSet == null)
                {
                    if (_logger.IsError)
                    {
                        _logger.Error($"Hint based cache could not get data set for {header.ToString(BlockHeader.Format.Short)}");
                    }
                    return(false);
                }
            }

            ulong  fullSize     = GetDataSize(epoch);
            Keccak headerHashed = GetTruncatedHash(header);

            (byte[] _, byte[] result, bool isValid) = Hashimoto(fullSize, dataSet, headerHashed, header.MixHash, header.Nonce);
            if (!isValid)
            {
                return(false);
            }

            return(IsLessThanTarget(result, header.Difficulty));
        }
Пример #2
0
 public FullDataSet(ulong setSize, IEthashDataSet cache)
 {
     Data = new uint[(uint)(setSize / Ethash.HashBytes)][];
     for (uint i = 0; i < Data.Length; i++)
     {
         Data[i] = cache.CalcDataSetItem(i);
     }
 }
Пример #3
0
        public bool Validate(BlockHeader header)
        {
            uint           epoch        = GetEpoch(header.Number);
            IEthashDataSet cache        = GetOrAddCache(epoch);
            ulong          fullSize     = GetDataSize(epoch);
            Keccak         headerHashed = GetTruncatedHash(header);

            (byte[] _, byte[] result) = Hashimoto(fullSize, cache, headerHashed, header.MixHash, header.Nonce);

            BigInteger threshold = BigInteger.Divide(BigInteger.Pow(2, 256), header.Difficulty);

            return(IsLessThanTarget(result, threshold));
        }
Пример #4
0
        private IEthashDataSet GetOrAddCache(uint epoch, bool precompute = true)
        {
            IEthashDataSet dataSet = _cacheCache.Get(epoch);

            if (dataSet == null)
            {
                uint   cacheSize = GetCacheSize(epoch);
                Keccak seed      = GetSeedHash(epoch);
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Building cache for epoch {epoch}");
                }
                _cacheStopwatch.Restart();
                dataSet = new EthashCache(cacheSize, seed.Bytes);
                _cacheStopwatch.Stop();
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Cache for epoch {epoch} built in {_cacheStopwatch.ElapsedMilliseconds}ms");
                }
                _cacheCache.Set(epoch, dataSet);
            }

            uint epochToPrecompute = epoch + 1;

            if (precompute && _epochsRequested.TryAdd(epochToPrecompute, null))
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Asking to precompute epoch {epochToPrecompute}");
                }
                PrecomputeCache(epochToPrecompute).ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        if (_logger.IsError)
                        {
                            _logger.Error($"Precompute failure at epoch {epochToPrecompute}");
                        }
                    }
                    else
                    {
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"Epoch precompute success at {epochToPrecompute}");
                        }
                    }
                });
            }

            return(dataSet);
        }
Пример #5
0
        public FullDataSet(ulong setSize, IEthashDataSet cache)
        {
            //Console.WriteLine($"building data set of length {setSize}"); // TODO: temp, remove
            Data = new uint[(uint)(setSize / Ethash.HashBytes)][];
            for (uint i = 0; i < Data.Length; i++)
            {
                if (i % 100000 == 0)
                {
                    //Console.WriteLine($"building data set of length {setSize}, built {i}"); // TODO: temp, remove
                }

                Data[i] = cache.CalcDataSetItem(i);
            }
        }
Пример #6
0
        public (byte[], byte[]) Hashimoto(ulong fullSize, IEthashDataSet dataSet, Keccak headerHash, Keccak expectedMixHash, ulong nonce)
        {
            uint       hashesInFull = (uint)(fullSize / HashBytes); // TODO: at current rate would cover around 200 years... but will the block rate change? what with private chains with shorter block times?
            const uint wordsInMix   = MixBytes / WordBytes;
            const uint hashesInMix  = MixBytes / HashBytes;

            byte[] nonceBytes = new byte[8];
            BinaryPrimitives.WriteUInt64LittleEndian(nonceBytes, nonce);

            byte[] headerAndNonceHashed = Keccak512.Compute(Bytes.Concat(headerHash.Bytes, nonceBytes)).Bytes; // this tests fine
            uint[] mixInts = new uint[MixBytes / WordBytes];

            for (int i = 0; i < hashesInMix; i++)
            {
                Buffer.BlockCopy(headerAndNonceHashed, 0, mixInts, i * headerAndNonceHashed.Length, headerAndNonceHashed.Length);
            }

            uint firstOfHeaderAndNonce = GetUInt(headerAndNonceHashed, 0);

            for (uint i = 0; i < Accesses; i++)
            {
                uint   p       = Fnv(i ^ firstOfHeaderAndNonce, mixInts[i % wordsInMix]) % (hashesInFull / hashesInMix) * hashesInMix; // since we take 'hashesInMix' consecutive blocks we want only starting indices of such blocks
                uint[] newData = new uint[wordsInMix];
                for (uint j = 0; j < hashesInMix; j++)
                {
                    uint[] item = dataSet.CalcDataSetItem(p + j);
                    Buffer.BlockCopy(item, 0, newData, (int)(j * item.Length * 4), item.Length * 4);
                }

                Fnv(mixInts, newData);
            }

            uint[] cmixInts = new uint[MixBytes / WordBytes / 4];
            for (uint i = 0; i < mixInts.Length; i += 4)
            {
                cmixInts[i / 4] = Fnv(Fnv(Fnv(mixInts[i], mixInts[i + 1]), mixInts[i + 2]), mixInts[i + 3]);
            }

            byte[] cmix = new byte[MixBytes / WordBytes];
            Buffer.BlockCopy(cmixInts, 0, cmix, 0, cmix.Length);

            if (expectedMixHash != null && !Bytes.AreEqual(cmix, expectedMixHash.Bytes))
            {
                // TODO: handle properly
                throw new InvalidOperationException(); // TODO: need to change this
            }

            return(cmix, Keccak.Compute(Bytes.Concat(headerAndNonceHashed, cmix)).Bytes);  // this tests fine
        }
Пример #7
0
        private IEthashDataSet GetOrAddCache(uint epoch)
        {
            DataSetWithAccessTime theOne;

            lock (_cacheCache)
            {
                for (uint i = Math.Max(epoch, 2) - 2; i < epoch + 3; i++)
                {
                    if (_cacheCache.Get(i) == default)
                    {
                        DataSetWithAccessTime someone = new DataSetWithAccessTime(i, BuildCache(i), Timestamper.Default.EpochSeconds);
                        _cacheCache.Set(i, someone);
                        _cacheMonitor.Add(someone);
                    }
                }

                var now = Timestamper.Default.EpochSeconds;
                theOne            = _cacheCache.Get(epoch);
                theOne.AccessTime = now;

                HashSet <DataSetWithAccessTime> removed = null;
                foreach (DataSetWithAccessTime dataSetWithAccessTime in _cacheMonitor)
                {
                    if (now - dataSetWithAccessTime.AccessTime > 15)
                    {
                        _cacheCache.Delete(dataSetWithAccessTime.Epoch);
                        if (removed == null)
                        {
                            removed = new HashSet <DataSetWithAccessTime>();
                        }

                        removed.Add(dataSetWithAccessTime);
                    }
                }

                if (removed != null)
                {
                    foreach (DataSetWithAccessTime dataSetWithAccessTime in removed)
                    {
                        _cacheMonitor.Remove(dataSetWithAccessTime);
                        dataSetWithAccessTime.DataSet.Result.Dispose();
                    }
                }
            }

            IEthashDataSet dataSet = theOne.DataSet.Result;

            return(dataSet);
        }
Пример #8
0
        public (Keccak MixHash, ulong Nonce) Mine(BlockHeader header, ulong?startNonce = null)
        {
            uint           epoch   = GetEpoch(header.Number);
            IEthashDataSet dataSet = _hintBasedCache.Get(epoch);

            if (dataSet == null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Ethash cache miss for block {header.ToString(BlockHeader.Format.Short)}");
                }
                dataSet = BuildCache(epoch);
            }

            ulong      fullSize     = GetDataSize(epoch);
            ulong      nonce        = startNonce ?? GetRandomNonce();
            BigInteger target       = BigInteger.Divide(_2To256, header.Difficulty);
            Keccak     headerHashed = GetTruncatedHash(header);

            // parallel for (just with ulong...) - adjust based on the available mining threads, low priority
            byte[] mixHash;
            while (true)
            {
                byte[] result;
                (mixHash, result, _) = Hashimoto(fullSize, dataSet, headerHashed, null, nonce);
                if (IsLessThanTarget(result, target))
                {
                    break;
                }

                unchecked
                {
                    nonce += 1;
                }
            }

            return(new Keccak(mixHash), nonce);
        }
Пример #9
0
        // TODO: in a separate thread
        private IEthashDataSet GetOrAddCache(uint epoch)
        {
            IEthashDataSet dataSet = _cacheCache.Get(epoch);

            if (dataSet == null)
            {
                uint   cacheSize = GetCacheSize(epoch);
                Keccak seed      = GetSeedHash(epoch);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Building cache for epoch {epoch}");
                }
                _cacheStopwatch.Restart();
                dataSet = new EthashCache(cacheSize, seed.Bytes);
                _cacheStopwatch.Stop();
                if (_logger.IsInfo)
                {
                    _logger.Info($"Cache for epoch {epoch} built in {_cacheStopwatch.ElapsedMilliseconds}ms");
                }
                _cacheCache.Set(epoch, dataSet);
            }

            return(dataSet);
        }