private async Task ShowDaemonSyncProgressAsync()
        {
            var infos = await daemon.ExecuteCmdAllAsync<JToken>(EC.GetSyncState);
            var firstValidResponse = infos.FirstOrDefault(x => x.Error == null && x.Response != null)?.Response;

            if (firstValidResponse != null)
            {
                // eth_syncing returns false if not synching
                if (firstValidResponse.Type == JTokenType.Boolean)
                    return;

                var syncStates = infos.Where(x => x.Error == null && x.Response != null && firstValidResponse.Type == JTokenType.Object)
                    .Select(x => x.Response.ToObject<SyncState>())
                    .ToArray();

                if (syncStates.Any())
                {
                    // get peer count
                    var response = await daemon.ExecuteCmdAllAsync<string>(EC.GetPeerCount);
                    var validResponses = response.Where(x => x.Error == null && x.Response != null).ToArray();
                    var peerCount = validResponses.Any() ? validResponses.Max(x => x.Response.IntegralFromHex<uint>()) : 0;

                    if (syncStates.Any(x => x.HighestBlock != 0))
                    {
                        var lowestHeight = syncStates.Min(x => x.CurrentBlock);
                        var totalBlocks = syncStates.Max(x => x.HighestBlock);
                        var percent = (double) lowestHeight / totalBlocks * 100;

                        logger.Info(() => $"[{LogCat}] Daemons have downloaded {percent:0.00}% of blockchain from {peerCount} peers");
                    }
                }
            }
        }
Beispiel #2
0
        protected virtual async Task ShowDaemonSyncProgressAsync()
        {
            if (hasLegacyDaemon)
            {
                await ShowDaemonSyncProgressLegacyAsync();

                return;
            }

            var infos = await daemon.ExecuteCmdAllAsync <BlockchainInfo>(BitcoinCommands.GetBlockchainInfo);

            if (infos.Length > 0)
            {
                var blockCount = infos
                                 .Max(x => x.Response?.Blocks);

                if (blockCount.HasValue)
                {
                    var peerInfo = await daemon.ExecuteCmdAnyAsync <PeerInfo[]>(BitcoinCommands.GetPeerInfo);

                    var peers = peerInfo.Response;

                    if (peers != null && peers.Length > 0)
                    {
                        var totalBlocks = peers.Max(x => x.StartingHeight);
                        var percent     = totalBlocks > 0 ? (double)blockCount / totalBlocks * 100 : 0;
                        logger.Info(() => $"[{LogCat}] Daemons have downloaded {percent:0.00}% of blockchain from {peers.Length} peers");
                    }
                }
            }
        }
Beispiel #3
0
        private async Task ShowDaemonSyncProgressAsync()
        {
            var infos = await daemon.ExecuteCmdAllAsync <Info>(BitcoinCommands.GetInfo);

            if (infos.Length > 0)
            {
                var blockCount = infos
                                 .Max(x => x.Response?.Blocks);

                if (blockCount.HasValue)
                {
                    // get list of peers and their highest block height to compare to ours
                    var peerInfo = await daemon.ExecuteCmdAnyAsync <PeerInfo[]>(BitcoinCommands.GetPeerInfo);

                    var peers = peerInfo.Response;

                    if (peers != null && peers.Length > 0)
                    {
                        var totalBlocks = peers.Max(x => x.StartingHeight);
                        var percent     = totalBlocks > 0 ? (double)blockCount / totalBlocks * 100 : 0;
                        logger.Info(() => $"[{LogCat}] Daemons have downloaded {percent:0.00}% of blockchain from {peers.Length} peers");
                    }
                }
            }
        }
        protected override async Task <bool> AreDaemonsHealthy()
        {
            // test daemons
            var responses = await daemon.ExecuteCmdAllAsync <GetInfoResponse>(MC.GetInfo);

            if (responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
                .Select(x => (DaemonClientException)x.Error.InnerException)
                .Any(x => x.Code == HttpStatusCode.Unauthorized))
            {
                logger.ThrowLogPoolStartupException($"Daemon reports invalid credentials", LogCat);
            }

            if (!responses.All(x => x.Error == null))
            {
                return(false);
            }

            // test wallet daemons
            var responses2 = await walletDaemon.ExecuteCmdAllAsync <object>(MWC.GetAddress);

            if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
                .Select(x => (DaemonClientException)x.Error.InnerException)
                .Any(x => x.Code == HttpStatusCode.Unauthorized))
            {
                logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials", LogCat);
            }

            return(responses2.All(x => x.Error == null));
        }
