示例#1
0
        public void Connect()
        {
            if (Status == TrackerStatus.Disconnected)
            {
                try
                {
                    ConnectionInfo connInfo   = new ConnectionInfo(Address);
                    Connection     newTCPConn = TCPConnection.GetConnection(connInfo);

                    Status     = TrackerStatus.Connected;
                    Connection = newTCPConn;

                    if (!Connection.IncomingPacketHandlerExists(typeof(ToPeerMessage).Name))
                    {
                        Connection.AppendIncomingPacketHandler <ToPeerMessage>(typeof(ToPeerMessage).Name, OnToPeerMessage);
                    }

                    if (!Connection.IncomingPacketHandlerExists(typeof(PeerHashMessage).Name))
                    {
                        Connection.AppendIncomingPacketHandler <PeerHashMessage>(typeof(PeerHashMessage).Name, OnPeerHashMessageFromTracker);
                    }

                    if (!Connection.IncomingPacketHandlerExists(typeof(PeersMessage).Name))
                    {
                        Connection.AppendIncomingPacketHandler <PeersMessage>(typeof(PeersMessage).Name,
                                                                              (p, c, m) =>
                        {
                            OnPeersMessageFromTracker?.Invoke(this, new MessageEventArgs(m, null, Address));
                        });
                    }

                    if (!Connection.IncomingPacketHandlerExists(typeof(ConnectToPeerWithTrackerMessage).Name))
                    {
                        Connection.AppendIncomingPacketHandler <ConnectToPeerWithTrackerMessage>(typeof(ConnectToPeerWithTrackerMessage).Name,
                                                                                                 (p, c, m) =>
                        {
                            OnConnectToPeerWithTrackerMessage?.Invoke(this, new MessageEventArgs(m, null, Address));
                        });
                    }

                    if (!Connection.IncomingPacketHandlerExists(typeof(PeerDisconnectMessage).Name))
                    {
                        Connection.AppendIncomingPacketHandler <PeerDisconnectMessage>(typeof(PeerDisconnectMessage).Name,
                                                                                       (p, c, m) =>
                        {
                            OnDisconnectPeer?.Invoke(this, new MessageEventArgs(m, null, Address));
                        });
                    }

                    Thread.Sleep(CommonHelpers.MessagesInterval);
                }
                catch
                {
                    ErrorsCount++;
                    Status = TrackerStatus.Disconnected;

                    if (ErrorsCount >= 3)
                    {
                        allTrackers.Remove(this);

                        OnTrackerDelete?.Invoke(this, new EventArgs());
                    }
                }


                CommonHelpers.LogTrackers(allTrackers);
            }
        }
 public string CalcSignature()
 {
     return(CommonHelpers.SignData(Hash, VotingsUser.PrivateKey));
 }
        bool CheckStartVotingTransaction(Transaction transaction)
        {
            //проверка хеша и подписи
            if (!(transaction.CheckHash() && transaction.CheckSignature()))
            {
                return(false);
            }

            var prevVoting = db.GetTransaction(transaction.PreviousHash);

            //проверка номера голосования
            if (transaction.VotingNumber != (prevVoting.VotingNumber + 1))
            {
                return(false);
            }

            //проверка даты транзакции
            if (!(transaction.Date0 >= prevVoting.Date0 && transaction.Date0 <= CommonHelpers.GetTime()))
            {
                return(false);
            }

            //проверка, что транзакцию создал корневой клиент
            if (transaction.SenderHash != VotingsUser.RootPublicKey)
            {
                return(false);
            }


            var existsVoting = db.GetVoting(transaction.VotingNumber);

            //проверка существования копий транзакции
            if (existsVoting != null)
            {
                //если копия уже в блоке то выход
                if (existsVoting.Status == TransactionStatus.InBlock)
                {
                    return(false);
                }
                //если копия транзакции старее и при этом свободна, то выход
                else if (existsVoting.Date0 <= transaction.Date0 && existsVoting.Status == TransactionStatus.Free)
                {
                    return(false);
                }
                //иначе удаляем существующую транзакцию из базы
                else
                {
                    db.DeleteTransaction(existsVoting);
                    NewTransaction?.Invoke(this, new IntEventArgs(db.TransactionsCount()));
                }
            }


            try
            {
                var info = JObject.Parse(transaction.Info);
                if (!(info["name"].Value <string>() != ""))
                {
                    return(false);
                }

                if (info["candidates"].Count() == 0)
                {
                    return(false);
                }

                foreach (JToken item in info["candidates"])
                {
                    if (item.Value <string>() == "")
                    {
                        return(false);
                    }
                }
            }
            catch
            {
                return(false);
            }

            return(true);
        }
        bool CheckBanUserTransaction(Transaction transaction)
        {
            //проверка хеша и подписи
            if (!(transaction.CheckHash() && transaction.CheckSignature()))
            {
                return(false);
            }

            //проверка даты транзакции
            if (!(transaction.Date0 >= VotingsUser.RootUserDate && transaction.Date0 <= CommonHelpers.GetTime()))
            {
                return(false);
            }

            //проверка, что транзакцию создал корневой клиент
            if (transaction.SenderHash != VotingsUser.RootPublicKey)
            {
                return(false);
            }


            var recieverCreation = db.GetUserCreation(transaction.RecieverHash);

            //проверка на существование пользователя на заданную дату
            if (recieverCreation.Date0 > transaction.Date0)
            {
                return(false);
            }



            var prevTransaction = db.GetTransaction(transaction.PreviousHash);

            //предыдущая транзакция и транзакция создания пользователя - одно и то же
            if (prevTransaction.Hash != recieverCreation.Hash)
            {
                return(false);
            }
            //проверка чтобы предыдущая транзакция не была новее
            if (transaction.Date0 < prevTransaction.Date0)
            {
                return(false);
            }


            var existsBan = db.GetUserBan(transaction.RecieverHash);

            //проверка существования копий транзакции
            if (existsBan != null)
            {
                //если копия уже в блоке то выход
                if (existsBan.Status == TransactionStatus.InBlock)
                {
                    return(false);
                }
                //если копия транзакции старее и при этом свободна, то выход
                else if (existsBan.Date0 <= transaction.Date0 && existsBan.Status == TransactionStatus.Free)
                {
                    return(false);
                }
                //иначе удаляем существующую транзакцию из базы
                else
                {
                    db.DeleteTransaction(existsBan);
                    NewTransaction?.Invoke(this, new IntEventArgs(db.TransactionsCount()));
                }
            }



            try
            {
                var info = JObject.Parse(transaction.Info);
                if (!(info["cause"].Value <string>() != ""))
                {
                    return(false);
                }
            }
            catch
            {
                return(false);
            }

            return(true);
        }
        bool CheckVoteTransaction(Transaction transaction)
        {
            var voting = db.GetTransaction(transaction.PreviousHash);

            //проверка хеша и подписи
            if (!(transaction.CheckHash() && transaction.CheckSignature()))
            {
                return(false);
            }

            //проверка даты транзакции
            if (!(transaction.Date0 >= voting.Date0 && transaction.Date0 <= CommonHelpers.GetTime()))
            {
                return(false);
            }

            var senderCreation = db.GetUserCreation(transaction.SenderHash);
            var senderBan      = db.GetUserBan(transaction.SenderHash);

            ////проверка создателя транзакции
            //if (senderCreation == null) return false;
            //проверка даты создателя транзакции
            if (!(senderCreation.Date0 <= voting.Date0 &&
                  (senderBan == null || senderBan.Date0 > voting.Date0)))
            {
                return(false);
            }

            var recieverCreation = db.GetUserCreation(transaction.RecieverHash);
            var recieverBan      = db.GetUserBan(transaction.RecieverHash);

            ////проверка получателя транзакции
            //if (recieverCreation == null) return false;
            //проверка даты получателя транзакции
            if (!(recieverCreation.Date0 <= voting.Date0 &&
                  (recieverBan == null || recieverBan.Date0 > voting.Date0)))
            {
                return(false);
            }

            var existsVote = db.GetUserVote(transaction.SenderHash, transaction.VotingNumber);

            //проверка существования копий транзакции
            if (existsVote != null)
            {
                //если копия уже в блоке то выход
                if (existsVote.Status == TransactionStatus.InBlock)
                {
                    return(false);
                }
                //если копия транзакции старее и при этом свободна, то выход
                else if (existsVote.Date0 <= transaction.Date0 && existsVote.Status == TransactionStatus.Free)
                {
                    return(false);
                }
                //иначе удаляем существующую транзакцию из базы
                else
                {
                    db.DeleteTransaction(existsVote);
                    NewTransaction?.Invoke(this, new IntEventArgs(db.TransactionsCount()));
                }
            }

            return(true);
        }
        private bool CheckTransaction(Transaction transaction, EndPoint peerAddress = null)
        {
            if (db.GetTransaction(transaction.Hash) != null)
            {
                return(false);
            }

            bool needWait = false;


            //проверка существования транзакции создания пользователя
            //если ее нет, то транзакция создания и бана пользователя запрашиваются
            if (db.GetUserCreation(transaction.SenderHash) == null)
            {
                //если ее нет и в ожидающих - запрашиваем от пиров
                if (!pendingTransactions.Keys.Any(tr => tr.RecieverHash == transaction.PreviousHash))
                {
                    RequestTransaction(transaction.SenderHash, peerAddress);
                }
                needWait = true;
            }

            //проверяем получателя транзакции только для транзакций бана и посылки голоса
            if ((transaction.Type == TransactionType.BanUser || transaction.Type == TransactionType.Vote) &&
                (db.GetUserCreation(transaction.RecieverHash) == null))
            {
                if (!pendingTransactions.Keys.Any(tr => tr.RecieverHash == transaction.RecieverHash))
                {
                    RequestTransaction(transaction.RecieverHash, peerAddress);
                }
                needWait = true;
            }

            //проверка существования предыдущей транзакции
            if (db.GetTransaction(transaction.PreviousHash) == null)
            {
                if (!pendingTransactions.Keys.Any(tr => tr.Hash == transaction.PreviousHash))
                {
                    RequestTransaction(transaction.PreviousHash, peerAddress);
                }
                needWait = true;
            }


            //если транзакция не в списке ожидающих и запросили инфу, то отправляем ее в список ожидающих
            if (needWait == true)
            {
                if (!pendingTransactions.ContainsKey(transaction))
                {
                    pendingTransactions.Add(transaction, CommonHelpers.GetTime());
                }
                return(false);
            }


            ////проверка транзакции

            //удаляем из ожидающих
            pendingTransactions.Remove(transaction);

            var checkPassed = true;

            switch (transaction.Type)
            {
            case TransactionType.CreateUser:
                checkPassed = CheckCreateUserTransaction(transaction); break;

            case TransactionType.BanUser:
                checkPassed = CheckBanUserTransaction(transaction); break;

            case TransactionType.Vote:
                checkPassed = CheckVoteTransaction(transaction); break;

            case TransactionType.StartVoting:
                checkPassed = CheckStartVotingTransaction(transaction); break;

            default:
                checkPassed = false; break;
            }


            ////если транзакция проверена успешно
            if (checkPassed)
            {
                //добавляем транзакцию в базу
                db.PutTransaction(transaction);

                NetworkComms.Logger.Warn("Added transaction " + transaction.Type.ToString() + " " + transaction.Hash);

                NewTransaction?.Invoke(this, new IntEventArgs(db.TransactionsCount()));

                //если добавили новую транзакцию голосования, то вызываем событие
                if (transaction.Type == TransactionType.StartVoting)
                {
                    NewVoting?.Invoke(this, new IntEventArgs(transaction.VotingNumber));
                }
                else if (transaction.Type == TransactionType.CreateUser)
                {
                    NewUser?.Invoke(this, new IntEventArgs(db.UsersCount()));
                }

                //проверяем нужно ли создавать новый блок
                MakeBlock();

                //ищем в ожидающих транзакции связанные с этой и проверяем их
                var pending = pendingTransactions.Keys.Where(tr =>
                {
                    return(tr.PreviousHash == transaction.Hash ||
                           tr.SenderHash == transaction.RecieverHash ||
                           tr.RecieverHash == transaction.RecieverHash);
                }).ToList();
                foreach (var item in pending)
                {
                    CheckTransaction(item);
                }


                //ищем в ожидающих блоках связанные с этим и проверяем их
                var pending2 = pendingBlocks.Keys.Where(bl =>
                {
                    return(bl.TransactionsBlob.Contains(transaction.Hash) ||
                           bl.CreatorHash == transaction.RecieverHash);
                }).ToList();
                foreach (var item in pending2)
                {
                    CheckBlock(item);
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }
        private bool CheckBlock(Block block, EndPoint peerAddress = null)
        {
            if (db.GetBlock(block.Hash) != null)
            {
                return(false);
            }

            var prevBlock = db.GetBlock(block.PreviousHash);
            var creator   = db.GetUserCreation(block.CreatorHash);

            bool needWait = false;


            //проверка существования предыдущего блока
            if (prevBlock == null)
            {
                //если ее нет и в ожидающих - запрашиваем от пиров
                if (!pendingBlocks.Keys.Any(bl => bl.Hash == block.PreviousHash))
                {
                    RequestBlock(block.PreviousHash, peerAddress);
                }
                needWait = true;
            }


            //проверка существования создателя блока
            if (creator == null)
            {
                if (!pendingTransactions.Keys.Any(tr => tr.RecieverHash == block.CreatorHash))
                {
                    RequestTransaction(block.CreatorHash, peerAddress);
                }
                needWait = true;
            }

            if (block.Transactions == null)
            {
                return(false);
            }

            //проверка существования транзакций
            foreach (var itemHash in block.Transactions)
            {
                if (db.GetTransaction(itemHash) == null)
                {
                    if (!pendingTransactions.Keys.Any(tr => tr.Hash == itemHash))
                    {
                        RequestTransaction(itemHash, peerAddress);
                    }
                    needWait = true;
                }
            }


            //если блок не в списке ожидающих и запросили инфу, то отправляем ее в список ожидающих
            if (needWait == true)
            {
                if (!pendingBlocks.ContainsKey(block))
                {
                    pendingBlocks.Add(block, CommonHelpers.GetTime());
                }
                return(false);
            }


            ////если все данные есть, начинаем проверку блока

            //удаляем его из ожидающих
            pendingBlocks.Remove(block);


            //проверка хеша и подписи
            if (!(block.CheckHash() && block.CheckSignature()))
            {
                return(false);
            }


            //проверка даты
            if (!(block.Date0 >= prevBlock.Date0 && block.Date0 <= CommonHelpers.GetTime()))
            {
                return(false);
            }


            //проверка создателя блока
            //var senderCreation = db.GetUserCreation(block.CreatorHash);
            var senderBan = db.GetUserBan(block.CreatorHash);

            if (!(creator.Date0 <= block.Date0 &&
                  (senderBan == null || senderBan.Date0 > block.Date0)))
            {
                return(false);
            }



            //сравнение длины цепочки этого блока с длиной цепочки в базе
            var blockCopyInDB = db.GetBlock(block.Number);

            if (blockCopyInDB != null)
            {
                var lastBlockInChain = GetLastBlockFromPending(block);
                var lastBlockInDB    = db.GetLastBlock();

                //если цепочка нового блока длиннее или цепочки равны, но дата нового блока раньше
                if ((lastBlockInChain.Number > lastBlockInDB.Number) ||
                    (lastBlockInChain.Number == lastBlockInDB.Number && block.Date0 < blockCopyInDB.Date0))
                {
                    //удаляем из базы все блоки начиная с этого
                    for (int i = blockCopyInDB.Number; i <= lastBlockInDB.Number; i++)
                    {
                        var blockToRemove = db.GetBlock(i);
                        db.DeleteBlock(blockToRemove);

                        //СПОРНЫЙ МОМЕНТ
                        //отмечаем транзакции из блока как свободные
                        db.MarkAsFreePendingTransactions(true, blockToRemove.Transactions);
                    }

                    //добавляем блок обратно в ожидающие
                    pendingBlocks.Add(block, CommonHelpers.GetTime());
                    //запускаем проверку последнего ожидающего блока из цепочки (он снова загрузит нужные транзакции)
                    CheckBlock(lastBlockInChain);
                }
                else
                {
                    //СПОРНЫЙ МОМЕНТ
                    //если не приняли новый блок, то освобождаем полученные для него транзакции
                    db.MarkAsFreePendingTransactions(false, block.Transactions);
                }


                NewTransaction?.Invoke(this, new IntEventArgs(db.TransactionsCount()));
                NewBlock?.Invoke(this, new IntEventArgs(db.BlocksCount()));

                return(false);
            }


            //проверка транзакций
            foreach (var itemHash in block.Transactions)
            {
                var tr = db.GetTransaction(itemHash);
                if (tr.Status == TransactionStatus.InBlock || tr.Date0 > block.Date0)
                {
                    return(false);
                }
            }



            ////если все хорошо, добавляем блок в базу
            db.PutBlock(block);

            NetworkComms.Logger.Warn("Added block " + block.Hash);

            NewBlock?.Invoke(this, new IntEventArgs(db.BlocksCount()));

            //помечаем транзакции, что они в блоке
            foreach (var itemHash in block.Transactions)
            {
                var tr = db.GetTransaction(itemHash);
                db.MarkTransaction(tr, TransactionStatus.InBlock);
            }

            //ищем в ожидающих блоках связанные с этим и проверяем их
            var pending = pendingBlocks.Keys.Where(bl => bl.PreviousHash == block.Hash).ToList();

            foreach (var item in pending)
            {
                CheckBlock(item);
            }


            return(true);
        }