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); } }
public PowBlockCreator(ITimestamper timestamper, IBlockValidator validator, IBlockFinalizer blockFinalizer, ITransactionValidator transactionValidator) { _timestamper = timestamper ?? throw new ArgumentNullException(nameof(timestamper)); _validator = validator ?? throw new ArgumentNullException(nameof(validator)); _blockFinalizer = blockFinalizer ?? throw new ArgumentNullException(nameof(blockFinalizer)); _transactionValidator = transactionValidator ?? throw new ArgumentNullException(nameof(transactionValidator)); EventPublisher.GetInstance().OnValidatedBlockCreated += OnValidatedBlockCreated; }
public NetworkManager(NetworkNodesPool nodePool, ILoggerFactory loggerFactory, IBlockValidator blockValidator, IDifficultyCalculator difficultyCalculator, IBlockchainRepository repo, string netId) { _logger = loggerFactory.CreateLogger <NetworkManager>(); _nodePool = nodePool; _repo = repo; _netId = netId; _relayedTransactionHashes = new List <string>(); _relayedBlockHashes = new List <string>(); _messageHandler = new MessageHandler(this, ConcurrentTransactionPool.GetInstance(), nodePool, difficultyCalculator, blockValidator, loggerFactory, repo, netId); _handshakeMessageHandler = new HandshakeMessageHandler(this, nodePool, loggerFactory, repo, netId); _delays = new ConcurrentBag <int>(); EventPublisher.GetInstance().OnValidatedBlockCreated += OnValidatedBlockCreated; EventPublisher.GetInstance().OnValidTransactionReceived += OnValidTransactionReceived; // In the first phase, check every 10s if there is a node that has a longer chain than ours. // After the sync completed, exit the 'syncing' state and accept new blocks and transactions. StartSyncProcess(new CancellationTokenSource()); // todo cts }
public Miner(string netId, string minerWalletPubKey, string minerWalletPrivKey, IBlockchainRepository blockchainRepo, ITransactionRepository transactionRepo, ITransactionCreator transactionCreator, ITransactionValidator transactionValidator, IDifficultyCalculator difficultyCalculator, IPowBlockCreator blockCreator, IBlockValidator blockValidator, ConcurrentTransactionPool txPool, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger <Miner>(); _walletPubKey = minerWalletPubKey; _walletPrivKey = minerWalletPrivKey; _blockchainRepo = blockchainRepo; _networkIdentifier = netId; _blockchain = _blockchainRepo.GetChainByNetId(_networkIdentifier); _transactionRepo = transactionRepo; _transactionCreator = transactionCreator; _transactionValidator = transactionValidator; _difficultyCalculator = difficultyCalculator; _blockCreator = blockCreator; _blockValidator = blockValidator; _txPool = txPool; EventPublisher.GetInstance().OnUnvalidatedTransactionReceived += OnUnvalidatedTransactionReceived; EventPublisher.GetInstance().OnUnvalidatedBlockCreated += OnUnvalidatedBlockCreated; difficulty = _difficultyCalculator.CalculateCurrentDifficulty(_blockchain); }
internal void HandleCommand() { ulong transferFee = TransferSupplyFee; // From BlockchainConstants.cs IEnumerable <Sku> skuWithHistory = null; var blockHash = ""; var txIndex = 0; Console.WriteLine("Current transfer supply fee is " + TransferSupplyFee + " TK."); while (skuWithHistory == null) { WriteLineWithInputCursor("Enter the block hash where the SKU was created:"); blockHash = Console.ReadLine().ToUpper(); WriteLineWithInputCursor("Enter index of the transaction where the SKU resides:"); var txIndexInput = Console.ReadLine().ToLower(); Int32.TryParse(txIndexInput, out txIndex); try { if (blockHash == "LAST") { skuWithHistory = _skuRepository.GetAllWithHistory(_netId).Last(); blockHash = skuWithHistory.First().Block.Header.Hash; } else { skuWithHistory = _skuRepository.GetSkuWithHistory(blockHash, txIndex, _netId); } } catch (Exception) { Console.WriteLine("The block or transaction could not be found. Try again."); } } Console.WriteLine("Selected " + skuWithHistory.First().Data.SkuId); WriteLineWithInputCursor("Enter the sender's public key:"); var fromPub = Console.ReadLine(); var fromPriv = Program.GetPrivKey(fromPub); while (String.IsNullOrWhiteSpace(fromPriv)) { Console.WriteLine("Private key not found."); WriteLineWithInputCursor("Enter the sender's public key:"); fromPub = Console.ReadLine(); } var senderBalance = _transactionRepo.GetTokenBalanceForPubKey(fromPub, _netId); var skuSupply = _skuRepository.GetSupplyBalanceForPubKey(fromPub, skuWithHistory); Console.WriteLine("The sender's token balance: " + senderBalance); Console.WriteLine("The sender's supply: " + skuSupply); WriteLineWithInputCursor("Enter the receiver's public key:"); var toPub = Console.ReadLine(); // Todo support custom fees in transactionCreator /* * var askFeeFirstTime = true; * var forceHigherFee = false; * while (transferFee < TransferSupplyFee && !forceHigherFee || askFeeFirstTime) * { * askFeeFirstTime = false; * WriteLineWithInputCursor("Use a different fee ["+ TransferSupplyFee + "]:"); * var feeInput = Console.ReadLine().ToLower(); * while (!ulong.TryParse(feeInput, out transferFee)) * { * transferFee = TransferSupplyFee; * if (feeInput != "") * { * WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); * feeInput = Console.ReadLine().ToLower(); * } * else * { * break; * } * } * * if (transferFee > senderBalance && !forceHigherFee) * { * Console.WriteLine("The given fee is higher than the sender's token balance and can cause a rejection."); * WriteLineWithInputCursor("Type 'force' to use the given amount. Press ENTER to specify another amount."); * feeInput = Console.ReadLine().ToLower(); * if (feeInput == "force") * { * forceHigherFee = true; * } * } * } */ uint amount = 0; bool forceAmount = false; while (amount < 1 || amount > skuSupply && !forceAmount) { WriteLineWithInputCursor("Specify the amount to transfer:"); var amountInput = Console.ReadLine().ToLower(); while (!UInt32.TryParse(amountInput, out amount)) { WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); amountInput = Console.ReadLine().ToLower(); } if (amount > skuSupply && !forceAmount) { Console.WriteLine("The given amount is higher than the sender's current supply balance and can cause a rejection."); WriteLineWithInputCursor("Type 'force' to use the given amount. Press ENTER to specify another amount."); amountInput = Console.ReadLine().ToLower(); if (amountInput == "force") { forceAmount = true; } } } WriteLineWithInputCursor("Enter optional data []:"); var optionalData = Console.ReadLine(); AbstractTransaction transactionToSend = _transactionCreator.CreateSupplyTransferTransaction(fromPub, fromPriv, toPub, amount, blockHash, txIndex, optionalData); EventPublisher.GetInstance().PublishUnvalidatedTransactionReceived(this, new TransactionReceivedEventArgs(transactionToSend)); Console.Write("> "); }
private void MineForBlocks(CancellationToken cancellationToken) { _logger.LogInformation("We want to achieve a total of {0} seconds for each {1} blocks to be created.", (BlockchainConstants.SecondsPerBlockGoal * BlockchainConstants.DifficultyUpdateCycle), BlockchainConstants.DifficultyUpdateCycle); _logger.LogInformation("Mining for blocks.."); while (!cancellationToken.IsCancellationRequested) { // Every x blocks, recalculate the difficulty and save the blockchain. CheckForDifficultyUpdate(); _logger.LogDebug("Current height: {0}", _blockchain.CurrentHeight); // Calculate our current balance var allReceivedTransactions = _transactionRepo.GetAllReceivedByPublicKey(_walletPubKey, _networkIdentifier); ulong balance = _transactionRepo.GetTokenBalanceForPubKey(_walletPubKey, _networkIdentifier); _logger.LogDebug("Our balance: {0}", balance); // Create & add the coinbase transaction and then mine the block var coinbaseTx = _transactionCreator.CreateCoinBaseTransaction(_walletPubKey, _walletPrivKey, "Mined by Montapacking!"); var transactions = new List <AbstractTransaction>() { coinbaseTx }; lock (_txPool) { int transactionsIncludedInBlock = _txPool.Count(); transactions.AddRange(_txPool.GetTransactions(maxTransactionsPerBlock - 1)); _logger.LogDebug("Inserted {0} transactions from the txpool into this block", transactions.Count - 1); } try { if (difficulty < 1) { difficulty = 1; } var newBlock = _blockCreator.CreateValidBlockAndAddToChain(_walletPrivKey, _blockchain, transactions, difficulty, cancellationToken); lock (_txPool) { foreach (var transaction in newBlock.Transactions) { _txPool.RemoveTransaction(transaction); } } _logger.LogInformation("Created a new block!"); EventPublisher.GetInstance().PublishValidatedBlockCreated(this, new BlockCreatedEventArgs(newBlock)); } catch (OperationCanceledException) { _logger.LogInformation("Mining operation canceled."); } catch (BlockRejectedException ex) { _logger.LogDebug("Our own block does not pass validation: {0}", ex.Message); } catch (NonceLimitReachedException) { _logger.LogWarning("Nonce limit reached."); } } }
internal void HandleCommand(string netId) { uint tokenFee = 10; // From BlockchainConstants.cs Console.WriteLine("Current transfer token fee is " + tokenFee + " TK."); WriteLineWithInputCursor("Enter the sender's public key:"); var fromPub = Console.ReadLine(); var fromPriv = Program.GetPrivKey(fromPub); while (String.IsNullOrWhiteSpace(fromPriv)) { Console.WriteLine("Private key not found."); WriteLineWithInputCursor("Enter the sender's public key:"); fromPub = Console.ReadLine(); } var balance = _transactionRepo.GetTokenBalanceForPubKey(fromPub, netId); Console.WriteLine("The sender's balance: " + balance); WriteLineWithInputCursor("Enter the receiver's public key:"); var toPub = Console.ReadLine(); // Todo support custom fees in transactionCreator /* * var askFeeFirstTime = true; * var forceLowerFee = false; * while (tokenFee < 10 && !forceLowerFee || askFeeFirstTime) * { * askFeeFirstTime = false; * WriteLineWithInputCursor("Use a different fee [10]:"); * var feeInput = Console.ReadLine().ToLower(); * while (!UInt32.TryParse(feeInput, out tokenFee)) * { * tokenFee = 10; * if (feeInput != "") * { * WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); * feeInput = Console.ReadLine().ToLower(); * } * else * { * break; * } * } * * if (tokenFee < 10 && !forceLowerFee) * { * Console.WriteLine("This low fee might result into a rejection. "); * WriteLineWithInputCursor("Type 'force' to use the given fee. Press ENTER to specify another amount."); * feeInput = Console.ReadLine().ToLower(); * if (feeInput == "force") * { * forceLowerFee = true; * } * } * } */ uint amount = 0; bool forceAmount = false; while (amount < 1 || amount > balance && !forceAmount) { Console.WriteLine("Enter the amount to send:"); var amountInput = Console.ReadLine().ToLower(); while (!UInt32.TryParse(amountInput, out amount)) { WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); amountInput = Console.ReadLine().ToLower(); } if (amount + tokenFee > balance && !forceAmount) { Console.WriteLine("The given amount + fee is higher than the sender's balance and can cause a rejection."); WriteLineWithInputCursor("Type 'force' to use the given amount. Press ENTER to specify another amount."); amountInput = Console.ReadLine().ToLower(); if (amountInput == "force") { forceAmount = true; } } } WriteLineWithInputCursor("Enter optional data []:"); var optionalData = Console.ReadLine(); AbstractTransaction transactionToSend = _transactionCreator.CreateTokenTransferTransaction(fromPub, fromPriv, toPub, amount, optionalData); EventPublisher.GetInstance().PublishUnvalidatedTransactionReceived(this, new TransactionReceivedEventArgs(transactionToSend)); Console.Write("> "); }
internal void HandleCommand(string netId) { ulong creationFee = CreateSkuFee; // From BlockchainConstants.cs Console.WriteLine("Current SKU creation fee is " + creationFee + " TK."); WriteLineWithInputCursor("Enter the sender's public key:"); var fromPub = Console.ReadLine(); var fromPriv = Program.GetPrivKey(fromPub); while (String.IsNullOrWhiteSpace(fromPriv)) { Console.WriteLine("Private key not found."); WriteLineWithInputCursor("Enter the sender's public key:"); fromPub = Console.ReadLine(); } var balance = _transactionRepo.GetTokenBalanceForPubKey(fromPub, netId); Console.WriteLine("The sender's balance: " + balance); // Todo support custom fees in transactionCreator /* * var askFeeFirstTime = true; * var forceLowerFee = false; * while (creationFee < CreateSkuFee && !forceLowerFee || askFeeFirstTime) * { * askFeeFirstTime = false; * WriteLineWithInputCursor("Use a different fee ["+ CreateSkuFee + "]:"); * var feeInput = Console.ReadLine().ToLower(); * while (!ulong.TryParse(feeInput, out creationFee)) * { * creationFee = CreateSkuFee; * if (feeInput != "") * { * WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); * feeInput = Console.ReadLine().ToLower(); * } * else * { * break; * } * } * * if (creationFee < CreateSkuFee && !forceLowerFee) * { * Console.WriteLine("This low fee might result into a rejection. "); * WriteLineWithInputCursor("Type 'force' to use the given fee. Press ENTER to specify another amount."); * feeInput = Console.ReadLine().ToLower(); * if (feeInput == "force") * { * forceLowerFee = true; * } * } * } */ var sku = HandleCreateSkuCommand(); bool firstTimeSupplyAmountInput = true; uint supplyAmount = 0; while (supplyAmount < 0 || firstTimeSupplyAmountInput) { firstTimeSupplyAmountInput = false; WriteLineWithInputCursor("Specify the initial supply:"); var amountInput = Console.ReadLine().ToLower(); while (!UInt32.TryParse(amountInput, out supplyAmount)) { WriteLineWithInputCursor("Invalid value. Use a positive numeric value without decimals."); amountInput = Console.ReadLine().ToLower(); } } AbstractTransaction transactionToSend = _transactionCreator.CreateSkuCreationTransaction(fromPub, fromPriv, supplyAmount, sku); EventPublisher.GetInstance().PublishUnvalidatedTransactionReceived(this, new TransactionReceivedEventArgs(transactionToSend)); Console.Write("> "); }
public static void Main(string[] args) { CryptographyCommandhandler cryptographyCmdHandler = new CryptographyCommandhandler(new KeyGenerator()); cryptographyCmdHandler.HandleGenerateKeysCommand(out string walletPubKey, out string walletPrivKey); PushKeyPair(walletPubKey, walletPrivKey); Console.WriteLine("Your new public key: " + walletPubKey); Console.WriteLine("Your new private key: " + walletPrivKey); Console.WriteLine("Loading blockchain.."); var networkIdentifier = "testnet"; var services = SetupDI(networkIdentifier, walletPubKey, walletPrivKey); ushort listeningPort = NetworkConstants.DefaultListeningPort; IPAddress publicIP = IPAddress.Parse("127.0.0.1"); // Our public IP so other nodes can find us, todo if (args.Length > 1 && args[0] == "-port") { listeningPort = ushort.Parse(args[1]); } GetServices( services, out IBlockchainRepository blockchainRepo, out ITransactionRepository transactionRepo, out ITransactionCreator transactionCreator, out ITimestamper timestamper, out ISkuRepository skuRepository, out INetworkManager networkManager, out ILoggerFactory loggerFactory, out ISkuRepository skuRepo, out Miner miner ); _logger = loggerFactory.CreateLogger <Program>(); Blockchain blockchain = blockchainRepo.GetChainByNetId(networkIdentifier); // Command handlers, only large commands are handles by these separate handlers. AccountsCommandHandler accountsCmdHandler = new AccountsCommandHandler(transactionRepo, networkIdentifier); SkusCommandHandler skusCmdHandler = new SkusCommandHandler(blockchainRepo, timestamper, skuRepository, networkIdentifier); TransactionsCommandHandler transactionsCmdHandler = new TransactionsCommandHandler(transactionRepo, networkIdentifier); TransactionPoolCommandHandler txpoolCmdHandler = new TransactionPoolCommandHandler(); TransferTokensCommandHandler transferTokensCmdHandler = new TransferTokensCommandHandler(transactionRepo, transactionCreator); CreateSkuCommandHandler createSkuCmdHandler = new CreateSkuCommandHandler(transactionRepo, transactionCreator); TransferSupplyCommandHandler transferSupplyCmdHandler = new TransferSupplyCommandHandler(skuRepository, transactionRepo, transactionCreator, networkIdentifier); NetworkingCommandHandler networkingCmdHandler = new NetworkingCommandHandler(); TransactionGeneratorCommandHandler txGeneratorCmdHandler = new TransactionGeneratorCommandHandler(miner, transactionCreator, skuRepo, blockchainRepo); CreateSupplyCommandHandler createSupplyCmdHandler = new CreateSupplyCommandHandler(skuRepository, transactionRepo, transactionCreator, networkIdentifier); DestroySupplyCommandHandler destroySupplyCmdHandler = new DestroySupplyCommandHandler(skuRepository, transactionRepo, transactionCreator, networkIdentifier); _logger.LogInformation("Loaded blockchain. Current height: {Height}", blockchain.CurrentHeight == -1 ? "GENESIS" : blockchain.CurrentHeight.ToString()); networkManager.AcceptConnections(publicIP, listeningPort, new CancellationTokenSource()); networkManager.ConnectToPeer(new NetworkNode(ConnectionType.Outbound, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345))); PrintConsoleCommands(); var skuTransactions = 0; var txpool = ConcurrentTransactionPool.GetInstance(); EventPublisher.GetInstance().OnValidTransactionReceived += (object sender, TransactionReceivedEventArgs txargs) => { if (txargs.Transaction.Action == TransactionAction.CreateSku.ToString()) { skuTransactions++; } if (skuTransactions > 200000 && txpool.Count() < 1) { miner.StopMining(true); txGeneratorCmdHandler.HandleStopCommand(); } }; var input = ""; while (input != "exit") { input = Console.ReadLine().ToLower(); switch (input) { case "help": PrintConsoleCommands(); break; case "transactiongenerator startandmine": txGeneratorCmdHandler.HandleStartCommand(true); break; case "transactiongenerator start": txGeneratorCmdHandler.HandleStartCommand(false); break; case "transactiongenerator stop": txGeneratorCmdHandler.HandleStopCommand(); break; case "generatekeys": cryptographyCmdHandler.HandleGenerateKeysCommand(out walletPubKey, out walletPrivKey); PushKeyPair(walletPubKey, walletPrivKey); Console.WriteLine("Your new public key: " + walletPubKey); Console.WriteLine("Your new private key: " + walletPrivKey); Console.Write("> "); break; case "accounts": case "users": case "balances": accountsCmdHandler.HandleCommand(); break; case "skus": skusCmdHandler.HandleCommand(); break; case "txpool": case "transactionpool": case "pendingtransactions": txpoolCmdHandler.HandleCommand(miner.TransactionPool); break; case "transactions": transactionsCmdHandler.HandleCommand(); break; case "startmining": miner.StartMining(); Console.Write("> "); break; case "stopmining": miner.StopMining(true); PrintConsoleCommands(); break; case "resetblockchain": miner.StopMining(false); blockchainRepo.Delete(networkIdentifier); Console.WriteLine("Blockchain deleted."); blockchain = blockchainRepo.GetChainByNetId(networkIdentifier); networkManager.Dispose(); _logger.LogWarning("All network connections shut down."); // Initialize all variables again because the heap references changed. services = SetupDI(networkIdentifier, walletPubKey, walletPrivKey); GetServices( services, out blockchainRepo, out transactionRepo, out transactionCreator, out timestamper, out skuRepository, out networkManager, out var ingored, out skuRepo, out miner ); networkManager.AcceptConnections(publicIP, listeningPort, new CancellationTokenSource()); accountsCmdHandler = new AccountsCommandHandler(transactionRepo, networkIdentifier); skusCmdHandler = new SkusCommandHandler(blockchainRepo, timestamper, skuRepository, networkIdentifier); transactionsCmdHandler = new TransactionsCommandHandler(transactionRepo, networkIdentifier); txpoolCmdHandler = new TransactionPoolCommandHandler(); transferTokensCmdHandler = new TransferTokensCommandHandler(transactionRepo, transactionCreator); createSkuCmdHandler = new CreateSkuCommandHandler(transactionRepo, transactionCreator); txGeneratorCmdHandler = new TransactionGeneratorCommandHandler(miner, transactionCreator, skuRepo, blockchainRepo); _logger.LogInformation("Loaded blockchain. Current height: {Height}", blockchain.CurrentHeight == -1 ? "GENESIS" : blockchain.CurrentHeight.ToString()); Console.Write("> "); break; case "transfertokens": transferTokensCmdHandler.HandleCommand(networkIdentifier); break; case "createsku": createSkuCmdHandler.HandleCommand(networkIdentifier); break; case "transfersupply": transferSupplyCmdHandler.HandleCommand(); break; case "createsupply": createSupplyCmdHandler.HandleCommand(); break; case "destroysupply": destroySupplyCmdHandler.HandleCommand(); break; case "networking setport": listeningPort = networkingCmdHandler.HandleSetPortCommand(listeningPort); break; case "networking setaddress": publicIP = networkingCmdHandler.HandleSetAddressCommand(publicIP); break; case "networking connect": networkingCmdHandler.HandleConnectCommand(networkManager); break; case "networking disconnect": networkingCmdHandler.HandleDisconnectCommand(networkManager); break; case "networking pool": networkingCmdHandler.HandleListPoolCommand(NetworkNodesPool.GetInstance(loggerFactory)); break; case "networking stop": networkManager.Dispose(); break; case "networking start": if (networkManager.IsDisposed) { networkManager = GetService <INetworkManager>(services); } networkManager.AcceptConnections(publicIP, listeningPort, new CancellationTokenSource()); break; case "networking restart": networkManager.Dispose(); Thread.Sleep(1000); networkManager = GetService <INetworkManager>(services); networkManager.AcceptConnections(publicIP, listeningPort, new CancellationTokenSource()); break; default: Console.WriteLine("I don't recognize that command."); Console.Write("> "); break; } } }
// 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(); } }