Beispiel #5
0
        protected override async Task <bool> AreDaemonsHealthyAsync()
        {
            // test daemons
            var responses = await daemon.ExecuteCmdAllAsync <GetInfoResponse>(logger, CryptonoteCommands.GetInfo);

            if (responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
                .Select(x => (DaemonClientException)x.Error.InnerException)
                .Any(x => x.Code == HttpStatusCode.Unauthorized))
            {
                logger.ThrowLogPoolStartupException($"Daemon reports invalid credentials");
            }

            if (!responses.All(x => x.Error == null))
            {
                return(false);
            }

            if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
            {
                // test wallet daemons
                var responses2 = await walletDaemon.ExecuteCmdAllAsync <object>(logger, CryptonoteWalletCommands.GetAddress);

                if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException))
                    .Select(x => (DaemonClientException)x.Error.InnerException)
                    .Any(x => x.Code == HttpStatusCode.Unauthorized))
                {
                    logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials");
                }

                return(responses2.All(x => x.Error == null));
            }

            return(true);
        }
Beispiel #6
0
        private async Task ShowDaemonSyncProgressAsync()
        {
            var infos = await daemon.ExecuteCmdAllAsync<GetInfoResponse>(MC.GetInfo);
            var firstValidResponse = infos.FirstOrDefault(x => x.Error == null && x.Response != null)?.Response;

            if (firstValidResponse != null)
            {
                var lowestHeight = infos.Where(x => x.Error == null && x.Response != null)
                    .Min(x => x.Response.Height);

                var totalBlocks = firstValidResponse.TargetHeight;
                var percent = (double) lowestHeight / totalBlocks * 100;

                logger.Info(() => $"[{LogCat}] Daemons have downloaded {percent:0.00}% of blockchain from {firstValidResponse.OutgoingConnectionsCount} peers");
            }
        }
