예제 #1
0
        override unsafe protected void MinerThread()
        {
            Random r = new Random();

            UInt32[] ethashOutput     = new UInt32[256];
            byte[]   ethashHeaderhash = new byte[32];

            MarkAsAlive();

            MainForm.Logger("Miner thread for Device #" + DeviceIndex + " started.");

            BuildEthashProgram();

            fixed(UInt32 *ethashOutputPtr = ethashOutput)
            fixed(byte *ethashHeaderhashPtr = ethashHeaderhash)
            fixed(byte *pascalMidstatePtr   = mPascalMidstate)
            fixed(byte *pascalInputPtr      = mPascalInput)
            fixed(UInt32 * pascalOutputPtr  = mPascalOutput)
            while (!Stopped)
            {
                MarkAsAlive();

                try
                {
                    int  ethashEpoch   = -1;
                    long ethashDAGSize = 0;
                    ComputeBuffer <byte> ethashDAGBuffer = null;

                    mEthashSearchKernel.SetMemoryArgument(7 + 0, mPascalInputBuffer);
                    mEthashSearchKernel.SetMemoryArgument(7 + 1, mPascalOutputBuffer);
                    mEthashSearchKernel.SetMemoryArgument(7 + 4, mPascalMidstateBuffer);
                    mEthashSearchKernel.SetValueArgument <UInt32>(7 + 5, mPascalRatio);

                    // Wait for the first job to arrive.
                    int elapsedTime = 0;
                    while ((mEthashStratum == null || mEthashStratum.GetJob() == null || mPascalStratum == null || mPascalStratum.GetJob() == null) && elapsedTime < 60000)
                    {
                        Thread.Sleep(100);
                        elapsedTime += 100;
                    }
                    if (mEthashStratum == null || mEthashStratum.GetJob() == null || mPascalStratum == null || mPascalStratum.GetJob() == null)
                    {
                        MainForm.Logger("Ethash stratum server failed to send a new job.");
                        //throw new TimeoutException("Stratum server failed to send a new job.");
                        return;
                    }

                    System.Diagnostics.Stopwatch consoleUpdateStopwatch = new System.Diagnostics.Stopwatch();
                    EthashStratum.Work           ethashWork;
                    PascalStratum.Work           pascalWork;

                    while (!Stopped && (ethashWork = mEthashStratum.GetWork()) != null && (pascalWork = mPascalStratum.GetWork()) != null)
                    {
                        MarkAsAlive();

                        String ethashPoolExtranonce      = mEthashStratum.PoolExtranonce;
                        byte[] ethashExtranonceByteArray = Utilities.StringToByteArray(ethashPoolExtranonce);
                        byte   ethashLocalExtranonce     = (byte)ethashWork.LocalExtranonce;
                        UInt64 ethashStartNonce          = (UInt64)ethashLocalExtranonce << (8 * (7 - ethashExtranonceByteArray.Length));
                        for (int i = 0; i < ethashExtranonceByteArray.Length; ++i)
                        {
                            ethashStartNonce |= (UInt64)ethashExtranonceByteArray[i] << (8 * (7 - i));
                        }
                        ethashStartNonce += (ulong)r.Next(0, int.MaxValue) & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8));
                        String ethashJobID      = ethashWork.GetJob().ID;
                        String ethashSeedhash   = ethashWork.GetJob().Seedhash;
                        double ethashDifficulty = mEthashStratum.Difficulty;
                        Buffer.BlockCopy(Utilities.StringToByteArray(ethashWork.GetJob().Headerhash), 0, ethashHeaderhash, 0, 32);
                        Queue.Write <byte>(mEthashHeaderBuffer, true, 0, 32, (IntPtr)ethashHeaderhashPtr, null);

                        var pascalJob = pascalWork.Job;
                        Array.Copy(pascalWork.Blob, mPascalInput, sPascalInputSize);
                        CalculatePascalMidState();
                        Queue.Write <byte>(mPascalMidstateBuffer, true, 0, sPascalMidstateSize, (IntPtr)pascalMidstatePtr, null);
                        UInt32 pascalStartNonce = (UInt32)(r.Next(0, int.MaxValue));
                        UInt64 PascalTarget     = (UInt64)((double)0xffff0000UL / mPascalStratum.Difficulty);
                        mEthashSearchKernel.SetValueArgument <UInt64>(7 + 3, PascalTarget);
                        Queue.Write <byte>(mPascalInputBuffer, true, 0, sPascalInputSize, (IntPtr)pascalInputPtr, null);

                        if (ethashEpoch != ethashWork.GetJob().Epoch)
                        {
                            if (ethashDAGBuffer != null)
                            {
                                ethashDAGBuffer.Dispose();
                                ethashDAGBuffer = null;
                            }
                            ethashEpoch = ethashWork.GetJob().Epoch;
                            DAGCache cache = new DAGCache(ethashEpoch, ethashWork.GetJob().Seedhash);
                            ethashDAGSize = Utilities.GetDAGSize(ethashEpoch);

                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                            sw.Start();
                            mEthashGlobalWorkSizeArray[0]  = ethashDAGSize / 64;
                            mEthashGlobalWorkSizeArray[0] /= 8;
                            if (mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0] > 0)
                            {
                                mEthashGlobalWorkSizeArray[0] += mEthashLocalWorkSizeArray[0] - mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0];
                            }

                            ComputeBuffer <byte> DAGCacheBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, cache.GetData().Length);

                            fixed(byte *p = cache.GetData())
                            Queue.Write <byte>(DAGCacheBuffer, true, 0, cache.GetData().Length, (IntPtr)p, null);

                            ethashDAGBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadWrite, mEthashGlobalWorkSizeArray[0] * 8 * 64 /* ethashDAGSize */); // With this, we can remove a conditional statement in the DAG kernel.

                            mEthashDAGKernel.SetValueArgument <UInt32>(0, 0);
                            mEthashDAGKernel.SetMemoryArgument(1, DAGCacheBuffer);
                            mEthashDAGKernel.SetMemoryArgument(2, ethashDAGBuffer);
                            mEthashDAGKernel.SetValueArgument <UInt32>(3, (UInt32)cache.GetData().Length / 64);
                            mEthashDAGKernel.SetValueArgument <UInt32>(4, 0xffffffffu);

                            for (long start = 0; start < ethashDAGSize / 64; start += mEthashGlobalWorkSizeArray[0])
                            {
                                mEthashGlobalWorkOffsetArray[0] = start;
                                Queue.Execute(mEthashDAGKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);
                                Queue.Finish();
                                if (Stopped || !mEthashStratum.GetJob().ID.Equals(ethashJobID))
                                {
                                    break;
                                }
                            }
                            DAGCacheBuffer.Dispose();
                            if (Stopped || !mEthashStratum.GetJob().ID.Equals(ethashJobID))
                            {
                                break;
                            }
                            sw.Stop();
                            MainForm.Logger("Generated DAG for Epoch #" + ethashEpoch + " (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                        }

                        consoleUpdateStopwatch.Start();

                        while (!Stopped && mEthashStratum.GetJob().ID.Equals(ethashJobID) && mEthashStratum.PoolExtranonce.Equals(ethashPoolExtranonce) && mPascalStratum.GetJob().Equals(pascalJob))
                        {
                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                            sw.Start();

                            MarkAsAlive();

                            // Get a new local extranonce if necessary.
                            if ((ethashStartNonce & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8)) + (ulong)mEthashGlobalWorkSizeArray[0] * 3 / 4) >= ((ulong)0x1 << (64 - (ethashExtranonceByteArray.Length * 8 + 8))))
                            {
                                break;
                            }
                            if (0xffffffffu - pascalStartNonce < (UInt32)mEthashGlobalWorkSizeArray[0] * mPascalRatio)
                            {
                                break;
                            }

                            UInt64 target = (UInt64)((double)0xffff0000U / ethashDifficulty);
                            mEthashSearchKernel.SetMemoryArgument(0, mEthashOutputBuffer);                   // g_output
                            mEthashSearchKernel.SetMemoryArgument(1, mEthashHeaderBuffer);                   // g_header
                            mEthashSearchKernel.SetMemoryArgument(2, ethashDAGBuffer);                       // _g_dag
                            mEthashSearchKernel.SetValueArgument <UInt32>(3, (UInt32)(ethashDAGSize / 128)); // DAG_SIZE
                            mEthashSearchKernel.SetValueArgument <UInt64>(4, ethashStartNonce);              // start_nonce
                            mEthashSearchKernel.SetValueArgument <UInt64>(5, target);                        // target
                            mEthashSearchKernel.SetValueArgument <UInt32>(6, 0xffffffffu);                   // isolate

                            mEthashSearchKernel.SetValueArgument <UInt32>(7 + 2, pascalStartNonce);

                            ethashOutput[255] = 0; // ethashOutput[255] is used as an atomic counter.
                            Queue.Write <UInt32>(mEthashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);
                            mEthashGlobalWorkOffsetArray[0] = 0;
                            mPascalOutput[255] = 0; // mPascalOutput[255] is used as an atomic counter.
                            Queue.Write <UInt32>(mPascalOutputBuffer, true, 0, sPascalOutputSize, (IntPtr)pascalOutputPtr, null);
                            Queue.Execute(mEthashSearchKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);

                            Queue.Read <UInt32>(mEthashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);
                            if (mEthashStratum.GetJob().ID.Equals(ethashJobID))
                            {
                                for (int i = 0; i < ethashOutput[255]; ++i)
                                {
                                    mEthashStratum.Submit(GatelessGateDevice, ethashWork.GetJob(), ethashStartNonce + (UInt64)ethashOutput[i]);
                                }
                            }
                            ethashStartNonce += (UInt64)mEthashGlobalWorkSizeArray[0] * 3 / 4;

                            Queue.Read <UInt32>(mPascalOutputBuffer, true, 0, sPascalOutputSize, (IntPtr)pascalOutputPtr, null);
                            if (mPascalStratum.GetJob().Equals(pascalJob))
                            {
                                for (int i = 0; i < mPascalOutput[255]; ++i)
                                {
                                    mPascalStratum.Submit(GatelessGateDevice, pascalWork, mPascalOutput[i]);
                                }
                            }
                            pascalStartNonce += (UInt32)mEthashGlobalWorkSizeArray[0] * mPascalRatio;

                            sw.Stop();
                            Speed       = ((double)mEthashGlobalWorkSizeArray[0]) / sw.Elapsed.TotalSeconds * 0.75;
                            SecondSpeed = ((double)mEthashGlobalWorkSizeArray[0]) / sw.Elapsed.TotalSeconds * mPascalRatio;
                            if (consoleUpdateStopwatch.ElapsedMilliseconds >= 10 * 1000)
                            {
                                MainForm.Logger("Device #" + DeviceIndex + ": " + String.Format("{0:N2} Mh/s (Ethash), ", Speed / (1000000)) + String.Format("{0:N2} Mh/s (Pascal)", SecondSpeed / (1000000)));
                                consoleUpdateStopwatch.Restart();
                            }
                        }
                    }

                    if (ethashDAGBuffer != null)
                    {
                        ethashDAGBuffer.Dispose();
                        ethashDAGBuffer = null;
                    }
                } catch (Exception ex) {
                    MainForm.Logger("Exception in miner thread: " + ex.Message + ex.StackTrace);
                    Speed = 0;
                    if (UnrecoverableException.IsUnrecoverableException(ex))
                    {
                        this.UnrecoverableException = new UnrecoverableException(ex, GatelessGateDevice);
                        Stop();
                    }
                    else
                    {
                        MainForm.Logger("Restarting miner thread...");
                        System.Threading.Thread.Sleep(5000);
                    }
                }
            }

            MarkAsDone();
        }
            public String GetMixHash(UInt64 nonce)
            {
                IHash hash = HashFactory.Crypto.SHA3.CreateKeccak512();

                byte[] data;

                System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                sw.Start();

                // def hashimoto(header, nonce, full_size, dataset_lookup):
                int epoch = Epoch;

                byte[]   seedhashArray   = Utilities.StringToByteArray(Seedhash);
                byte[]   headerhashArray = Utilities.StringToByteArray(Headerhash);
                DAGCache cache           = new DAGCache(epoch, Seedhash);

                data = cache.GetData();
                long fullSize = Utilities.GetDAGSize(epoch);
                //     n = full_size / HASH_BYTES
                int n = (int)(fullSize / HASH_BYTES);
                //     w = MIX_BYTES // WORD_BYTES
                int w = MIX_BYTES / WORD_BYTES;
                //     mixhashes = MIX_BYTES / HASH_BYTES
                int mixhashes = MIX_BYTES / HASH_BYTES;

                //     # combine header+nonce into a 64 byte seed
                //     s = sha3_512(header + nonce[::-1])
                byte[] combined = new byte[headerhashArray.Length + sizeof(UInt64)];
                Buffer.BlockCopy(headerhashArray, 0, combined, 0, headerhashArray.Length);
                for (int i = 0; i < 8; ++i)
                {
                    combined[headerhashArray.Length + i] = (byte)((nonce >> (i * 8)) & 0xff);
                }
                byte[] s = hash.ComputeBytes(combined).GetBytes();
                //     # start the mix with replicated s
                //     mix = []
                //     for _ in range(MIX_BYTES / HASH_BYTES):
                //         mix.extend(s)
                byte[] mix = new byte[s.Length * mixhashes];
                for (int i = 0; i < mixhashes; ++i)
                {
                    Buffer.BlockCopy(s, 0, mix, i * s.Length, s.Length);
                }
                //     # mix in random dataset nodes
                //     for i in range(ACCESSES):
                for (int i = 0; i < ACCESSES; ++i)
                {
                    //         p = fnv(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes
                    UInt32 v1       = ((UInt32)s[0] ^ (UInt32)i) | ((UInt32)s[1] << 8) | ((UInt32)s[2] << 16) | ((UInt32)s[3] << 24);
                    int    mixIndex = (i % w) * 4;
                    UInt32 v2       = ((UInt32)mix[mixIndex + 0] << 0) | ((UInt32)mix[mixIndex + 1] << 8) | ((UInt32)mix[mixIndex + 2] << 16) | ((UInt32)mix[mixIndex + 3] << 24);
                    int    p        = (int)(FNV(v1, v2) % (n / mixhashes) * mixhashes);
                    //         newdata = []
                    //         for j in range(MIX_BYTES / HASH_BYTES):
                    //             newdata.extend(dataset_lookup(p + j))
                    byte[] newData = new byte[s.Length * mixhashes];
                    for (int j = 0; j < mixhashes; ++j)
                    {
                        Buffer.BlockCopy(CalculateDatasetItem(data, p + j), 0, newData, j * (int)s.Length, (int)s.Length);
                    }
                    //         mix = map(fnv, mix, newdata)
                    for (int j = 0; j < s.Length * mixhashes; j += 4)
                    {
                        v1 = ((UInt32)mix[j + 0] << 0) | ((UInt32)mix[j + 1] << 8) | ((UInt32)mix[j + 2] << 16) | ((UInt32)mix[j + 3] << 24);
                        v2 = ((UInt32)newData[j + 0] << 0) | ((UInt32)newData[j + 1] << 8) | ((UInt32)newData[j + 2] << 16) | ((UInt32)newData[j + 3] << 24);
                        UInt32 result = FNV(v1, v2);
                        for (int k = 0; k < 4; ++k)
                        {
                            mix[j + k] = (byte)((result >> (k * 8)) & 0xff);
                        }
                    }
                }
                //     # compress mix
                //     cmix = []
                //     for i in range(0, len(mix), 4):
                //         cmix.append(fnv(fnv(fnv(mix[i], mix[i+1]), mix[i+2]), mix[i+3]))
                byte[] cmix = new byte[mix.Length / 4];
                for (int i = 0; i < mix.Length / 4; i += 4)
                {
                    UInt32 v1 = ((UInt32)mix[i * 4 + 0] << 0) | ((UInt32)mix[i * 4 + 1] << 8) | ((UInt32)mix[i * 4 + 2] << 16) | ((UInt32)mix[i * 4 + 3] << 24);
                    for (int j = 1; j < 4; ++j)
                    {
                        UInt32 v2 = ((UInt32)mix[(i + j) * 4 + 0] << 0) | ((UInt32)mix[(i + j) * 4 + 1] << 8) | ((UInt32)mix[(i + j) * 4 + 2] << 16) | ((UInt32)mix[(i + j) * 4 + 3] << 24);
                        v1 = FNV(v1, v2);
                    }
                    for (int j = 0; j < 4; ++j)
                    {
                        cmix[i + j] = (byte)((v1 >> (j * 8)) & 0xff);
                    }
                }
                //     return {
                //         "mix digest": serialize_hash(cmix),
                //         "result": serialize_hash(sha3_256(s+cmix))
                //     }
                sw.Stop();
                MainForm.Logger("Generated mix hash (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                return(Utilities.ByteArrayToString(cmix));
            }
        override unsafe protected void MinerThread()
        {
            Random r = new Random();

            MarkAsAlive();

            MainForm.Logger("Dual Ethash/Lbry miner thread for Device #" + DeviceIndex + " started.");

            BuildEthashProgram();

            fixed(UInt32 *ethashOutputPtr = mEthashOutput)
            fixed(byte *ethashHeaderhashPtr = mEthashHeaderhash)
            fixed(byte *lbryInputPtr        = mLbryInput)
            fixed(UInt32 * lbryOutputPtr    = mLbryOutput)
            while (!Stopped)
            {
                MarkAsAlive();

                try
                {
                    int  ethashEpoch   = -1;
                    long ethashDAGSize = 0;
                    ComputeBuffer <byte> ethashDAGBuffer = null;

                    // Wait for the first job to arrive.
                    int elapsedTime = 0;
                    while ((mEthashStratum == null || mEthashStratum.GetJob() == null || mLbryStratum == null || mLbryStratum.GetJob() == null) && elapsedTime < Parameters.TimeoutForFirstJobInMilliseconds && !Stopped)
                    {
                        Thread.Sleep(100);
                        elapsedTime += 100;
                    }
                    if (mEthashStratum == null || mEthashStratum.GetJob() == null || mLbryStratum == null || mLbryStratum.GetJob() == null)
                    {
                        MainForm.Logger("Stratum server failed to send a new job.");
                        return;
                    }

                    System.Diagnostics.Stopwatch consoleUpdateStopwatch = new System.Diagnostics.Stopwatch();
                    EthashStratum.Work           ethashWork;
                    LbryStratum.Work             lbryWork;

                    mEthashSearchKernel.SetMemoryArgument(7, mLbryInputBuffer);
                    mEthashSearchKernel.SetMemoryArgument(8, mLbryOutputBuffer);

                    while (!Stopped && (ethashWork = mEthashStratum.GetWork()) != null && (lbryWork = mLbryStratum.GetWork()) != null)
                    {
                        MarkAsAlive();

                        String ethashPoolExtranonce      = mEthashStratum.PoolExtranonce;
                        byte[] ethashExtranonceByteArray = Utilities.StringToByteArray(ethashPoolExtranonce);
                        byte   ethashLocalExtranonce     = (byte)ethashWork.LocalExtranonce;
                        UInt64 ethashStartNonce          = (UInt64)ethashLocalExtranonce << (8 * (7 - ethashExtranonceByteArray.Length));
                        for (int i = 0; i < ethashExtranonceByteArray.Length; ++i)
                        {
                            ethashStartNonce |= (UInt64)ethashExtranonceByteArray[i] << (8 * (7 - i));
                        }
                        ethashStartNonce += (ulong)r.Next(0, int.MaxValue) & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8));
                        String ethashJobID      = ethashWork.GetJob().ID;
                        String ethashSeedhash   = ethashWork.GetJob().Seedhash;
                        double ethashDifficulty = mEthashStratum.Difficulty;
                        Buffer.BlockCopy(Utilities.StringToByteArray(ethashWork.GetJob().Headerhash), 0, mEthashHeaderhash, 0, 32);
                        Queue.Write <byte>(mEthashHeaderBuffer, true, 0, 32, (IntPtr)ethashHeaderhashPtr, null);

                        var lbryJob = lbryWork.Job;
                        Array.Copy(lbryWork.Blob, mLbryInput, 112);
                        UInt32 lbryStartNonce = (UInt32)(r.Next(0, int.MaxValue));
                        UInt64 lbryTarget     = (UInt64)((double)0xffff0000UL / (mLbryStratum.Difficulty / 256));
                        mEthashSearchKernel.SetValueArgument <UInt64>(10, lbryTarget);
                        Queue.Write <byte>(mLbryInputBuffer, true, 0, 112, (IntPtr)lbryInputPtr, null);

                        if (ethashEpoch != ethashWork.GetJob().Epoch)
                        {
                            if (ethashDAGBuffer != null)
                            {
                                ethashDAGBuffer.Dispose();
                                ethashDAGBuffer = null;
                            }
                            ethashEpoch = ethashWork.GetJob().Epoch;
                            DAGCache cache = new DAGCache(ethashEpoch, ethashWork.GetJob().Seedhash);
                            ethashDAGSize = Utilities.GetDAGSize(ethashEpoch);

                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                            sw.Start();
                            mEthashGlobalWorkSizeArray[0]  = ethashDAGSize / 64;
                            mEthashGlobalWorkSizeArray[0] /= 8;
                            if (mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0] > 0)
                            {
                                mEthashGlobalWorkSizeArray[0] += mEthashLocalWorkSizeArray[0] - mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0];
                            }

                            ComputeBuffer <byte> DAGCacheBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, cache.GetData().Length);

                            fixed(byte *p = cache.GetData())
                            Queue.Write <byte>(DAGCacheBuffer, true, 0, cache.GetData().Length, (IntPtr)p, null);

                            ethashDAGBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadWrite, mEthashGlobalWorkSizeArray[0] * 8 * 64 /* ethashDAGSize */); // With this, we can remove a conditional statement in the DAG kernel.

                            mEthashDAGKernel.SetValueArgument <UInt32>(0, 0);
                            mEthashDAGKernel.SetMemoryArgument(1, DAGCacheBuffer);
                            mEthashDAGKernel.SetMemoryArgument(2, ethashDAGBuffer);
                            mEthashDAGKernel.SetValueArgument <UInt32>(3, (UInt32)cache.GetData().Length / 64);
                            mEthashDAGKernel.SetValueArgument <UInt32>(4, 0xffffffffu);

                            for (long start = 0; start < ethashDAGSize / 64; start += mEthashGlobalWorkSizeArray[0])
                            {
                                mEthashGlobalWorkOffsetArray[0] = start;
                                Queue.Execute(mEthashDAGKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);
                                Queue.Finish();
                                if (Stopped || !mEthashStratum.GetJob().ID.Equals(ethashJobID))
                                {
                                    break;
                                }
                            }
                            DAGCacheBuffer.Dispose();
                            if (Stopped || !mEthashStratum.GetJob().ID.Equals(ethashJobID))
                            {
                                break;
                            }
                            sw.Stop();
                            MainForm.Logger("Generated DAG for Epoch #" + ethashEpoch + " (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                        }

                        consoleUpdateStopwatch.Start();

                        while (!Stopped && mEthashStratum.GetJob().ID.Equals(ethashJobID) && mEthashStratum.PoolExtranonce.Equals(ethashPoolExtranonce) && mLbryStratum.GetJob().Equals(lbryJob))
                        {
                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                            sw.Start();

                            MarkAsAlive();

                            mEthashGlobalWorkOffsetArray[0] = 0;

                            mEthashSearchKernel.SetValueArgument <UInt32>(9, lbryStartNonce);;

                            // Get a new local extranonce if necessary.
                            if ((ethashStartNonce & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8)) + (ulong)mEthashGlobalWorkSizeArray[0] * 3 / 4) >= ((ulong)0x1 << (64 - (ethashExtranonceByteArray.Length * 8 + 8))))
                            {
                                break;
                            }
                            if (0xffffffffu - lbryStartNonce < (UInt32)mEthashGlobalWorkSizeArray[0] / 4)
                            {
                                break;
                            }

                            UInt64 target = (UInt64)((double)0xffff0000U / ethashDifficulty);
                            mEthashSearchKernel.SetMemoryArgument(0, mEthashOutputBuffer);                   // g_output
                            mEthashSearchKernel.SetMemoryArgument(1, mEthashHeaderBuffer);                   // g_header
                            mEthashSearchKernel.SetMemoryArgument(2, ethashDAGBuffer);                       // _g_dag
                            mEthashSearchKernel.SetValueArgument <UInt32>(3, (UInt32)(ethashDAGSize / 128)); // DAG_SIZE
                            mEthashSearchKernel.SetValueArgument <UInt64>(4, ethashStartNonce);              // start_nonce
                            mEthashSearchKernel.SetValueArgument <UInt64>(5, target);                        // target
                            mEthashSearchKernel.SetValueArgument <UInt32>(6, 0xffffffffu);                   // isolate
                            mEthashOutput[255] = 0;                                                          // mEthashOutput[255] is used as an atomic counter.
                            Queue.Write <UInt32>(mEthashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);

                            mLbryOutput[255] = 0; // mLbryOutput[255] is used as an atomic counter.
                            Queue.Write <UInt32>(mLbryOutputBuffer, true, 0, lbryOutputSize, (IntPtr)lbryOutputPtr, null);

                            Queue.Execute(mEthashSearchKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);

                            Queue.Read <UInt32>(mEthashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);
                            if (mEthashStratum.GetJob().ID.Equals(ethashJobID))
                            {
                                for (int i = 0; i < mEthashOutput[255]; ++i)
                                {
                                    mEthashStratum.Submit(Device, ethashWork.GetJob(), ethashStartNonce + (UInt64)mEthashOutput[i]);
                                }
                            }
                            ethashStartNonce += (UInt64)mEthashGlobalWorkSizeArray[0] * 3 / 4;

                            Queue.Read <UInt32>(mLbryOutputBuffer, true, 0, lbryOutputSize, (IntPtr)lbryOutputPtr, null);
                            if (mLbryStratum.GetJob().Equals(lbryJob))
                            {
                                for (int i = 0; i < mLbryOutput[255]; ++i)
                                {
                                    String result = "";
                                    for (int j = 0; j < 8; ++j)
                                    {
                                        UInt32 word = mLbryOutput[256 + i * 8 + j];
                                        result += String.Format("{0:x2}{1:x2}{2:x2}{3:x2}", ((word >> 0) & 0xff), ((word >> 8) & 0xff), ((word >> 16) & 0xff), ((word >> 24) & 0xff));
                                    }
                                    mLbryStratum.Submit(Device, lbryWork, mLbryOutput[i], result);
                                }
                            }
                            lbryStartNonce += (UInt32)mEthashGlobalWorkSizeArray[0] / 4;

                            sw.Stop();
                            Speed = ((double)mEthashGlobalWorkSizeArray[0] * 3 / 4) / sw.Elapsed.TotalSeconds;
                            double speedSecondary = (((double)mEthashGlobalWorkSizeArray[0] / 4) / sw.Elapsed.TotalSeconds);
                            if (consoleUpdateStopwatch.ElapsedMilliseconds >= 10 * 1000)
                            {
                                MainForm.Logger("Device #" + DeviceIndex + " (Ethash): " + String.Format("{0:N2} Mh/s (Ethash), {1:N2} Mh/s (Lbry)", Speed / (1000000), speedSecondary / (1000000)));
                                consoleUpdateStopwatch.Restart();
                            }
                        }
                    }

                    if (ethashDAGBuffer != null)
                    {
                        ethashDAGBuffer.Dispose();
                        ethashDAGBuffer = null;
                    }
                } catch (Exception ex) {
                    MainForm.Logger("Exception in miner thread: " + ex.Message + ex.StackTrace);
                    Speed = 0;
                    if (UnrecoverableException.IsUnrecoverableException(ex))
                    {
                        this.UnrecoverableException = new UnrecoverableException(ex, Device);
                        Stop();
                    }
                    else
                    {
                        MainForm.Logger("Restarting miner thread...");
                        System.Threading.Thread.Sleep(5000);
                    }
                }
            }

            MarkAsDone();
        }
