protected virtual void DataRecieved(object sender, string data) { Data.Add(data); if (!Processing && LookForBlock()) { Processing = true; NewBlock?.Invoke(this, ProcessBlock()); Processing = false; } }
public void Synchronize() { Interlocked.Exchange(ref _running, 1); Task.Run(async() => { try { var blockCount = await RpcClient.GetBlockCountAsync(); var isIIB = true; // Initial Index Building phase while (IsRunning) { try { // If stop was requested return. if (IsRunning == false) { return; } Height height = StartingHeight; uint256 prevHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index.Last(); height = lastIndex.BlockHeight + 1; prevHash = lastIndex.BlockHash; } } if (blockCount - height <= 100) { isIIB = false; } Block block = null; try { block = await RpcClient.GetBlockAsync(height); } catch (RPCException) // if the block didn't come yet { await Task.Delay(1000); continue; } if (blockCount - height <= 2) { NewBlock?.Invoke(this, block); } if (!(prevHash is null)) { // In case of reorg: if (prevHash != block.Header.HashPrevBlock && !isIIB) // There is no reorg in IIB { Logger.LogInfo <IndexBuilderService>($"REORG Invalid Block: {prevHash}"); // 1. Rollback index using (await IndexLock.LockAsync()) { Index.RemoveLast(); } // 2. Serialize Index. (Remove last line.) var lines = File.ReadAllLines(IndexFilePath); File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Rollback Bech32UtxoSet if (Bech32UtxoSetHistory.Count != 0) { Bech32UtxoSetHistory.Last().Rollback(Bech32UtxoSet); // The Bech32UtxoSet MUST be recovered to its previous state. Bech32UtxoSetHistory.RemoveLast(); // 4. Serialize Bech32UtxoSet. await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); } // 5. Skip the current block. continue; } } if (!isIIB) { if (Bech32UtxoSetHistory.Count >= 100) { Bech32UtxoSetHistory.RemoveFirst(); } Bech32UtxoSetHistory.Add(new ActionHistoryHelper()); } var scripts = new HashSet <Script>(); foreach (var tx in block.Transactions) { // If stop was requested return. // Because this tx iteration can take even minutes // It doesn't need to be accessed with a thread safe fasion with Interlocked through IsRunning, this may have some performance benefit if (_running != 1) { return; } for (int i = 0; i < tx.Outputs.Count; i++) { var output = tx.Outputs[i]; if (!output.ScriptPubKey.IsPayToScriptHash && output.ScriptPubKey.IsWitness) { var outpoint = new OutPoint(tx.GetHash(), i); Bech32UtxoSet.Add(outpoint, output.ScriptPubKey); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Add, outpoint, output.ScriptPubKey); } scripts.Add(output.ScriptPubKey); } } foreach (var input in tx.Inputs) { OutPoint prevOut = input.PrevOut; if (Bech32UtxoSet.TryGetValue(prevOut, out Script foundScript)) { Bech32UtxoSet.Remove(prevOut); if (!isIIB) { Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Remove, prevOut, foundScript); } scripts.Add(foundScript); } } } GolombRiceFilter filter = null; if (scripts.Count != 0) { filter = new GolombRiceFilterBuilder() .SetKey(block.GetHash()) .SetP(20) .SetM(1 << 20) .AddEntries(scripts.Select(x => x.ToCompressedBytes())) .Build(); } var filterModel = new FilterModel { BlockHash = block.GetHash(), BlockHeight = height, Filter = filter }; await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }); using (await IndexLock.LockAsync()) { Index.Add(filterModel); } if (File.Exists(Bech32UtxoSetFilePath)) { File.Delete(Bech32UtxoSetFilePath); } await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes()))); // If not close to the tip, just log debug. // Use height.Value instead of simply height, because it cannot be negative height. if (blockCount - height.Value <= 3 || height % 100 == 0) { Logger.LogInfo <IndexBuilderService>($"Created filter for block: {height}."); } else { Logger.LogDebug <IndexBuilderService>($"Created filter for block: {height}."); } } catch (Exception ex) { Logger.LogDebug <IndexBuilderService>(ex); } } } finally { if (IsStopping) { Interlocked.Exchange(ref _running, 3); } } }); }
protected virtual void OnNewBlock(NewBlockRequest newBlockRequest) { NewBlock?.Invoke(this, new NewBlockEventArgs(newBlockRequest.Block)); }
public 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); }
private void OnNewBlock(Block block) => NewBlock?.Invoke(this, block);