public bool AddTransactionToPool(AbstractTransaction tx, bool publishToNetwork) { //_logger.LogInformation("Miner received transaction: {0}", JsonConvert.SerializeObject(tx)); try { if (_txPool.Contains(tx)) { throw new TransactionRejectedException("Transaction already submitted to txpool"); } _transactionValidator.ValidateTransaction(tx); _txPool.AddTransaction(tx); _logger.LogInformation("Added transaction to txpool ({0})", tx.Hash); if (publishToNetwork) // todo move this to ConcurrentTransactionPool maybe? { EventPublisher.GetInstance().PublishValidTransactionReceived(this, new TransactionReceivedEventArgs(tx)); } return(true); } catch (TransactionRejectedException e) { var errorTx = e.Transaction ?? tx; _logger.LogInformation("Transaction with hash {0} was rejected: {1}", errorTx.Hash, e.Message); return(false); } catch (Exception e) { _logger.LogInformation("An {0} occurred: {1}", e.GetType().Name, e.Message); return(false); } }
// todo Chain of Responsibility pattern, make XXMessageHandler class for each command type // and refactor abstractmessagehandler to a regular MessageHandlerHelper public override async Task HandleMessage(NetworkNode node, Message msg) { try { if (msg.Command == NetworkCommand.CloseConn.ToString()) { await node.Disconnect(); } else if (msg.Command == NetworkCommand.GetAddr.ToString()) { // Send our known peers var addresses = new AddrPayload(_nodePool.GetAllRemoteListenEndpoints()); await SendMessageToNode(node, NetworkCommand.Addr, addresses); } else if (msg.Command == NetworkCommand.Addr.ToString()) { // Connect to all neighbors that the other node knows var payload = (AddrPayload)msg.Payload; foreach (IPEndPoint endpoint in payload.Endpoints) { await _networkManager.ConnectToPeer(endpoint); } } else if (msg.Command == NetworkCommand.GetHeaders.ToString()) { // Send our headers var payload = (GetHeadersPayload)msg.Payload; try { var headers = GetBlocksFromHash(payload.HighestHeightHash, payload.StoppingHash, false).Select(b => b.Header); var headersPayload = new HeadersPayload(headers); await SendMessageToNode(node, NetworkCommand.Headers, headersPayload); } catch (KeyNotFoundException) { // Send empty payload await SendMessageToNode(node, NetworkCommand.NotFound, null); } } else if (msg.Command == NetworkCommand.GetBlocks.ToString()) { var payload = (GetBlocksPayload)msg.Payload; var blocksPayload = new StateBlocksPayload(); if (payload.Headers.Count() > 0) { blocksPayload = new StateBlocksPayload(GetBlocksFromHash(payload.Headers.First(), payload.Headers.Last(), true)); } await SendMessageToNode(node, NetworkCommand.Blocks, blocksPayload); } else if (msg.Command == NetworkCommand.Headers.ToString() && node.IsSyncingWithNode) { node.SetSyncStatus(SyncStatus.InProgress); var headersPayload = (HeadersPayload)msg.Payload; if (headersPayload.Headers.Count() == 0) { _logger.LogInformation("Successfully synced with remote node."); node.SetSyncStatus(SyncStatus.Succeeded); return; } // Request these blocks var getBlocksPayload = new GetBlocksPayload(headersPayload.Headers.Select(h => h.Hash)); await SendMessageToNode(node, NetworkCommand.GetBlocks, getBlocksPayload); } else if (msg.Command == NetworkCommand.NotFound.ToString() && node.IsSyncingWithNode) { node.SetSyncStatus(SyncStatus.Failed); // Restart the syncing process with another node. } else if (msg.Command == NetworkCommand.Blocks.ToString() && node.IsSyncingWithNode) { var blocksPayload = (StateBlocksPayload)msg.Payload; // Todo rewrite this code to support multithreaded 'Blocks' messages. Combine all gathered blocks // until the process has completed and all blocks are downloaded. Then, grab a block that points to the // end of our chain and add it to our chain. Repeat that process until all blocks have been added. var blocksProcessed = 0; lock (_blockchain) { while (blocksPayload.Blocks.Where(b => b.Header.PreviousHash == _blockchain.Blocks.Last().Header.Hash).Any()) { var blockToProcess = blocksPayload.Blocks.Where(b => b.Header.PreviousHash == _blockchain.Blocks.Last().Header.Hash).First(); if (_blockchain.CurrentHeight % 5 == 0 && _blockchain.CurrentHeight > 0) { _difficulty = _difficultyCalculator.CalculateCurrentDifficulty(_blockchain); // todo use CalculateCurrentDifficulty when testing is done } if (_difficulty < 1) { _difficulty = 1; } var currentTarget = BlockchainConstants.MaximumTarget / _difficulty; // todo refactor these 3 lines. They are copy-pasted from the miner. _blockValidator.ValidateBlock(blockToProcess, currentTarget, _blockchain, false, true); // Rethrow when we have a Block- / TransactionRejectedException. We don't want to keep a connection with bad nodes. blocksProcessed++; } } _logger.LogDebug("Downloaded and added {0} new blocks from remote node", blocksProcessed); _logger.LogDebug("Current height: {0}", _blockchain.CurrentHeight); if (blocksProcessed != blocksPayload.Blocks.Count()) { _logger.LogError("Added {0} new blocks from remote node, but expected {1}. Sync failed.", blocksProcessed, blocksPayload.Blocks.Count()); node.SetSyncStatus(SyncStatus.Failed); return; } _blockchainRepo.Update(_blockchain); // Block batch processed. Keep on ask for more headers. var getHeadersPayload = new GetHeadersPayload(_blockchain.Blocks.Last().Header.Hash); await SendMessageToNode(node, NetworkCommand.GetHeaders, getHeadersPayload); } else if (msg.Command == NetworkCommand.GetTxPool.ToString()) { var txPoolPayload = new StateTransactionsPayload(_txPool.GetAllTransactions()); await SendMessageToNode(node, NetworkCommand.TxPool, txPoolPayload); } else if (msg.Command == NetworkCommand.TxPool.ToString()) { var txPoolPayload = (StateTransactionsPayload)msg.Payload; foreach (var tx in txPoolPayload.Transactions) { _txPool.AddTransaction(tx); } } else if (msg.Command == NetworkCommand.NewTransaction.ToString()) { var txPayload = (SingleStateTransactionPayload)msg.Payload; if (_networkManager.IsSyncing) { return; } var validationResult = EventPublisher.GetInstance().PublishUnvalidatedTransactionReceived(node, new TransactionReceivedEventArgs(txPayload.Transaction)); if (validationResult == false) { //await node.Disconnect(); // Dishonest node, buuh! } } else if (msg.Command == NetworkCommand.NewBlock.ToString()) { var blockPayload = (SingleStateBlockPayload)msg.Payload; if (_networkManager.IsSyncing) { return; } var validationResult = EventPublisher.GetInstance().PublishUnvalidatedBlockCreated(node, new BlockCreatedEventArgs(blockPayload.Block)); if (validationResult == false) { //await node.Disconnect(); // Dishonest node, buuh! } } } catch (Exception ex) { _logger.LogError("An {0} occurred during the process of handling a {1} message: {2}. Node will be disconnected.", ex.GetType().Name, msg.Command.ToString(), ex.Message); node?.Disconnect(); } }