예제 #4
0
        override unsafe protected void MinerThread()
        {
            Random r = new Random();

            UInt32[]      output        = new UInt32[256];
            ComputeDevice computeDevice = Device.GetComputeDevice();

            MarkAsAlive();

            MainForm.Logger("Miner thread for Device #" + DeviceIndex + " started.");

            ComputeProgram program;

            try { mProgramArrayMutex.WaitOne(); } catch (Exception) { }
            if (mProgramArray.ContainsKey(new long[] { DeviceIndex, mLocalWorkSize }))
            {
                program = mProgramArray[new long[] { DeviceIndex, mLocalWorkSize }];
            }
            else
            {
                String source = System.IO.File.ReadAllText(@"Kernels\ethash.cl");
                program = new ComputeProgram(Context, source);
                MainForm.Logger("Loaded ethash program for Device #" + DeviceIndex + ".");
                String buildOptions = (Device.Vendor == "AMD"
                                          ? "-O1 "
                                          : Device.Vendor == "NVIDIA"
                                              ? ""
                                              : // "-cl-nv-opt-level=1 -cl-nv-maxrregcount=256 " :
                                       "")
                                      + " -IKernels -DWORKSIZE=" + mLocalWorkSize;
                try
                {
                    program.Build(Device.DeviceList, buildOptions, null, IntPtr.Zero);
                }
                catch (Exception)
                {
                    MainForm.Logger(program.GetBuildLog(computeDevice));
                    throw;
                }
                MainForm.Logger("Built cryptonight program for Device #" + DeviceIndex + ".");
                MainForm.Logger("Built options: " + buildOptions);
                mProgramArray[new long[] { DeviceIndex, mLocalWorkSize }] = program;
            }
            try { mProgramArrayMutex.ReleaseMutex(); } catch (Exception) { }

            while (!Stopped)
            {
                MarkAsAlive();

                try
                {
                    // Wait for the first job to arrive.
                    int elapsedTime = 0;
                    while ((mStratum == null || mStratum.GetJob() == null) && elapsedTime < 5000)
                    {
                        Thread.Sleep(10);
                        elapsedTime += 10;
                    }
                    if (mStratum == null || mStratum.GetJob() == null)
                    {
                        MainForm.Logger("Stratum server failed to send a new job.");
                        //throw new TimeoutException("Stratum server failed to send a new job.");
                        return;
                    }

                    int  epoch   = -1;
                    long DAGSize = 0;
                    ComputeBuffer <byte> DAGBuffer = null;

                    using (ComputeKernel DAGKernel = program.CreateKernel("GenerateDAG"))
                        using (ComputeKernel searchKernel = program.CreateKernel("search"))
                            using (ComputeBuffer <UInt32> outputBuffer = new ComputeBuffer <UInt32>(Context, ComputeMemoryFlags.ReadWrite, 256))
                                using (ComputeBuffer <byte> headerBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, 32))
                                {
                                    MarkAsAlive();

                                    System.Diagnostics.Stopwatch consoleUpdateStopwatch = new System.Diagnostics.Stopwatch();
                                    EthashStratum.Work           work;

                                    while (!Stopped && (work = mStratum.GetWork()) != null)
                                    {
                                        String poolExtranonce      = mStratum.PoolExtranonce;
                                        byte[] extranonceByteArray = Utilities.StringToByteArray(poolExtranonce);
                                        byte   localExtranonce     = work.LocalExtranonce;
                                        UInt64 startNonce          = (UInt64)localExtranonce << (8 * (7 - extranonceByteArray.Length));
                                        for (int i = 0; i < extranonceByteArray.Length; ++i)
                                        {
                                            startNonce |= (UInt64)extranonceByteArray[i] << (8 * (7 - i));
                                        }
                                        startNonce += (ulong)r.Next(0, int.MaxValue) & (0xfffffffffffffffful >> (extranonceByteArray.Length * 8 + 8));
                                        String jobID      = work.GetJob().ID;
                                        String headerhash = work.GetJob().Headerhash;
                                        String seedhash   = work.GetJob().Seedhash;
                                        double difficulty = mStratum.Difficulty;

                                        fixed(byte *p = Utilities.StringToByteArray(headerhash))
                                        Queue.Write <byte>(headerBuffer, true, 0, 32, (IntPtr)p, null);

                                        if (epoch != work.GetJob().Epoch)
                                        {
                                            if (DAGBuffer != null)
                                            {
                                                DAGBuffer.Dispose();
                                                DAGBuffer = null;
                                            }
                                            epoch = work.GetJob().Epoch;
                                            DAGCache cache = new DAGCache(epoch, work.GetJob().Seedhash);
                                            DAGSize = Utilities.GetDAGSize(epoch);

                                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                                            sw.Start();
                                            fixed(byte *p = cache.GetData())
                                            {
                                                long globalWorkSize = DAGSize / 64;

                                                globalWorkSize /= 8;
                                                if (globalWorkSize % mLocalWorkSize > 0)
                                                {
                                                    globalWorkSize += mLocalWorkSize - globalWorkSize % mLocalWorkSize;
                                                }

                                                ComputeBuffer <byte> DAGCacheBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, cache.GetData().Length, (IntPtr)p);

                                                DAGBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadWrite, globalWorkSize * 8 * 64 /* DAGSize */); // With this, we can remove a conditional statement in the DAG kernel.

                                                DAGKernel.SetValueArgument <UInt32>(0, 0);
                                                DAGKernel.SetMemoryArgument(1, DAGCacheBuffer);
                                                DAGKernel.SetMemoryArgument(2, DAGBuffer);
                                                DAGKernel.SetValueArgument <UInt32>(3, (UInt32)cache.GetData().Length / 64);
                                                DAGKernel.SetValueArgument <UInt32>(4, 0xffffffffu);

                                                for (long start = 0; start < DAGSize / 64; start += globalWorkSize)
                                                {
                                                    Queue.Execute(DAGKernel, new long[] { start }, new long[] { globalWorkSize }, new long[] { mLocalWorkSize }, null);
                                                    Queue.Finish();
                                                    if (Stopped || !mStratum.GetJob().ID.Equals(jobID))
                                                    {
                                                        break;
                                                    }
                                                }
                                                DAGCacheBuffer.Dispose();
                                                if (Stopped || !mStratum.GetJob().ID.Equals(jobID))
                                                {
                                                    break;
                                                }
                                            }
                                            sw.Stop();
                                            MainForm.Logger("Generated DAG for Epoch #" + epoch + " (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                                        }

                                        consoleUpdateStopwatch.Start();

                                        while (!Stopped && mStratum.GetJob().ID.Equals(jobID) && mStratum.PoolExtranonce.Equals(poolExtranonce))
                                        {
                                            MarkAsAlive();

                                            // Get a new local extranonce if necessary.
                                            if ((startNonce & (0xfffffffffffffffful >> (extranonceByteArray.Length * 8 + 8)) + (ulong)mGlobalWorkSize) >= ((ulong)0x1 << (64 - (extranonceByteArray.Length * 8 + 8))))
                                            {
                                                break;
                                            }

                                            UInt64 target = (UInt64)((double)0xffff0000U / difficulty);
                                            searchKernel.SetMemoryArgument(0, outputBuffer);                    // g_output
                                            searchKernel.SetMemoryArgument(1, headerBuffer);                    // g_header
                                            searchKernel.SetMemoryArgument(2, DAGBuffer);                       // _g_dag
                                            searchKernel.SetValueArgument <UInt32>(3, (UInt32)(DAGSize / 128)); // DAG_SIZE
                                            searchKernel.SetValueArgument <UInt64>(4, startNonce);              // start_nonce
                                            searchKernel.SetValueArgument <UInt64>(5, target);                  // target
                                            searchKernel.SetValueArgument <UInt32>(6, 0xffffffffu);             // isolate

                                            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                                            sw.Start();
                                            fixed(UInt32 *p = output)
                                            {
                                                output[255] = 0; // output[255] is used as an atomic counter.
                                                Queue.Write <UInt32>(outputBuffer, true, 0, 256, (IntPtr)p, null);
                                                Queue.Execute(searchKernel, new long[] { 0 }, new long[] { mGlobalWorkSize }, new long[] { mLocalWorkSize }, null);
                                                Queue.Read <UInt32>(outputBuffer, true, 0, 256, (IntPtr)p, null);
                                            }
                                            sw.Stop();
                                            mSpeed = ((double)mGlobalWorkSize) / sw.Elapsed.TotalSeconds;
                                            if (consoleUpdateStopwatch.ElapsedMilliseconds >= 10 * 1000)
                                            {
                                                MainForm.Logger("Device #" + DeviceIndex + ": " + String.Format("{0:N2} Mh/s", mSpeed / (1000000)));
                                                consoleUpdateStopwatch.Restart();
                                            }
                                            if (mStratum.GetJob().ID.Equals(jobID))
                                            {
                                                for (int i = 0; i < output[255]; ++i)
                                                {
                                                    mStratum.Submit(GatelessGateDevice, work.GetJob(), startNonce + (UInt64)output[i]);
                                                }
                                            }
                                            startNonce += (UInt64)mGlobalWorkSize;
                                        }
                                    }
                                }

                    if (DAGBuffer != null)
                    {
                        DAGBuffer.Dispose();
                        DAGBuffer = null;
                    }
                }
                catch (Exception ex)
                {
                    MainForm.Logger("Exception in miner thread: " + ex.Message + ex.StackTrace);
                    MainForm.Logger("Restarting miner thread...");
                }
            }

            MarkAsDone();
        }
        unsafe public void MinerThread()
        {
            MainForm.Logger("Miner thread for Device #" + DeviceIndex + " started.");

            // Wait for the first job to arrive.
            int timePassed = 0;

            while ((mStratum == null || mStratum.CurrentJob == null) && timePassed < 5000)
            {
                Thread.Sleep(10);
                timePassed += 10;
            }
            if (mStratum == null || mStratum.CurrentJob == null)
            {
                MainForm.Logger("Stratum server failed to send a new job.");
                //throw new TimeoutException("Stratum server failed to send a new job.");
                return;
            }

            System.Diagnostics.Stopwatch consoleUpdateStopwatch = new System.Diagnostics.Stopwatch();
            EthashStratum.Work           work;
            int  epoch   = -1;
            long DAGSize = 0;
            ComputeBuffer <byte>   DAGBuffer    = null;
            ComputeBuffer <UInt32> outputBuffer = new ComputeBuffer <UInt32>(Context, ComputeMemoryFlags.ReadWrite, 256);
            ComputeBuffer <byte>   headerBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, 32);

            UInt32[] output = new UInt32[256];
            Random   r      = new Random();

            while (!Stopped && (work = mStratum.GetWork()) != null)
            {
                String poolExtranonce      = mStratum.PoolExtranonce;
                byte[] extranonceByteArray = Utilities.StringToByteArray(poolExtranonce);
                byte   localExtranonce     = work.LocalExtranonce;
                UInt64 startNonce          = (UInt64)localExtranonce << (8 * (7 - extranonceByteArray.Length));
                for (int i = 0; i < extranonceByteArray.Length; ++i)
                {
                    startNonce |= (UInt64)extranonceByteArray[i] << (8 * (7 - i));
                }
                startNonce += (ulong)r.Next(0, int.MaxValue) & (0xfffffffffffffffful >> (extranonceByteArray.Length * 8 + 16));
                String jobID      = work.GetJob().ID;
                String headerhash = work.GetJob().Headerhash;
                String seedhash   = work.GetJob().Seedhash;
                double difficulty = mStratum.Difficulty;

                fixed(byte *p = Utilities.StringToByteArray(headerhash))
                Queue.Write <byte>(headerBuffer, true, 0, 32, (IntPtr)p, null);

                if (epoch != work.GetJob().Epoch)
                {
                    if (DAGBuffer != null)
                    {
                        DAGBuffer.Dispose();
                        DAGBuffer = null;
                    }
                    epoch = work.GetJob().Epoch;
                    DAGCache cache = new DAGCache(epoch, work.GetJob().Seedhash);
                    DAGSize = Utilities.GetDAGSize(epoch);

                    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                    sw.Start();
                    fixed(byte *p = cache.GetData())
                    {
                        long globalWorkSize = DAGSize / 64;

                        globalWorkSize /= 8;
                        if (globalWorkSize % mLocalWorkSize > 0)
                        {
                            globalWorkSize += mLocalWorkSize - globalWorkSize % mLocalWorkSize;
                        }

                        ComputeBuffer <byte> DAGCacheBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, cache.GetData().Length, (IntPtr)p);

                        DAGBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadWrite, globalWorkSize * 8 * 64 /* DAGSize */); // With this, we can remove a conditional statement in the DAG kernel.

                        mDAGKernel.SetValueArgument <UInt32>(0, 0);
                        mDAGKernel.SetMemoryArgument(1, DAGCacheBuffer);
                        mDAGKernel.SetMemoryArgument(2, DAGBuffer);
                        mDAGKernel.SetValueArgument <UInt32>(3, (UInt32)cache.GetData().Length / 64);
                        //mDAGKernel.SetValueArgument<UInt32>(4, (UInt32)(DAGSize / 64));
                        mDAGKernel.SetValueArgument <UInt32>(4, 0xffffffffu);

                        for (long start = 0; start < DAGSize / 64; start += globalWorkSize)
                        {
                            Queue.Execute(mDAGKernel, new long[] { start }, new long[] { globalWorkSize }, new long[] { mLocalWorkSize }, null);
                            Queue.Finish();
                        }
                        DAGCacheBuffer.Dispose();
                    }

                    sw.Stop();
                    MainForm.Logger("Generated DAG for Epoch #" + epoch + " (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                }

                consoleUpdateStopwatch.Start();

                while (!Stopped && mStratum.CurrentJob.ID.Equals(jobID) && mStratum.PoolExtranonce.Equals(poolExtranonce))
                {
                    UInt64 target = (UInt64)((double)0xffff0000U / difficulty);
                    mSearchKernel.SetMemoryArgument(0, outputBuffer);                    // g_output
                    mSearchKernel.SetMemoryArgument(1, headerBuffer);                    // g_header
                    mSearchKernel.SetMemoryArgument(2, DAGBuffer);                       // _g_dag
                    mSearchKernel.SetValueArgument <UInt32>(3, (UInt32)(DAGSize / 128)); // DAG_SIZE
                    mSearchKernel.SetValueArgument <UInt64>(4, startNonce);              // start_nonce
                    mSearchKernel.SetValueArgument <UInt64>(5, target);                  // target
                    mSearchKernel.SetValueArgument <UInt32>(6, 0xffffffffu);             // isolate

                    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                    sw.Start();
                    fixed(UInt32 *p = output)
                    {
                        output[255] = 0; // output[255] is used as an atomic counter.
                        Queue.Write <UInt32>(outputBuffer, true, 0, 256, (IntPtr)p, null);
                        Queue.Execute(mSearchKernel, new long[] { 0 }, new long[] { mGlobalWorkSize }, new long[] { mLocalWorkSize }, null);
                        Queue.Read <UInt32>(outputBuffer, true, 0, 256, (IntPtr)p, null);
                    }

                    sw.Stop();
                    mSpeed = ((double)mGlobalWorkSize) / sw.Elapsed.TotalSeconds;
                    if (consoleUpdateStopwatch.ElapsedMilliseconds >= 10 * 1000)
                    {
                        MainForm.Logger("Device #" + DeviceIndex + ": " + String.Format("{0:N2} Mh/s", mSpeed / (1000000)));
                        consoleUpdateStopwatch.Restart();
                    }
                    for (int i = 0; i < output[255]; ++i)
                    {
                        mStratum.Submit(work.GetJob(), startNonce + (UInt64)output[i]);
                    }
                    startNonce += (UInt64)mGlobalWorkSize;
                }
            }

            headerBuffer.Dispose();
            outputBuffer.Dispose();
            DAGBuffer.Dispose();
            mSpeed = 0;
        }
        override unsafe protected void MinerThread()
        {
            ComputeProgram program = null;

            try {
                Random        r                = new Random();
                ComputeDevice computeDevice    = OpenCLDevice.GetComputeDevice();
                UInt32[]      ethashOutput     = new UInt32[256];
                byte[]        ethashHeaderhash = new byte[32];

                MarkAsAlive();

                MainForm.Logger("Miner thread for Device #" + DeviceIndex + " started.");

                program = BuildProgram("ethash", mEthashLocalWorkSizeArray[0], "-O1", "", "");

                MemoryUsage = 256 + 32;

                using (var searchKernel = program.CreateKernel("search"))
                    using (var DAGKernel = program.CreateKernel("GenerateDAG"))
                        using (var ethashOutputBuffer = new ComputeBuffer <UInt32>(Context, ComputeMemoryFlags.ReadWrite, 256))
                            using (var ethashHeaderBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, 32))
                                fixed(UInt32 *ethashOutputPtr = ethashOutput)
                                fixed(byte *ethashHeaderhashPtr = ethashHeaderhash)
                                while (!Stopped)
                                {
                                    ComputeBuffer <byte> ethashDAGBuffer = null;

                                    MarkAsAlive();

                                    try
                                    {
                                        int  ethashEpoch   = -1;
                                        long ethashDAGSize = 0;

                                        // Wait for the first job to arrive.
                                        int elapsedTime = 0;
                                        while ((Stratum == null || Stratum.GetJob() == null) && elapsedTime < Parameters.TimeoutForFirstJobInMilliseconds && !Stopped)
                                        {
                                            Thread.Sleep(100);
                                            elapsedTime += 100;
                                        }
                                        if (Stratum == null || Stratum.GetJob() == null)
                                        {
                                            MainForm.Logger("Ethash stratum server failed to send a new job.");
                                            //throw new TimeoutException("Stratum server failed to send a new job.");
                                            return;
                                        }

                                        System.Diagnostics.Stopwatch consoleUpdateStopwatch = new System.Diagnostics.Stopwatch();
                                        EthashStratum.Work           ethashWork;
                                        EthashStratum.Job            ethashJob;

                                        while (!Stopped && (ethashWork = Stratum.GetWork()) != null && (ethashJob = ethashWork.GetJob()) != null)
                                        {
                                            MarkAsAlive();

                                            String ethashPoolExtranonce      = Stratum.PoolExtranonce;
                                            byte[] ethashExtranonceByteArray = Utilities.StringToByteArray(ethashPoolExtranonce);
                                            byte   ethashLocalExtranonce     = (byte)ethashWork.LocalExtranonce;
                                            UInt64 ethashStartNonce          = (UInt64)ethashLocalExtranonce << (8 * (7 - ethashExtranonceByteArray.Length));
                                            for (int i = 0; i < ethashExtranonceByteArray.Length; ++i)
                                            {
                                                ethashStartNonce |= (UInt64)ethashExtranonceByteArray[i] << (8 * (7 - i));
                                            }
                                            ethashStartNonce += (ulong)r.Next(0, int.MaxValue) & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8));
                                            String ethashJobID      = ethashJob.ID;
                                            String ethashSeedhash   = ethashJob.Seedhash;
                                            double ethashDifficulty = Stratum.Difficulty;

                                            Buffer.BlockCopy(Utilities.StringToByteArray(ethashWork.GetJob().Headerhash), 0, ethashHeaderhash, 0, 32);
                                            Queue.Write <byte>(ethashHeaderBuffer, true, 0, 32, (IntPtr)ethashHeaderhashPtr, null);

                                            if (ethashEpoch != ethashWork.GetJob().Epoch)
                                            {
                                                if (ethashDAGBuffer != null)
                                                {
                                                    MemoryUsage -= ethashDAGBuffer.Size;
                                                    ethashDAGBuffer.Dispose();
                                                    ethashDAGBuffer = null;
                                                }
                                                ethashEpoch = ethashWork.GetJob().Epoch;
                                                DAGCache cache = new DAGCache(ethashEpoch, ethashWork.GetJob().Seedhash);
                                                ethashDAGSize = Utilities.GetDAGSize(ethashEpoch);

                                                System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                                                sw.Start();
                                                mEthashGlobalWorkSizeArray[0]  = ethashDAGSize / 64;
                                                mEthashGlobalWorkSizeArray[0] /= 8;
                                                if (mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0] > 0)
                                                {
                                                    mEthashGlobalWorkSizeArray[0] += mEthashLocalWorkSizeArray[0] - mEthashGlobalWorkSizeArray[0] % mEthashLocalWorkSizeArray[0];
                                                }

                                                ComputeBuffer <byte> DAGCacheBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadOnly, cache.GetData().Length);

                                                fixed(byte *p = cache.GetData())
                                                Queue.Write <byte>(DAGCacheBuffer, true, 0, cache.GetData().Length, (IntPtr)p, null);

                                                ethashDAGBuffer = new ComputeBuffer <byte>(Context, ComputeMemoryFlags.ReadWrite, mEthashGlobalWorkSizeArray[0] * 8 * 64 /* ethashDAGSize */); // With this, we can remove a conditional statement in the DAG kernel.
                                                MemoryUsage    += DAGCacheBuffer.Size + ethashDAGBuffer.Size;

                                                DAGKernel.SetValueArgument <UInt32>(0, 0);
                                                DAGKernel.SetMemoryArgument(1, DAGCacheBuffer);
                                                DAGKernel.SetMemoryArgument(2, ethashDAGBuffer);
                                                DAGKernel.SetValueArgument <UInt32>(3, (UInt32)cache.GetData().Length / 64);
                                                DAGKernel.SetValueArgument <UInt32>(4, 0xffffffffu);

                                                for (long start = 0; start < ethashDAGSize / 64; start += mEthashGlobalWorkSizeArray[0])
                                                {
                                                    mEthashGlobalWorkOffsetArray[0] = start;
                                                    Queue.Execute(DAGKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);
                                                    Queue.Finish();
                                                    if (Stopped || Stratum.GetJob() == null || !Stratum.GetJob().ID.Equals(ethashJobID))
                                                    {
                                                        break;
                                                    }
                                                }
                                                MemoryUsage -= DAGCacheBuffer.Size;
                                                DAGCacheBuffer.Dispose();
                                                if (Stopped || Stratum.GetJob() == null || !Stratum.GetJob().ID.Equals(ethashJobID))
                                                {
                                                    break;
                                                }
                                                sw.Stop();
                                                MainForm.Logger("Generated DAG for Epoch #" + ethashEpoch + " (" + (long)sw.Elapsed.TotalMilliseconds + "ms).");
                                            }

                                            consoleUpdateStopwatch.Start();

                                            while (!Stopped && Stratum.GetJob() != null && Stratum.GetJob().ID.Equals(ethashJobID) && Stratum.PoolExtranonce.Equals(ethashPoolExtranonce))
                                            {
                                                MarkAsAlive();

                                                // Get a new local extranonce if necessary.
                                                if ((ethashStartNonce & (0xfffffffffffffffful >> (ethashExtranonceByteArray.Length * 8 + 8)) + (ulong)mEthashGlobalWorkSizeArray[0]) >= ((ulong)0x1 << (64 - (ethashExtranonceByteArray.Length * 8 + 8))))
                                                {
                                                    break;
                                                }

                                                UInt64 target = (UInt64)((double)0xffff0000U / ethashDifficulty);
                                                searchKernel.SetMemoryArgument(0, ethashOutputBuffer);                    // g_output
                                                searchKernel.SetMemoryArgument(1, ethashHeaderBuffer);                    // g_header
                                                searchKernel.SetMemoryArgument(2, ethashDAGBuffer);                       // _g_dag
                                                searchKernel.SetValueArgument <UInt32>(3, (UInt32)(ethashDAGSize / 128)); // DAG_SIZE
                                                searchKernel.SetValueArgument <UInt64>(4, ethashStartNonce);              // start_nonce
                                                searchKernel.SetValueArgument <UInt64>(5, target);                        // target
                                                searchKernel.SetValueArgument <UInt32>(6, 0xffffffffu);                   // isolate

                                                System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                                                sw.Start();
                                                ethashOutput[255] = 0; // ethashOutput[255] is used as an atomic counter.
                                                Queue.Write <UInt32>(ethashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);
                                                mEthashGlobalWorkOffsetArray[0] = 0;
                                                Queue.Execute(searchKernel, mEthashGlobalWorkOffsetArray, mEthashGlobalWorkSizeArray, mEthashLocalWorkSizeArray, null);
                                                Queue.Read <UInt32>(ethashOutputBuffer, true, 0, 256, (IntPtr)ethashOutputPtr, null);
                                                if (Stratum.GetJob() != null && Stratum.GetJob().ID.Equals(ethashJobID))
                                                {
                                                    for (int i = 0; i < ethashOutput[255]; ++i)
                                                    {
                                                        Stratum.Submit(Device, ethashWork.GetJob(), ethashStartNonce + (UInt64)ethashOutput[i]);
                                                    }
                                                }
                                                ethashStartNonce += (UInt64)mEthashGlobalWorkSizeArray[0];

                                                sw.Stop();
                                                Speed = ((double)mEthashGlobalWorkSizeArray[0]) / sw.Elapsed.TotalSeconds;
                                                Device.TotalHashesPrimaryAlgorithm += (double)mEthashGlobalWorkSizeArray[0];
                                                if (consoleUpdateStopwatch.ElapsedMilliseconds >= 10 * 1000)
                                                {
                                                    MainForm.Logger("Device #" + DeviceIndex + " (Ethash): " + String.Format("{0:N2} Mh/s", Speed / (1000000)));
                                                    consoleUpdateStopwatch.Restart();
                                                }
                                            }
                                        }
                                    } catch (Exception ex) {
                                        MainForm.Logger("Exception in miner thread: " + ex.Message + ex.StackTrace);
                                        Speed = 0;
                                        if (UnrecoverableException.IsUnrecoverableException(ex))
                                        {
                                            this.UnrecoverableException = new UnrecoverableException(ex, Device);
                                            Stop();
                                        }
                                        else
                                        {
                                            MainForm.Logger("Restarting miner thread...");
                                            System.Threading.Thread.Sleep(Parameters.WaitTimeForRestartingMinerThreadInMilliseconds);
                                        }
                                    } finally {
                                        if (ethashDAGBuffer != null)
                                        {
                                            MemoryUsage -= ethashDAGBuffer.Size;
                                            ethashDAGBuffer.Dispose();
                                            ethashDAGBuffer = null;
                                        }
                                    }
                                }
            } catch (UnrecoverableException ex) {
                this.UnrecoverableException = ex;
            } catch (Exception ex) {
                this.UnrecoverableException = new UnrecoverableException(ex, Device);
            } finally {
                MarkAsDone();
                MemoryUsage = 0;
                if (program != null)
                {
                    program.Dispose();
                }
                program = null;
            }
        }