Beispiel #7
0
        private async Task ShowDaemonSyncProgressAsync()
        {
            var responses = await daemon.ExecuteCmdAllAsync <object>(logger, EC.GetSyncState);

            var firstValidResponse = responses.FirstOrDefault(x => x.Error == null && x.Response != null)?.Response;

            if (firstValidResponse != null)
            {
                // eth_syncing returns false if not synching
                if (firstValidResponse is bool)
                {
                    return;
                }

                var syncStates = responses.Where(x => x.Error == null && x.Response != null && firstValidResponse is JObject)
                                 .Select(x => ((JObject)x.Response).ToObject <SyncState>())
                                 .ToArray();

                if (syncStates.Any())
                {
                    // get peer count
                    var response = await daemon.ExecuteCmdAllAsync <string>(logger, EC.GetPeerCount);

                    var validResponses = response.Where(x => x.Error == null && x.Response != null).ToArray();
                    var peerCount      = validResponses.Any() ? validResponses.Max(x => x.Response.IntegralFromHex <uint>()) : 0;

                    if (syncStates.Any(x => x.WarpChunksAmount != 0))
                    {
                        var warpChunkAmount    = syncStates.Min(x => x.WarpChunksAmount);
                        var warpChunkProcessed = syncStates.Max(x => x.WarpChunksProcessed);
                        var percent            = (double)warpChunkProcessed / warpChunkAmount * 100;

                        logger.Info(() => $"Daemons have downloaded {percent:0.00}% of warp-chunks from {peerCount} peers");
                    }

                    else if (syncStates.Any(x => x.HighestBlock != 0))
                    {
                        var lowestHeight = syncStates.Min(x => x.CurrentBlock);
                        var totalBlocks  = syncStates.Max(x => x.HighestBlock);
                        var percent      = (double)lowestHeight / totalBlocks * 100;

                        logger.Info(() => $"Daemons have downloaded {percent:0.00}% of blockchain from {peerCount} peers");
                    }
                }
            }
        }
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(blocks, nameof(blocks));

            var coin       = poolConfig.Template.As <EthereumCoinTemplate>();
            var pageSize   = 100;
            var pageCount  = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var blockCache = new Dictionary <long, DaemonResponses.Block>();
            var result     = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                // get a page full of blocks
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                // get latest block
                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(logger, EC.GetBlockByNumber, new[] { (object)"latest", true });

                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                // execute batch
                var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray());

                for (var j = 0; j < blockInfos.Length; j++)
                {
                    var blockInfo = blockInfos[j];
                    var block     = page[j];

                    // update progress
                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations);
                    result.Add(block);

                    messageBus.NotifyBlockConfirmationProgress(poolConfig.Id, block, coin);

                    // is it block mined by us?
                    if (string.Equals(blockInfo.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase))
                    {
                        // mature?
                        if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)
                        {
                            var blockHashResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(logger, EC.GetBlockByNumber, new[] { (object)block.BlockHeight.ToStringHexWithPrefix(), true });

                            var blockHash = blockHashResponses.First(x => x.Error == null && x.Response?.Hash != null).Response.Hash;

                            block.Hash   = blockHash;
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.BlockHeight          = (ulong)blockInfo.Height;
                            block.Reward = GetBaseBlockReward(chainType, block.BlockHeight); // base reward
                            block.Type   = "block";

                            if (extraConfig?.KeepUncles == false)
                            {
                                block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards
                            }
                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo); // tx fees
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");

                            messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                        }

                        continue;
                    }

                    // search for a block containing our block as an uncle by checking N blocks in either direction
                    var heightMin = block.BlockHeight - BlockSearchOffset;
                    var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight);
                    var range     = new List <long>();

                    for (var k = heightMin; k < heightMax; k++)
                    {
                        range.Add((long)k);
                    }

                    // execute batch
                    var blockInfo2s = await FetchBlocks(blockCache, range.ToArray());

                    foreach (var blockInfo2 in blockInfo2s)
                    {
                        // don't give up yet, there might be an uncle
                        if (blockInfo2.Uncles.Length > 0)
                        {
                            // fetch all uncles in a single RPC batch request
                            var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EC.GetUncleByBlockNumberAndIndex,
                                                                                                  new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() }))
                                             .ToArray();

                            logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}");

                            var uncleResponses = await daemon.ExecuteBatchAnyAsync(logger, uncleBatch);

                            logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}");

                            var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null)
                                        .Select(x => x.Response.ToObject <DaemonResponses.Block>())
                                        .FirstOrDefault(x => string.Equals(x.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase));

                            if (uncle != null)
                            {
                                // mature?
                                if (latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
                                {
                                    var blockHashUncleResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(logger, EC.GetBlockByNumber, new[] { (object)uncle.Height.Value.ToStringHexWithPrefix(), true });

                                    var blockHashUncle = blockHashUncleResponses.First(x => x.Error == null && x.Response?.Hash != null).Response.Hash;

                                    block.Hash   = blockHashUncle;
                                    block.Status = BlockStatus.Confirmed;
                                    block.ConfirmationProgress = 1;
                                    block.Reward      = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);
                                    block.BlockHeight = uncle.Height.Value;
                                    block.Type        = EthereumConstants.BlockTypeUncle;

                                    logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");

                                    messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                                }

                                else
                                {
                                    logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");
                                }

                                break;
                            }
                        }
                    }

                    if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75)
                    {
                        // we've lost this one
                        block.Hash   = "0x0";
                        block.Status = BlockStatus.Orphaned;
                        block.Reward = 0;

                        messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                    }
                }
            }

            return(result.ToArray());
        }
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(blocks, nameof(blocks));

            await DetectChainAsync();

            var pageSize  = 100;
            var pageCount = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var result    = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                // get a page full of blocks
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                // get latest block
                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(EC.GetBlockByNumber, new[] { (object)"latest", true });

                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                // build command batch (block.TransactionConfirmationData is the hash of the blocks coinbase transaction)
                var blockBatch = page.Select(block => new DaemonCmd(EC.GetBlockByNumber,
                                                                    new[]
                {
                    (object)block.BlockHeight.ToStringHexWithPrefix(),
                    true
                })).ToArray();

                // execute batch
                var responses = await daemon.ExecuteBatchAnyAsync(blockBatch);

                for (var j = 0; j < responses.Length; j++)
                {
                    var blockResponse = responses[j];

                    var blockInfo = blockResponse.Response?.ToObject <DaemonResponses.Block>();
                    var block     = page[j];

                    // extract confirmation data from stored block
                    var mixHash = block.TransactionConfirmationData.Split(":").First();
                    var nonce   = block.TransactionConfirmationData.Split(":").Last();

                    // check error
                    if (blockResponse.Error != null)
                    {
                        logger.Warn(() => $"[{LogCategory}] Daemon reports error '{blockResponse.Error.Message}' (Code {blockResponse.Error.Code}) for block {page[j].BlockHeight}. Will retry.");
                        continue;
                    }

                    // missing details with no error
                    if (blockInfo == null)
                    {
                        logger.Warn(() => $"[{LogCategory}] Daemon returned null result for block {page[j].BlockHeight}. Will retry.");
                        continue;
                    }

                    // update progress
                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations);
                    result.Add(block);

                    // is it block mined by us?
                    if (blockInfo.Miner == poolConfig.Address)
                    {
                        // additional check
                        // NOTE: removal of first character of both sealfields caused by
                        // https://github.com/paritytech/parity/issues/1090
                        var match = blockInfo.SealFields[0].Substring(4) == mixHash.Substring(2) &&
                                    blockInfo.SealFields[1].Substring(4) == nonce.Substring(2);

                        // mature?
                        if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)
                        {
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.Reward = GetBaseBlockReward(block.BlockHeight);         // base reward

                            if (extraConfig?.KeepUncles == false)
                            {
                                block.Reward += blockInfo.Uncles.Length * (block.Reward / 32);             // uncle rewards
                            }
                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo);             // tx fees
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");
                        }

                        continue;
                    }

                    // don't give up yet, there might be an uncle
                    if (blockInfo.Uncles.Length > 0)
                    {
                        // fetch all uncles in a single RPC batch request
                        var uncleBatch = blockInfo.Uncles.Select((x, index) => new DaemonCmd(EC.GetUncleByBlockNumberAndIndex,
                                                                                             new[] { blockInfo.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() }))
                                         .ToArray();

                        var uncleResponses = await daemon.ExecuteBatchAnyAsync(uncleBatch);

                        var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null)
                                    .Select(x => x.Response.ToObject <DaemonResponses.Block>())
                                    .FirstOrDefault(x => x.Miner == poolConfig.Address);

                        if (uncle != null)
                        {
                            // mature?
                            if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)
                            {
                                block.Status = BlockStatus.Confirmed;
                                block.ConfirmationProgress = 1;
                                block.Reward = GetUncleReward(uncle.Height.Value, block.BlockHeight);

                                logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {block.BlockHeight} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");
                            }

                            continue;
                        }
                    }

                    if (block.ConfirmationProgress > 0.75)
                    {
                        // we've lost this one
                        block.Status = BlockStatus.Orphaned;
                    }
                }
            }

            return(result.ToArray());
        }
