public void AddInitTransactions()
        {
            state.OutputLog.Add($"{DateTime.Now} - Adding mined transactions..");
            if (!state.Balances.ContainsKey(state.Username))
            {
                state.Balances.Add(state.Username, 0);
            }
            int maxTxNumber = state.Transactions.Count == 0 ? 1 : state.Transactions.Last().Number + 1;

            for (int i = 0; i < 10; i++)
            {
                System.DateTime dtDateTime  = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
                Transaction     transaction = new Transaction(maxTxNumber, "00", state.Username, (int)(DateTime.UtcNow - dtDateTime).TotalSeconds);
                state.Transactions.Add(transaction);
                state.Balances[state.Username] += 1;
                maxTxNumber++;

                foreach (var node in seedNodes)
                {
                    byte[] message = MessageProcessor.ProcessMessage(BlockchainCommands.NEW_TRANS,
                                                                     new string[] { transaction.Number.ToString(), transaction.From,
                                                                                    transaction.To, transaction.Timestamp.ToString() });
                    NodeBroadcasting.BroadcastToSeedNode(message, node);
                }
            }
            state.OutputLog.Add($"{DateTime.Now} - Mined transactions added.");
        }
        public static State PerformSyncing(State state, UdpClient udpServer, int sourcePort, List <NodeDetails> seedNodes)
        {
            state.OutputLog.Add($"{DateTime.Now} - Syncing..");
            int         highestTransaction     = 1;
            NodeDetails highestTransactionNode = null;

            string[] messageParams = new string[] { };
            byte[]   message       = MessageProcessor.ProcessMessage(BlockchainCommands.HIGHEST_TRN, messageParams);
            foreach (var node in seedNodes)
            {
                NodeBroadcasting.BroadcastToSeedNode(message, node);
                var command = SyncingOperations.ReceiveMessage(state, udpServer, node, BlockchainCommands.HIGHEST_TRN_RES, sourcePort);
                if (command.CommandType != BlockchainCommands.NO_RESPONSE)
                {
                    var highestTransactionResultCommand = (HighestTransactionResultCommand)command;
                    if (highestTransaction < highestTransactionResultCommand.TransactionNumber)
                    {
                        state.OutputLog.Add($"{DateTime.Now} - Highest Transaction Updated.");
                        highestTransaction     = highestTransactionResultCommand.TransactionNumber;
                        highestTransactionNode = node;
                    }
                }
            }

            int currentTxNumber = state.Transactions.Count == 0 ? 1 : state.Transactions.Max(p => p.Number);

            if (currentTxNumber < highestTransaction)
            {
                UpdateState(udpServer, state, highestTransactionNode, sourcePort, highestTransaction, currentTxNumber);
                state.Transactions.OrderBy(p => p.Number);
            }

            state.OutputLog.Add($"{DateTime.Now} - Syncing Finished.");
            return(state);
        }
        public static State ProcessNewTransactionReceived(State state, Command command, NodeDetails node)
        {
            var newTransactionCommand = (NewTransactionCommand)command;
            var existingTransaction   = state.Transactions.FirstOrDefault(p => p.Number == newTransactionCommand.TransactionNumber);

            if (existingTransaction == null)
            {
                state.Transactions.Add(new Transaction(newTransactionCommand.TransactionNumber, newTransactionCommand.FromUser,
                                                       newTransactionCommand.ToUser, newTransactionCommand.Timestamp));
            }
            else
            {
                int index = state.Transactions.FindIndex(p => p.Number == newTransactionCommand.TransactionNumber);
                if (existingTransaction.Timestamp > newTransactionCommand.Timestamp)
                {
                    //Replace transaction
                    state.Balances[state.Transactions[index].To] -= 1;
                    state.Transactions[index] = new Transaction(newTransactionCommand.TransactionNumber, newTransactionCommand.FromUser,
                                                                newTransactionCommand.ToUser, newTransactionCommand.Timestamp);
                    if (!state.Balances.ContainsKey(newTransactionCommand.ToUser))
                    {
                        state.Balances.Add(newTransactionCommand.ToUser, 0);
                    }
                    state.Balances[newTransactionCommand.ToUser] += 1;
                }
            }

            NodeBroadcasting.SendOkMessage(node);
            return(state);
        }
        public static void ProcessHighestTransactionRequest(State state, Command command, NodeDetails node)
        {
            int transactionNumber = state.Transactions.Count == 0 ? 0 : state.Transactions.Max(p => p.Number);

            byte[] message = MessageProcessor.ProcessMessage(BlockchainCommands.HIGHEST_TRN_RES,
                                                             new string[] { transactionNumber.ToString() });
            NodeBroadcasting.BroadcastToSeedNode(message, node);
        }
        public static void ProcessGetTransactionRequest(State state, Command command, NodeDetails node)
        {
            var getTransactionCommand = (GetTransactionCommand)command;
            var transaction           = state.Transactions.FirstOrDefault(p => p.Number == getTransactionCommand.TransactionNumber);

            if (transaction == null)
            {
                NodeBroadcasting.SendNotOkMessage(node);
            }
            else
            {
                byte[] message = MessageProcessor.ProcessMessage(BlockchainCommands.NEW_TRANS, new string[]
                {
                    transaction.Number.ToString(), transaction.From, transaction.To, transaction.Timestamp.ToString()
                });

                NodeBroadcasting.BroadcastToSeedNode(message, node);
            }
        }
        private Transaction AddNewTransaction(string to)
        {
            int transactionNumber = state.Transactions.Max(p => p.Number) + 1;

            if (!state.Balances.ContainsKey(to))
            {
                Console.WriteLine($"User {to} does not exist.");
                return(null);
            }
            else if (state.Balances[state.Username] == 0)
            {
                Console.WriteLine($"Not enough balance for user {state.Username}.");
                return(null);
            }

            while (!state.NodeState.Equals(NodeState.AVAILABLE))
            {
            }

            state.NodeState = NodeState.SENDING;
            Transaction transaction = new Transaction(transactionNumber, state.Username, to, (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds);

            state.Transactions.Add(transaction);
            state.Balances[state.Username] -= 1;
            if (!state.Balances.ContainsKey(to))
            {
                state.Balances.Add(to, 0);
            }

            state.Balances[to] += 1;
            foreach (var node in seedNodes)
            {
                byte[] message = MessageProcessor.ProcessMessage(BlockchainCommands.NEW_TRANS,
                                                                 new string[] { transaction.Number.ToString(), transaction.From,
                                                                                transaction.To, transaction.Timestamp.ToString() });
                NodeBroadcasting.BroadcastToSeedNode(message, node);
            }

            state.NodeState = NodeState.AVAILABLE;
            return(transaction);
        }
        private static State UpdateState(UdpClient udpServer, State state, NodeDetails node, int sourcePort, int highestTransaction, int currentTransaction)
        {
            for (int i = currentTransaction; i <= highestTransaction; i++)
            {
                state.OutputLog.Add($"{DateTime.Now} - Getting Transaction {i}..");
                byte[] message = MessageProcessor.ProcessMessage(BlockchainCommands.GET_TRANS, new string[] { i.ToString() });
                int    retry   = 0;
                var    command = new NewTransactionCommand();
                while (retry < 5)
                {
                    NodeBroadcasting.BroadcastToSeedNode(message, node);
                    var newCommand = ReceiveNewTransaction(udpServer, node, i, sourcePort);
                    if (newCommand.CommandType.Equals(BlockchainCommands.NEW_TRANS))
                    {
                        command = (NewTransactionCommand)newCommand;
                        break;
                    }
                    retry++;
                }

                if (retry >= 5)
                {
                    state.OutputLog.Add($"{DateTime.Now} - Failed to get transaction {i}");
                    return(state);
                }
                state.Transactions.Add(new Transaction(command.TransactionNumber, command.FromUser, command.ToUser, command.Timestamp));
                if (!state.Balances.ContainsKey(command.ToUser))
                {
                    state.Balances.Add(command.ToUser, 0);
                }
                state.Balances[command.ToUser] += 1;

                if (state.Balances.ContainsKey(command.FromUser))
                {
                    state.Balances[command.FromUser] -= 1;
                }
            }

            return(state);
        }