public void ReorgOne() { // remove the last block PartialBlock pb; if (Chain.Count != 0) { Chain.TryRemove(BestHeight, out pb); if (pb.Transactions.Count != 0) { // set the transactions to unconfirmed foreach (var txId in pb.Transactions.Select(x => x.GetHash())) { TrackedTransactions.AddOrReplace(txId, -1); } } } // remove the last block from the buffer too Block b; if (FullBlockBuffer.Count() != 0) { FullBlockBuffer.TryRemove(FullBlockBuffer.Keys.Max(), out b); } }
public void ReorgOne() { UnprocessedBlockBuffer.Clear(); // remove the last block if (MerkleChain.Count != 0) { var bestBlock = MerkleChain.Max(); if (MerkleChain.TryRemove(bestBlock)) { List <SmartTransaction> affectedTxs = TrackedTransactions.Where(x => x.Height == bestBlock.Height).Select(x => x).ToList(); foreach (var tx in affectedTxs) { TrackedTransactions.TryRemove(tx); // add it back as a mempool transaction, it'll drop out anyway TrackedTransactions.TryAdd(new SmartTransaction(tx.Transaction, Height.MemPool)); } if (MerkleChain.Count != 0) { BestHeight = MerkleChain.Max().Height; } else { BestHeight = Height.Unknown; } } } }
public async Task SaveAsync(string trackerFolderPath) { await Saving.WaitAsync().ConfigureAwait(false); try { if (TrackedScriptPubKeys.Count > 0 || TrackedTransactions.Count > 0 || MerkleChain.Count > 0) { Directory.CreateDirectory(trackerFolderPath); } if (TrackedScriptPubKeys.Count > 0) { File.WriteAllLines( Path.Combine(trackerFolderPath, TrackedScriptPubKeysFileName), TrackedScriptPubKeys.Select(x => x.ToString())); } if (TrackedTransactions.Count > 0) { File.WriteAllLines( Path.Combine(trackerFolderPath, TrackedTransactionsFileName), TrackedTransactions .Select(x => $"{x.Transaction.ToHex()}:{x.Height}")); } if (MerkleChain.Count > 0) { var path = Path.Combine(trackerFolderPath, MerkleChainFileName); if (File.Exists(path)) { const string backupName = MerkleChainFileName + "_backup"; var backupPath = Path.Combine(trackerFolderPath, backupName); File.Copy(path, backupPath, overwrite: true); File.Delete(path); } using (FileStream stream = File.OpenWrite(path)) { var toFile = MerkleChain.First().ToBytes(); await stream.WriteAsync(toFile, 0, toFile.Length).ConfigureAwait(false); foreach (var block in MerkleChain.Skip(1)) { await stream.WriteAsync(blockSep, 0, blockSep.Length).ConfigureAwait(false); var blockBytes = block.ToBytes(); await stream.WriteAsync(blockBytes, 0, blockBytes.Length).ConfigureAwait(false); } } } } finally { Saving.Release(); } }
private IEnumerable <SmartTransaction> GetNotYetFoundTrackedTransactions() { var notFound = new HashSet <SmartTransaction>(); foreach (var tx in TrackedTransactions.Where(x => !x.Confirmed)) { notFound.Add(tx); } return(notFound); }
private void TrackIfFindRelatedTransactions(Script scriptPubKey, int height, Block block) { foreach (var tx in block.Transactions) { foreach (var output in tx.Outputs) { if (output.ScriptPubKey.Equals(scriptPubKey)) { TrackedTransactions.AddOrReplace(tx.GetHash(), height); } } } }
public async Task LoadAsync(string trackerFolderPath) { using (await AsyncLockSaving.LockAsync()) { if (!Directory.Exists(trackerFolderPath)) { throw new DirectoryNotFoundException($"No Blockchain found at {trackerFolderPath}"); } var tspb = Path.Combine(trackerFolderPath, TrackedScriptPubKeysFileName); if (File.Exists(tspb) && new FileInfo(tspb).Length != 0) { foreach (var line in await File.ReadAllLinesAsync(tspb)) { TrackedScriptPubKeys.Add(new Script(line)); } } var tt = Path.Combine(trackerFolderPath, TrackedTransactionsFileName); if (File.Exists(tt) && new FileInfo(tt).Length != 0) { foreach (var line in await File.ReadAllLinesAsync(tt)) { var pieces = line.Split(':'); var tx = new SmartTransaction(new Transaction(pieces[0]), new Height(pieces[1])); TrackedTransactions.TryAdd(tx); } } var pbc = Path.Combine(trackerFolderPath, MerkleChainFileName); if (File.Exists(pbc) && new FileInfo(pbc).Length != 0) { foreach (var block in CollectionHelpers.Separate(await File.ReadAllBytesAsync(pbc), BlockSep)) { try { SmartMerkleBlock smartMerkleBlock = SmartMerkleBlock.FromBytes(block); MerkleChain.Add(smartMerkleBlock); } catch (EndOfStreamException) { // Some corruption is fine, the software will self correct and save the right data } } if (MerkleChain.Count != 0) { BestHeight = MerkleChain.Max().Height; } } } }
public async Task SaveAsync(string trackerFolderPath) { using (await AsyncLockSaving.LockAsync()) { if (TrackedScriptPubKeys.Count > 0 || TrackedTransactions.Count > 0 || MerkleChain.Count > 0) { Directory.CreateDirectory(trackerFolderPath); } if (TrackedScriptPubKeys.Count > 0) { await File.WriteAllLinesAsync( Path.Combine(trackerFolderPath, TrackedScriptPubKeysFileName), TrackedScriptPubKeys.Select(x => x.ToString())); } if (TrackedTransactions.Count > 0) { await File.WriteAllLinesAsync( Path.Combine(trackerFolderPath, TrackedTransactionsFileName), TrackedTransactions .Select(x => $"{x.Transaction.ToHex()}:{x.Height}")); } if (MerkleChain.Count > 0) { var path = Path.Combine(trackerFolderPath, MerkleChainFileName); // remove legacy backup file if (File.Exists(path + "_backup")) { File.Delete(path + "_backup"); } using (FileStream stream = File.Open(path, FileMode.Create, FileAccess.Write)) { var toFile = MerkleChain.First().ToBytes(); await stream.WriteAsync(toFile, 0, toFile.Length); foreach (var block in MerkleChain.Skip(1)) { await stream.WriteAsync(BlockSep, 0, BlockSep.Length); var blockBytes = block.ToBytes(); await stream.WriteAsync(blockBytes, 0, blockBytes.Length); } } } } }
/// <summary> Track a transaction </summary> /// <returns>False if not found. When confirms, it starts tracking. If too old you need to resync the chain.</returns> public bool Track(uint256 transactionId) { if (TrackedTransactions.Keys.Contains(transactionId)) { var tracked = TrackedTransactions.First(x => x.Key.Equals(transactionId)); if (tracked.Value == -1) { return(false); } else { return(true); } } TrackedTransactions.AddOrReplace(transactionId, -1); Transaction transaction = null; Block block = null; foreach (var b in FullBlockBuffer.Values) { Transaction tx = b.Transactions.FirstOrDefault(x => transactionId.Equals(x.GetHash())); if (tx != default(Transaction)) { transaction = tx; block = b; break; } } // This warning doesn't make sense: // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (block == null || transaction == null) { return(false); } else { PartialBlock partialBlock = Chain.First(x => block.Header.GetHash().Equals(x.Value.MerkleProof.Header.GetHash())).Value; partialBlock.Transactions.Add(transaction); var transactionHashes = partialBlock.MerkleProof.PartialMerkleTree.GetMatchedTransactions() as HashSet <uint256>; transactionHashes.Add(transaction.GetHash()); partialBlock.MerkleProof = block.Filter(transactionHashes.ToArray()); return(true); } }
/// <summary> /// /// </summary> /// <param name="scriptPubKey"></param> /// <param name="receivedTransactions">int: block height</param> /// <param name="spentTransactions">int: block height</param> /// <returns></returns> public bool TryFindConfirmedTransactions(Script scriptPubKey, out ConcurrentHashSet <SmartTransaction> receivedTransactions, out ConcurrentHashSet <SmartTransaction> spentTransactions) { var found = false; receivedTransactions = new ConcurrentHashSet <SmartTransaction>(); spentTransactions = new ConcurrentHashSet <SmartTransaction>(); foreach (var tx in TrackedTransactions.Where(x => x.Confirmed)) { // if already has that tx continue if (receivedTransactions.Any(x => x.GetHash() == tx.GetHash())) { continue; } foreach (var output in tx.Transaction.Outputs) { if (output.ScriptPubKey.Equals(scriptPubKey)) { receivedTransactions.Add(tx); found = true; } } } if (found) { foreach (var tx in TrackedTransactions.Where(x => x.Confirmed)) { // if already has that tx continue if (spentTransactions.Any(x => x.GetHash() == tx.GetHash())) { continue; } foreach (var input in tx.Transaction.Inputs) { if (receivedTransactions.Select(x => x.GetHash()).Contains(input.PrevOut.Hash)) { spentTransactions.Add(tx); found = true; } } } } return(found); }
public async Task LoadAsync(string partialChainFolderPath) { await Saving.WaitAsync().ConfigureAwait(false); try { if (!Directory.Exists(partialChainFolderPath)) { throw new DirectoryNotFoundException($"No Blockchain found at {partialChainFolderPath}"); } var tspb = Path.Combine(partialChainFolderPath, FilesNames.TrackedScriptPubKeys.ToString()); if (File.Exists(tspb) && new FileInfo(tspb).Length != 0) { foreach (var line in File.ReadAllLines(tspb)) { TrackedScriptPubKeys.Add(new Script(line)); } } var tt = Path.Combine(partialChainFolderPath, FilesNames.TrackedTransactions.ToString()); if (File.Exists(tt) && new FileInfo(tt).Length != 0) { foreach (var line in File.ReadAllLines(tt)) { var pieces = line.Split(':'); TrackedTransactions.TryAdd(new uint256(pieces[0]), int.Parse(pieces[1])); } } var pbc = Path.Combine(partialChainFolderPath, FilesNames.PartialBlockChain.ToString()); if (File.Exists(pbc) && new FileInfo(pbc).Length != 0) { foreach (var block in Help.Separate(File.ReadAllBytes(pbc), blockSep)) { PartialBlock pb = new PartialBlock().FromBytes(block); Chain.TryAdd(pb.Height, pb); } } } finally { Saving.Release(); } }
public async Task SaveAsync(string partialChainFolderPath) { await Saving.WaitAsync().ConfigureAwait(false); try { if (TrackedScriptPubKeys.Count > 0 || TrackedTransactions.Count > 0 || Chain.Count > 0) { Directory.CreateDirectory(partialChainFolderPath); } if (TrackedScriptPubKeys.Count > 0) { File.WriteAllLines( Path.Combine(partialChainFolderPath, FilesNames.TrackedScriptPubKeys.ToString()), TrackedScriptPubKeys.Select(x => x.ToString())); } if (TrackedTransactions.Count > 0) { File.WriteAllLines( Path.Combine(partialChainFolderPath, FilesNames.TrackedTransactions.ToString()), TrackedTransactions.Select(x => $"{x.Key}:{x.Value}")); } if (Chain.Count > 0) { byte[] toFile = Chain.Values.First().ToBytes(); foreach (var block in Chain.Values.Skip(1)) { toFile = toFile.Concat(blockSep).Concat(block.ToBytes()).ToArray(); } File.WriteAllBytes(Path.Combine(partialChainFolderPath, FilesNames.PartialBlockChain.ToString()), toFile); } } finally { Saving.Release(); } }
/// <returns>if processed it transaction</returns> public bool ProcessTransaction(SmartTransaction transaction) { // 1. If already tracking can we update it? var found = TrackedTransactions.FirstOrDefault(x => x == transaction); if (found != default(SmartTransaction)) { // if in a lower level don't track if (found.Height.Type <= transaction.Height.Type) { return(false); } else { // else update TrackedTransactions.TryRemove(transaction); TrackedTransactions.TryAdd(transaction); return(true); } } // 2. If this transaction arrived to any of our scriptpubkey track it! if (transaction.Transaction.Outputs.Any(output => TrackedScriptPubKeys.Contains(output.ScriptPubKey))) { TrackedTransactions.TryAdd(transaction); return(true); } // 3. If this transaction spends any of our scriptpubkeys track it! if (transaction.Transaction.Inputs.Any(input => TrackedTransactions .Where(ttx => ttx.GetHash() == input.PrevOut.Hash) .Any(ttx => TrackedScriptPubKeys .Contains(ttx.Transaction.Outputs[input.PrevOut.N].ScriptPubKey)))) { TrackedTransactions.TryAdd(transaction); return(true); } // if got so far we are not interested return(false); }
/// <summary> /// /// </summary> /// <param name="scriptPubKey"></param> /// <returns>if never had any money on it</returns> public bool IsClean(Script scriptPubKey) => TrackedTransactions.All(tx => !tx.Transaction.Outputs.Any(output => output.ScriptPubKey.Equals(scriptPubKey)));