Beispiel #10
0
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(blocks, nameof(blocks));

            var pageSize   = 100;
            var pageCount  = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var blockCache = new Dictionary <long, DaemonResponses.Block>();
            var result     = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                // get a page full of blocks
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                // get latest block
                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(AionCommands.GetBlockByNumber, new[] { (object)"latest", true });

                if (!latestBlockResponses.Any(x => x.Error == null && x.Response?.Height != null))
                {
                    break;
                }
                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                // execute batch
                var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray());

                for (var j = 0; j < blockInfos.Length; j++)
                {
                    var blockInfo = blockInfos[j];
                    var block     = page[j];

                    // extract confirmation data from stored block
                    var mixHash = block.TransactionConfirmationData.Split(":").First();
                    var nonce   = block.TransactionConfirmationData.Split(":").Last();

                    // update progress
                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / extraConfig.MinimumConfirmations);
                    result.Add(block);

                    // is it block mined by us?
                    if (blockInfo.Miner == poolConfig.Address)
                    {
                        // mature?
                        if (latestBlockHeight - block.BlockHeight >= (ulong)extraConfig.MinimumConfirmations)
                        {
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.Reward = AionUtils.calculateReward((long)block.BlockHeight);

                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo); // tx fees
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");
                        }

                        continue;
                    }

                    if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75)
                    {
                        // we've lost this one
                        block.Status = BlockStatus.Orphaned;
                        block.Reward = 0;
                    }
                }
            }

            return(result.ToArray());
        }
