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);
            }
        }
Exemple #2
0
 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;
             }
         }
     }
 }
Exemple #3
0
        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();
            }
        }
Exemple #4
0
        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);
             }
         }
     }
 }
Exemple #6
0
        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;
                    }
                }
            }
        }
Exemple #7
0
        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);
            }
        }
Exemple #9
0
        /// <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();
            }
        }
Exemple #12
0
        /// <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);
        }
Exemple #13
0
 /// <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)));