public static bool TryGetHeader(Height height, out ChainedBlock creationHeader) { creationHeader = null; try { if (_connectionParameters == null) { return(false); } creationHeader = HeaderChain.GetBlock(height); if (creationHeader == null) { return(false); } else { return(true); } } catch { return(false); } }
/// <summary> /// /// </summary> /// <param name="account">if null then default safe, if doesn't contain, then exception</param> /// <returns></returns> public IEnumerable <SafeHistoryRecord> GetSafeHistory(SafeAccount account = null) { AssertAccount(account); var safeHistory = new HashSet <SafeHistoryRecord>(); var transactions = GetAllChainAndMemPoolTransactionsBySafeAccount(account); var scriptPubKeys = GetTrackedScriptPubKeysBySafeAccount(account); foreach (SmartTransaction transaction in transactions) { SafeHistoryRecord record = new SafeHistoryRecord(); record.TransactionId = transaction.GetHash(); record.BlockHeight = transaction.Height; // todo: the mempool could note when it seen the transaction the first time record.TimeStamp = !transaction.Confirmed ? DateTimeOffset.UtcNow : HeaderChain.GetBlock(transaction.Height).Header.BlockTime; record.Amount = Money.Zero; //for now // how much came to our scriptpubkeys foreach (var output in transaction.Transaction.Outputs) { if (scriptPubKeys.Contains(output.ScriptPubKey)) { record.Amount += output.Value; } } foreach (var input in transaction.Transaction.Inputs) { // do we have the input? SmartTransaction inputTransaction = transactions.FirstOrDefault(x => x.GetHash() == input.PrevOut.Hash); if (default(SmartTransaction) != inputTransaction) { // if yes then deduct from amount (bitcoin output cannot be partially spent) var prevOutput = inputTransaction.Transaction.Outputs[input.PrevOut.N]; if (scriptPubKeys.Contains(prevOutput.ScriptPubKey)) { record.Amount -= prevOutput.Value; } } // if no then whatever } safeHistory.Add(record); } return(safeHistory.ToList().OrderBy(x => x.TimeStamp)); }
private async Task BlockPullerJobAsync(CancellationToken ctsToken) { const int currTimeoutDownSec = 360; MemPoolJob.Enabled = false; while (true) { try { if (ctsToken.IsCancellationRequested) { return; } // the headerchain didn't catch up to the creationheight yet if (CreationHeight == Height.Unknown || HeaderChain.Height < CreationHeight) { State = WalletState.SyncingHeaders; await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } Height height; if (Tracker.BlockCount == 0) { height = CreationHeight; } else { int headerChainHeight = HeaderChain.Height; Height trackerBestHeight = Tracker.BestHeight; Height unprocessedBlockBestHeight = Tracker.UnprocessedBlockBuffer.BestHeight; // if no blocks to download (or process) start syncing mempool if (headerChainHeight <= trackerBestHeight) { State = MemPoolJob.SyncedOnce ? WalletState.Synced : WalletState.SyncingMemPool; MemPoolJob.Enabled = true; await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } // else sync blocks else { MemPoolJob.Enabled = false; State = WalletState.SyncingBlocks; // if unprocessed buffer hit the headerchain height // or unprocessed buffer is full // wait until they get processed if (( unprocessedBlockBestHeight.Type == HeightType.Chain && (headerChainHeight <= unprocessedBlockBestHeight) ) || Tracker.UnprocessedBlockBuffer.Full) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } // else figure out the next block's height to download else { int relevant = unprocessedBlockBestHeight.Type == HeightType.Chain ? unprocessedBlockBestHeight.Value : 0; // should not happen at this point, but better to check if (trackerBestHeight.Type != HeightType.Chain) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } height = new Height( Math.Max(trackerBestHeight.Value, relevant) + 1); } } } var chainedBlock = HeaderChain.GetBlock(height); BlockPuller.SetLocation(new ChainedBlock(chainedBlock.Previous.Header, chainedBlock.Previous.Height)); Block block = null; CancellationTokenSource ctsBlockDownload = CancellationTokenSource.CreateLinkedTokenSource( new CancellationTokenSource(TimeSpan.FromSeconds(currTimeoutDownSec)).Token, ctsToken); var blockDownloadTask = Task.Run(() => BlockPuller.NextBlock(ctsBlockDownload.Token)); block = await blockDownloadTask.ContinueWith(t => { if (ctsToken.IsCancellationRequested) { return(null); } if (t.IsCanceled || t.IsFaulted) { Nodes.Purge("no reason"); Debug.WriteLine( $"Purging nodes, reason: couldn't download block in {currTimeoutDownSec} seconds."); return(null); } return(t.Result); }).ConfigureAwait(false); if (ctsToken.IsCancellationRequested) { return; } if (blockDownloadTask.IsCanceled || blockDownloadTask.IsFaulted) { continue; } if (block == null) // then reorg happened { Reorg(); continue; } Tracker.AddOrReplaceBlock(new Height(chainedBlock.Height), block); } catch (Exception ex) { Debug.WriteLine($"Ignoring {nameof(BlockPullerJobAsync)} exception:"); Debug.WriteLine(ex); } } }
private async Task BlockDownloadingJobAsync(CancellationToken ctsToken) { MemPoolJob.Enabled = false; while (true) { try { if (ctsToken.IsCancellationRequested) { return; } // the headerchain didn't catch up to the creationheight yet if (Nodes.ConnectedNodes.Count < 3 || // at this condition it might catched up already, neverthless don't progress further CreationHeight == Height.Unknown || HeaderChain.Height < CreationHeight) { State = WalletState.SyncingHeaders; await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } Height height; if (Tracker.BlockCount == 0) { height = CreationHeight; } else { int headerChainHeight = HeaderChain.Height; Height trackerBestHeight = Tracker.BestHeight; Height unprocessedBlockBestHeight = Tracker.UnprocessedBlockBuffer.BestHeight; // if no blocks to download (or process) start syncing mempool if (headerChainHeight <= trackerBestHeight) { State = MemPoolJob.SyncedOnce ? WalletState.Synced : WalletState.SyncingMemPool; MemPoolJob.Enabled = true; await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } // else sync blocks else { MemPoolJob.Enabled = false; State = WalletState.SyncingBlocks; // if unprocessed buffer hit the headerchain height // or unprocessed buffer is full // wait until they get processed if (( unprocessedBlockBestHeight.Type == HeightType.Chain && (headerChainHeight <= unprocessedBlockBestHeight) ) || Tracker.UnprocessedBlockBuffer.Full) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } // else figure out the next block's height to download else { int relevant = unprocessedBlockBestHeight.Type == HeightType.Chain ? unprocessedBlockBestHeight.Value : 0; // should not happen at this point, but better to check if (trackerBestHeight.Type != HeightType.Chain) { await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false); continue; } height = new Height( Math.Max(trackerBestHeight.Value, relevant) + 1); } } } var blockFeature = new BlockFeature(height.Value); Task <GetBlockResponse> getBlockTask = _noTorQBitClient.GetBlock(blockFeature, headerOnly: false, extended: false); var getBlockRespone = await getBlockTask.ConfigureAwait(false); if (getBlockRespone == null) { // probably our local headerchain is in front of qbit server await Task.Delay(1000).ConfigureAwait(false); continue; } var block = getBlockRespone.Block; if (block == null) { // should not happen await Task.Delay(1000).ConfigureAwait(false); continue; } if (ctsToken.IsCancellationRequested) { return; } // if the hash of the downloaded block is not the same as the header's // if the proof of work and merkle root isn't valid if (HeaderChain.GetBlock(height).HashBlock != block.GetHash() || !block.Check()) { Reorg(); continue; } Tracker.AddOrReplaceBlock(height, block); } catch (Exception ex) { Debug.WriteLine($"Ignoring {nameof(BlockDownloadingJobAsync)} exception:"); Debug.WriteLine(ex); } } }