Beispiel #11
0
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(blocks, nameof(blocks));

            var coin       = poolConfig.Template.As <EthereumCoinTemplate>();
            var pageSize   = 100;
            var pageCount  = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var blockCache = new Dictionary <long, DaemonResponses.Block>();
            var result     = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                // get a page full of blocks
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                // get latest block
                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(logger, EthCommands.GetBlockByNumber, new[] { (object)"latest", true });

                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                // execute batch
                var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray());

                for (var j = 0; j < blockInfos.Length; j++)
                {
                    logger.Info(() => $"Blocks count to process : {blockInfos.Length - j}");
                    var blockInfo = blockInfos[j];
                    var block     = page[j];

                    // extract confirmation data from stored block
                    var mixHash = block.TransactionConfirmationData.Split(":").First();
                    var nonce   = block.TransactionConfirmationData.Split(":").Last();
                    logger.Debug(() => $"** TransactionData: {block.TransactionConfirmationData}");


                    // update progress
                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations);
                    result.Add(block);

                    messageBus.NotifyBlockConfirmationProgress(poolConfig.Id, block, coin);


                    if (string.Equals(blockInfo.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase))
                    {
                        // additional check
                        // NOTE: removal of first character of both sealfields caused by
                        // https://github.com/paritytech/parity/issues/1090
                        logger.Info(() => $"** Ethereum Deamon is Parity : {isParity}");

                        // is the block mined by us?
                        bool match = false;
                        if (isParity)
                        {
                            match = isParity ? true : blockInfo.SealFields[0].Substring(2) == mixHash && blockInfo.SealFields[1].Substring(2) == nonce;
                            logger.Debug(() => $"** Parity mixHash : {blockInfo.SealFields[0].Substring(2)} =?= {mixHash}");
                            logger.Debug(() => $"** Parity nonce   : {blockInfo.SealFields[1].Substring(2)} =?= {nonce}");
                        }
                        else
                        {
                            if (blockInfo.MixHash == mixHash && blockInfo.Nonce == nonce)
                            {
                                match = true;
                                logger.Debug(() => $"** Geth mixHash : {blockInfo.MixHash} =?= {mixHash}");
                                logger.Debug(() => $"** Geth nonce   : {blockInfo.Nonce} =?= {nonce}");
                                logger.Debug(() => $"** (MIXHASH_NONCE) Is the Block mined by us? {match}");
                            }
                            if (blockInfo.Miner == poolConfig.Address)
                            {
                                //match = true;
                                logger.Debug(() => $"Is the block mined by us? Yes if equal: {blockInfo.Miner} =?= {poolConfig.Address}");
                                logger.Debug(() => $"** (WALLET_MATCH) Is the Block mined by us? {match}");
                                logger.Debug(() => $"** Possible Uncle or Orphan block found");
                            }
                        }

                        // mature?
                        if (match && (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations))
                        {
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.BlockHeight          = (ulong)blockInfo.Height;
                            block.Reward = GetBaseBlockReward(chainType, block.BlockHeight); // base reward
                            block.Type   = "block";

                            if (extraConfig?.KeepUncles == false)
                            {
                                block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards
                            }
                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo); // tx fees
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");

                            messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                        }

                        continue;
                    }

                    // search for a block containing our block as an uncle by checking N blocks in either direction
                    var heightMin = block.BlockHeight - BlockSearchOffset;
                    var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight);
                    var range     = new List <long>();

                    for (var k = heightMin; k < heightMax; k++)
                    {
                        range.Add((long)k);
                    }

                    // execute batch
                    var blockInfo2s = await FetchBlocks(blockCache, range.ToArray());

                    foreach (var blockInfo2 in blockInfo2s)
                    {
                        // don't give up yet, there might be an uncle
                        if (blockInfo2.Uncles.Length > 0)
                        {
                            // fetch all uncles in a single RPC batch request
                            var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EthCommands.GetUncleByBlockNumberAndIndex, new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() })).ToArray();

                            logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}");

                            var uncleResponses = await daemon.ExecuteBatchAnyAsync(logger, uncleBatch);

                            logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}");

                            var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null)
                                        .Select(x => x.Response.ToObject <DaemonResponses.Block>())
                                        .FirstOrDefault(x => string.Equals(x.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase));

                            if (uncle != null)
                            {
                                // mature?
                                if (latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
                                {
                                    block.Status = BlockStatus.Confirmed;
                                    block.ConfirmationProgress = 1;
                                    block.Reward      = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);
                                    block.BlockHeight = uncle.Height.Value;
                                    block.Type        = EthereumConstants.BlockTypeUncle;

                                    logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");

                                    messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                                }

                                else
                                {
                                    logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");
                                }

                                break;
                            }
                        }
                    }

                    if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75)
                    {
                        // we've lost this one
                        block.Status = BlockStatus.Orphaned;
                        block.Reward = 0;

                        messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin);
                    }
                }
            }

            return(result.ToArray());
        }
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(blocks, nameof(blocks));

            await DetectChainAsync();

            var pageSize   = 100;
            var pageCount  = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var blockCache = new Dictionary <long, DaemonResponses.Block>();
            var result     = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                // get a page full of blocks
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                // get latest block
                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(EC.GetBlockByNumber, new[] { (object)"latest", true });

                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                // execute batch
                var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray());

                for (var j = 0; j < blockInfos.Length; j++)
                {
                    var blockInfo = blockInfos[j];
                    var block     = page[j];

                    // extract confirmation data from stored block
                    var mixHash = block.TransactionConfirmationData.Split(":").First();
                    var nonce   = block.TransactionConfirmationData.Split(":").Last();

                    // update progress
                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations);
                    result.Add(block);

                    // is it block mined by us?
                    if (blockInfo.Miner == poolConfig.Address)
                    {
                        // additional check
                        // NOTE: removal of first character of both sealfields caused by
                        // https://github.com/paritytech/parity/issues/1090
                        var match = blockInfo.SealFields[0].Substring(4) == mixHash.Substring(2) &&
                                    blockInfo.SealFields[1].Substring(4) == nonce.Substring(2);

                        // mature?
                        if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)
                        {
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.Reward = GetBaseBlockReward(block.BlockHeight); // base reward

                            if (extraConfig?.KeepUncles == false)
                            {
                                block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards
                            }
                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo); // tx fees
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");
                        }

                        continue;
                    }

                    // search for a block containing our block as an uncle by checking N blocks in either direction
                    var heightMin = block.BlockHeight - BlockSearchOffset;
                    var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight);
                    var range     = new List <long>();

                    for (var k = heightMin; k < heightMax; k++)
                    {
                        range.Add((long)k);
                    }

                    // execute batch
                    var blockInfo2s = await FetchBlocks(blockCache, range.ToArray());

                    foreach (var blockInfo2 in blockInfo2s)
                    {
                        // don't give up yet, there might be an uncle
                        if (blockInfo2.Uncles.Length > 0)
                        {
                            // fetch all uncles in a single RPC batch request
                            var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EC.GetUncleByBlockNumberAndIndex,
                                                                                                  new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() }))
                                             .ToArray();

                            logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}");

                            var uncleResponses = await daemon.ExecuteBatchAnyAsync(uncleBatch);

                            logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}");

                            var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null)
                                        .Select(x => x.Response.ToObject <DaemonResponses.Block>())
                                        .FirstOrDefault(x => x.Miner == poolConfig.Address);

                            if (uncle != null)
                            {
                                // mature?
                                if (latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
                                {
                                    block.Status = BlockStatus.Confirmed;
                                    block.ConfirmationProgress = 1;
                                    block.Reward      = GetUncleReward(uncle.Height.Value, blockInfo2.Height.Value);
                                    block.BlockHeight = uncle.Height.Value;
                                    block.Type        = EthereumConstants.BlockTypeUncle;

                                    logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");
                                }

                                else
                                {
                                    logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");
                                }

                                break;
                            }
                        }
                    }

                    if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75)
                    {
                        // we've lost this one
                        block.Status = BlockStatus.Orphaned;
                        block.Reward = 0;
                    }
                }
            }

            return(result.ToArray());
        }
