Пример #1
0
        /// <summary>
        /// Given a block header, checks if the proof of work is valid on the block header. That is, if the header values, nonce, mix hash and difficulty are all suitable.
        /// </summary>
        /// <param name="blockNumber">The number of the block which we wish to validate.</param>
        /// <param name="headerHash">Hash of a portion of the block header which is used with the nonce to generate a seed for the proof.</param>
        /// <param name="mixHash">The resulting mix hash for the provided nonce after running the hashimoto algorithm.</param>
        /// <param name="nonce">The nonce which, along with the other provided values, will calculate the mix hash to be verified.</param>
        /// <param name="difficulty">The difficulty controls filtering for a plausible solution to the block which we wish to mine.</param>
        /// <returns>Returns true if the proof of work is valid, returns false is the proof is invalid.</returns>
        public static bool CheckProof(BigInteger blockNumber, byte[] headerHash, byte[] mixHash, byte[] nonce, BigInteger difficulty)
        {
            // Verify the length of our hashes and nonce
            if (headerHash.Length != KeccakHash.HASH_SIZE || mixHash.Length != KeccakHash.HASH_SIZE || nonce.Length != 8)
            {
                return(false);
            }

            // Flip endianness if we need to (should be little endian).
            byte[] nonceFlipped = (byte[])nonce.Clone();
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(nonceFlipped);
            }

            // Obtain our cache
            Memory <byte> cache = Ethash.MakeCache(blockNumber); // TODO: Make a helper function for this to cache x results.

            // Hash our block with the given nonce and etc.
            var result = Ethash.HashimotoLight(cache, blockNumber, headerHash, nonceFlipped);

            // Verify our mix hash matches
            if (!result.MixHash.ValuesEqual(mixHash))
            {
                return(false);
            }

            // Convert the result to a big integer.
            BigInteger upperBoundInclusive = BigInteger.Pow(2, EVMDefinitions.WORD_SIZE_BITS) / BigInteger.Max(difficulty, 1);
            BigInteger resultInteger       = BigIntegerConverter.GetBigInteger(result.Result);

            return(resultInteger <= upperBoundInclusive);
        }
Пример #2
0
        /// <summary>
        /// Mines a given block starting from the provided nonce for the provided number of rounds.
        /// </summary>
        /// <param name="blockNumber">The number of the block which we are mining.</param>
        /// <param name="difficulty">The difficulty of the block which we are mining.</param>
        /// <param name="miningHash">The mining hash (partial header hash) of the block which we are mining.</param>
        /// <param name="startNonce">The starting nonce we will use and iterate through to try and find a suitable one for the reward.</param>
        /// <param name="rounds">The number of steps to take from our starting nonce before giving up. Use ulong.MaxValue to try all.</param>
        /// <returns>Returns the nonce and mixhash if block is successfully mined, otherwise both are null.</returns>
        public static (byte[] Nonce, byte[] MixHash) Mine(BigInteger blockNumber, BigInteger difficulty, byte[] miningHash, ulong startNonce, ulong rounds)
        {
            // Verify the length of our hashes and nonce
            if (miningHash == null || miningHash.Length != KeccakHash.HASH_SIZE)
            {
                return(null, null);
            }

            // Get our cache, set our start nonce and rounds remaining
            Memory <byte> cache           = Ethash.MakeCache(blockNumber);
            ulong         nonce           = startNonce;
            BigInteger    roundsRemaining = rounds;

            // Obtain our upper bound.
            BigInteger upperBoundInclusive = (BigInteger.Pow(2, EVMDefinitions.WORD_SIZE_BITS) / BigInteger.Max(difficulty, 1)) - 1;

            // Loop for each round.
            for (ulong i = 0; i <= rounds; i++)
            {
                // Increment our nonce.
                nonce++;

                // Obtain the bytes for it
                byte[] nonceData = BitConverter.GetBytes(nonce);

                // Flip endianness if we need to (should be little endian).
                if (!BitConverter.IsLittleEndian)
                {
                    Array.Reverse(nonceData);
                }

                // Obtain our mix hash with this nonce.
                var        result        = Ethash.HashimotoLight(cache, blockNumber, miningHash, nonceData);
                BigInteger resultInteger = BigIntegerConverter.GetBigInteger(result.Result);

                // If our result is below our difficulty bound.
                if (resultInteger <= upperBoundInclusive)
                {
                    // Flip endianness if we need to (returning nonce should be big endian).
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(nonceData);
                    }

                    // Return our nonce and mix hash.
                    return(nonceData, result.MixHash);
                }
            }

            return(null, null);
        }