Beispiel #13
0
        public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks)
        {
            Assertion.RequiresNonNull(poolConfig, nameof(poolConfig));
            Assertion.RequiresNonNull(blocks, nameof(blocks));

            var pageSize   = 100;
            var pageCount  = (int)Math.Ceiling(blocks.Length / (double)pageSize);
            var blockCache = new Dictionary <long, DaemonResponses.Block>();
            var result     = new List <Block>();

            for (var i = 0; i < pageCount; i++)
            {
                var page = blocks
                           .Skip(i * pageSize)
                           .Take(pageSize)
                           .ToArray();

                var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(EC.GetBlockByNumber, new[] { (object)"latest", true });

                var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value;

                var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray());

                for (var j = 0; j < blockInfos.Length; j++)
                {
                    var blockInfo = blockInfos[j];
                    var block     = page[j];

                    var mixHash = block.TransactionConfirmationData.Split(":").First();
                    var nonce   = block.TransactionConfirmationData.Split(":").Last();

                    block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations);
                    result.Add(block);

                    if (blockInfo.Miner == poolConfig.Address)
                    {
                        var match = blockInfo.SealFields[0].Substring(4) == mixHash.Substring(2) &&
                                    blockInfo.SealFields[1].Substring(4) == nonce.Substring(2);

                        if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)
                        {
                            block.Status = BlockStatus.Confirmed;
                            block.ConfirmationProgress = 1;
                            block.Reward = GetBaseBlockReward(chainType, block.BlockHeight);
                            if (extraConfig?.KeepUncles == false)
                            {
                                block.Reward += blockInfo.Uncles.Length * (block.Reward / 32);
                            }
                            if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0)
                            {
                                block.Reward += await GetTxRewardAsync(blockInfo);
                            }
                            logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}");
                        }

                        continue;
                    }

                    var heightMin = block.BlockHeight - BlockSearchOffset;
                    var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight);
                    var range     = new List <long>();

                    for (var k = heightMin; k < heightMax; k++)
                    {
                        range.Add((long)k);
                    }

                    var blockInfo2s = await FetchBlocks(blockCache, range.ToArray());

                    foreach (var blockInfo2 in blockInfo2s)
                    {
                        if (blockInfo2.Uncles.Length > 0)
                        {
                            var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EC.GetUncleByBlockNumberAndIndex,
                                                                                                  new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() }))
                                             .ToArray();

                            logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}");

                            var uncleResponses = await daemon.ExecuteBatchAnyAsync(uncleBatch);

                            logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}");

                            var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null)
                                        .Select(x => x.Response.ToObject <DaemonResponses.Block>())
                                        .FirstOrDefault(x => x.Miner == poolConfig.Address);

                            if (uncle != null)
                            {
                                if (latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations)
                                {
                                    block.Status = BlockStatus.Confirmed;
                                    block.ConfirmationProgress = 1;
                                    block.Reward      = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value);
                                    block.BlockHeight = uncle.Height.Value;
                                    block.Type        = EthereumConstants.BlockTypeUncle;

                                    logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}");
                                }

                                else
                                {
                                    logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again.");
                                }

                                break;
                            }
                        }
                    }

                    if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75)
                    {
                        block.Status = BlockStatus.Orphaned;
                        block.Reward = 0;
                    }
                }
            }

            return(result.ToArray());
        }