Пример #1
0
        public void Initialize()
        {
            // The transaction index is needed in the event of a reorg.
            if (!this.storeSettings.AddressIndex)
            {
                this.logger.LogTrace("(-)[DISABLED]");
                return;
            }

            string dbPath = Path.Combine(this.dataFolder.RootPath, AddressIndexerDatabaseFilename);

            FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            this.db = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });

            this.addressIndexRepository = new AddressIndexRepository(this.db, this.loggerFactory);

            this.logger.LogDebug("Address indexing is enabled.");

            this.tipDataStore = this.db.GetCollection <AddressIndexerTipData>(DbTipDataKey);

            lock (this.lockObject)
            {
                AddressIndexerTipData tipData = this.tipDataStore.FindAll().FirstOrDefault();

                this.logger.LogDebug("Tip data: '{0}'.", tipData == null ? "null" : tipData.ToString());

                this.IndexerTip = tipData == null ? this.chainIndexer.Genesis : this.consensusManager.Tip.FindAncestorOrSelf(new uint256(tipData.TipHashBytes));

                if (this.IndexerTip == null)
                {
                    // This can happen if block hash from tip data is no longer a part of the consensus chain and node was killed in the middle of a reorg.
                    int rewindAmount = this.compactionTriggerDistance / 2;

                    if (rewindAmount > this.consensusManager.Tip.Height)
                    {
                        this.IndexerTip = this.chainIndexer.Genesis;
                    }
                    else
                    {
                        this.IndexerTip = this.consensusManager.Tip.GetAncestor(this.consensusManager.Tip.Height - rewindAmount);
                    }
                }
            }

            this.outpointsRepository = new AddressIndexerOutpointsRepository(this.db, this.loggerFactory);

            this.RewindAndSave(this.IndexerTip);

            this.logger.LogDebug("Indexer initialized at '{0}'.", this.IndexerTip);

            this.indexingTask = Task.Run(async() => await this.IndexAddressesContinuouslyAsync().ConfigureAwait(false));

            this.asyncProvider.RegisterTask($"{nameof(AddressIndexer)}.{nameof(this.indexingTask)}", this.indexingTask);

            this.nodeStats.RegisterStats(this.AddInlineStats, StatsType.Inline, this.GetType().Name, 400);
        }
Пример #2
0
        public void OutPointCacheCanRetrieveExisting()
        {
            const string CollectionName = "DummyCollection";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory());

            var outPoint = new OutPoint(uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"), 0);

            var data = new OutPointData()
            {
                Outpoint = outPoint.ToString(), ScriptPubKeyBytes = new byte[] { 0, 0, 0, 0 }, Money = Money.Coins(1)
            };

            cache.AddOutPointData(data);

            Assert.True(cache.TryGetOutPointData(outPoint, out OutPointData retrieved));

            Assert.NotNull(retrieved);
            Assert.Equal(outPoint.ToString(), retrieved.Outpoint);
        }
Пример #3
0
 private LiteDatabase Connect(LiteDB.FileMode fileMode)
 => _mem != null
       ? new LiteDatabase(_mem)
       : new LiteDatabase(new ConnectionString
 {
     Filename  = DbPath,
     Journal   = false,
     Mode      = fileMode,
     LimitSize = long.MaxValue
 });
Пример #4
0
        public AddressIndexerOutpointsRepositoryTests()
        {
            LiteDB.FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? LiteDB.FileMode.Exclusive : LiteDB.FileMode.Shared;
            var             db       = new LiteDatabase(new ConnectionString()
            {
                Filename = this.RandomString(20) + ".db", Upgrade = true, Mode = fileMode
            });

            this.repository = new AddressIndexerOutpointsRepository(db, new ExtendedLoggerFactory(), this.maxItems);
        }
        public void Initialize()
        {
            // The transaction index is needed in the event of a reorg.
            if (!this.storeSettings.AddressIndex)
            {
                this.logger.LogTrace("(-)[DISABLED]");
                return;
            }

            string dbPath = Path.Combine(this.dataFolder.RootPath, AddressIndexerDatabaseFilename);

            FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            this.db = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });

            this.addressIndexRepository = new AddressIndexRepository(this.db, this.loggerFactory);

            this.logger.LogDebug("Address indexing is enabled.");

            this.tipDataStore = this.db.GetCollection <AddressIndexerTipData>(DbTipDataKey);

            lock (this.lockObject)
            {
                this.tipData = this.tipDataStore.FindAll().FirstOrDefault();

                if (this.tipData == null)
                {
                    this.logger.LogDebug("Tip was not found, initializing with genesis.");

                    this.tipData = new AddressIndexerTipData()
                    {
                        TipHashBytes = this.network.GenesisHash.ToBytes(), Height = 0
                    };
                    this.tipDataStore.Insert(this.tipData);
                }

                this.IndexerTip = this.consensusManager.Tip.FindAncestorOrSelf(new uint256(this.tipData.TipHashBytes));
            }

            this.outpointsRepository = new AddressIndexerOutpointsRepository(this.db, this.loggerFactory);

            if (this.IndexerTip == null)
            {
                this.IndexerTip = this.consensusManager.Tip.GetAncestor(0);
            }

            this.indexingTask = Task.Run(async() => await this.IndexAddressesContinuouslyAsync().ConfigureAwait(false));

            this.asyncProvider.RegisterTask($"{nameof(AddressIndexer)}.{nameof(this.indexingTask)}", this.indexingTask);

            this.nodeStats.RegisterStats(this.AddInlineStats, StatsType.Inline, 400);
        }
Пример #6
0
        public WalletStore(Network network, DataFolder dataFolder, Types.Wallet wallet)
        {
            var dbPath = Path.Combine(dataFolder.WalletFolderPath, $"{wallet.Name}.db");

            if (!Directory.Exists(dataFolder.WalletFolderPath))
            {
                Directory.CreateDirectory(dataFolder.WalletFolderPath);
            }

            BsonMapper mapper = this.Create();

            LiteDB.FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? LiteDB.FileMode.Exclusive : LiteDB.FileMode.Shared;

            if (!File.Exists(dbPath))
            {
                this.db = new LiteDatabase(new ConnectionString()
                {
                    Filename = dbPath, Mode = fileMode
                }, mapper: mapper);
            }
            else
            {
                // Only perform this check if the database file already exists.
                this.db = new LiteDatabase(new ConnectionString()
                {
                    Filename = dbPath, Mode = fileMode
                }, mapper: mapper);

                // Attempt to access the user version, this will crash if the loaded database is V5 and we use V4 packages.
                try
                {
                    var userVersion = this.db.Engine.UserVersion;
                }
                catch (LiteDB.LiteException)
                {
                    var dbBackupPath = Path.Combine(dataFolder.WalletFolderPath, $"{wallet.Name}.error.db");

                    // Move the problematic database file, which might be a V5 database.
                    File.Move(dbPath, dbBackupPath);

                    // Re-create the database object after we renamed the file.
                    this.db = new LiteDatabase(new ConnectionString()
                    {
                        Filename = dbPath, Mode = fileMode
                    }, mapper: mapper);
                }
            }

            this.network = network;

            this.Init(wallet);
        }
Пример #7
0
        public void Initialize()
        {
            if (!this.storeSettings.TxIndex || !this.storeSettings.AddressIndex)
            {
                this.logger.LogTrace("(-)[DISABLED]");
                return;
            }

            string dbPath = Path.Combine(this.dataFolder.RootPath, "addressindex.litedb");

            FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            this.db = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });

            this.logger.LogDebug("TxIndexing is enabled.");

            this.dataStore = this.db.GetCollection <AddressIndexerData>(DbKey);

            lock (this.lockObject)
            {
                this.addressesIndex = this.dataStore.FindAll().FirstOrDefault();

                if (this.addressesIndex == null)
                {
                    this.logger.LogDebug("Tip was not found, initializing with genesis.");

                    this.addressesIndex = new AddressIndexerData()
                    {
                        TipHashBytes   = this.network.GenesisHash.ToBytes(),
                        AddressChanges = new Dictionary <string, List <AddressBalanceChange> >()
                    };
                    this.dataStore.Insert(this.addressesIndex);
                }

                this.IndexerTip = this.consensusManager.Tip.FindAncestorOrSelf(new uint256(this.addressesIndex.TipHashBytes));
            }

            if (this.IndexerTip == null)
            {
                this.IndexerTip = this.consensusManager.Tip.GetAncestor(0);
            }

            this.indexingTask = Task.Run(async() => await this.IndexAddressesContinuouslyAsync().ConfigureAwait(false));

            this.nodeStats.RegisterStats(this.AddInlineStats, StatsType.Inline, 400);
        }
Пример #8
0
        public void OutPointCacheCannotRetrieveNonexistent()
        {
            const string CollectionName = "DummyCollection";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory());

            Assert.False(cache.TryGetOutPointData(new OutPoint(uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"), 1), out OutPointData retrieved));
            Assert.Null(retrieved);
        }
        private static LiteDatabase Connect <T>(this LiteRevisionsDB <T> revDb, LiteDB.FileMode fileMode)
        {
            var db = revDb._mem != null
                   ? new LiteDatabase(revDb._mem)
                   : new LiteDatabase(new ConnectionString
            {
                Filename  = revDb.DbPath,
                Journal   = false,
                Mode      = fileMode,
                LimitSize = long.MaxValue
            });

            if (fileMode != LiteDB.FileMode.ReadOnly)
            {
                db.Version1 <T>().EnsureIndex(nameof(Versioned <T> .GroupId));
            }

            return(db);
        }
Пример #10
0
        public void AddressCacheRetrievesBlankRecordForNonexistent()
        {
            const string CollectionName = "DummyCollection";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory());

            AddressIndexerData retrieved = cache.GetOrCreateAddress("xyz");

            // A record will be returned with no balance changes associated, if it is new.
            Assert.NotNull(retrieved);
            Assert.Equal("xyz", retrieved.Address);
            Assert.Empty(retrieved.BalanceChanges);
        }
        /// <inheritdoc />
        public void Initialize()
        {
            if (this.db != null)
            {
                throw new Exception("NFTTransferIndexer already initialized!");
            }

            string dbPath = Path.Combine(this.dataFolder.RootPath, DatabaseFilename);

            FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            this.db = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            this.NFTContractCollection = this.db.GetCollection <NFTContractModel>(DbOwnedNFTsKey);

            this.indexingTask = Task.Run(async() => await this.IndexNFTsContinuouslyAsync().ConfigureAwait(false));
            this.asyncProvider.RegisterTask($"{nameof(AddressIndexer)}.{nameof(this.indexingTask)}", this.indexingTask);
        }
Пример #12
0
        public void AddressCacheCanRetrieveExisting()
        {
            const string CollectionName = "DummyCollection";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory());

            string address        = "xyz";
            var    balanceChanges = new List <AddressBalanceChange>();

            balanceChanges.Add(new AddressBalanceChange()
            {
                BalanceChangedHeight = 1, Deposited = true, Satoshi = 1
            });

            var data = new AddressIndexerData()
            {
                Address = address, BalanceChanges = balanceChanges
            };

            cache.AddOrUpdate(data.Address, data, data.BalanceChanges.Count + 1);

            AddressIndexerData retrieved = cache.GetOrCreateAddress("xyz");

            Assert.NotNull(retrieved);
            Assert.Equal("xyz", retrieved.Address);
            Assert.Equal(1, retrieved.BalanceChanges.First().BalanceChangedHeight);
            Assert.True(retrieved.BalanceChanges.First().Deposited);
            Assert.Equal(1, retrieved.BalanceChanges.First().Satoshi);
        }
Пример #13
0
        public void AddressCacheEvicts()
        {
            const string CollectionName = "AddrData";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory(), 4);

            // Recall, each index entry counts as 1 and each balance change associated with it is an additional 1.
            Assert.Equal(0, database.GetCollection <AddressIndexerData>(CollectionName).Count());

            string address1        = "xyz";
            var    balanceChanges1 = new List <AddressBalanceChange>();

            balanceChanges1.Add(new AddressBalanceChange()
            {
                BalanceChangedHeight = 1, Deposited = true, Satoshi = 1
            });
            var data1 = new AddressIndexerData()
            {
                Address = address1, BalanceChanges = balanceChanges1
            };

            cache.AddOrUpdate(data1.Address, data1, data1.BalanceChanges.Count + 1);

            Assert.Equal(0, database.GetCollection <AddressIndexerData>(CollectionName).Count());

            string address2        = "abc";
            var    balanceChanges2 = new List <AddressBalanceChange>();

            balanceChanges2.Add(new AddressBalanceChange()
            {
                BalanceChangedHeight = 2, Deposited = false, Satoshi = 2
            });

            cache.AddOrUpdate(address2, new AddressIndexerData()
            {
                Address = address2, BalanceChanges = balanceChanges2
            }, balanceChanges2.Count + 1);

            Assert.Equal(0, database.GetCollection <AddressIndexerData>(CollectionName).Count());

            string address3        = "def";
            var    balanceChanges3 = new List <AddressBalanceChange>();

            balanceChanges3.Add(new AddressBalanceChange()
            {
                BalanceChangedHeight = 3, Deposited = true, Satoshi = 3
            });
            cache.AddOrUpdate(address3, new AddressIndexerData()
            {
                Address = address3, BalanceChanges = balanceChanges3
            }, balanceChanges3.Count + 1);

            // One of the cache items should have been evicted, and will therefore be persisted on disk.
            Assert.Equal(1, database.GetCollection <AddressIndexerData>(CollectionName).Count());

            // The evicted item should be data1.
            Assert.Equal(data1.Address, database.GetCollection <AddressIndexerData>(CollectionName).FindAll().First().Address);
            Assert.Equal(1, database.GetCollection <AddressIndexerData>(CollectionName).FindAll().First().BalanceChanges.First().BalanceChangedHeight);
            Assert.True(database.GetCollection <AddressIndexerData>(CollectionName).FindAll().First().BalanceChanges.First().Deposited);
            Assert.Equal(1, database.GetCollection <AddressIndexerData>(CollectionName).FindAll().First().BalanceChanges.First().Satoshi);

            // Check that the first address can still be retrieved, it should come from disk in this case.
            AddressIndexerData retrieved = cache.GetOrCreateAddress("xyz");

            Assert.NotNull(retrieved);
            Assert.Equal("xyz", retrieved.Address);
            Assert.Equal(1, retrieved.BalanceChanges.First().BalanceChangedHeight);
            Assert.True(retrieved.BalanceChanges.First().Deposited);
            Assert.Equal(1, retrieved.BalanceChanges.First().Satoshi);
        }
Пример #14
0
        public void OutPointCacheEvicts()
        {
            const string CollectionName = "OutputsData";
            var          dataFolder     = new DataFolder(TestBase.CreateTestDir(this));
            string       dbPath         = Path.Combine(dataFolder.RootPath, CollectionName);
            FileMode     fileMode       = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared;

            var database = new LiteDatabase(new ConnectionString()
            {
                Filename = dbPath, Mode = fileMode
            });
            var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory(), 2);

            Assert.Equal(0, cache.Count);
            Assert.Equal(0, database.GetCollection <OutPointData>(CollectionName).Count());

            var outPoint1 = new OutPoint(uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"), 1);;
            var pair1     = new OutPointData()
            {
                Outpoint = outPoint1.ToString(), ScriptPubKeyBytes = new byte[] { 0, 0, 0, 0 }, Money = Money.Coins(1)
            };

            cache.AddOutPointData(pair1);

            Assert.Equal(1, cache.Count);
            Assert.Equal(0, database.GetCollection <OutPointData>(CollectionName).Count());

            var outPoint2 = new OutPoint(uint256.Parse("cf8ce1419bbc4870b7d4f1c084534d91126dd3283b51ec379e0a20e27bd23633"), 2);;
            var pair2     = new OutPointData()
            {
                Outpoint = outPoint2.ToString(), ScriptPubKeyBytes = new byte[] { 1, 1, 1, 1 }, Money = Money.Coins(2)
            };

            cache.AddOutPointData(pair2);

            Assert.Equal(2, cache.Count);
            Assert.Equal(0, database.GetCollection <OutPointData>(CollectionName).Count());

            var outPoint3 = new OutPoint(uint256.Parse("126dd3283b51ec379e0a20e27bd23633cf8ce1419bbc4870b7d4f1c084534d91"), 3);;
            var pair3     = new OutPointData()
            {
                Outpoint = outPoint3.ToString(), ScriptPubKeyBytes = new byte[] { 2, 2, 2, 2 }, Money = Money.Coins(3)
            };

            cache.AddOutPointData(pair3);

            Assert.Equal(2, cache.Count);

            // One of the cache items should have been evicted, and will therefore be persisted on disk.
            Assert.Equal(1, database.GetCollection <OutPointData>(CollectionName).Count());

            // The evicted item should be pair1.
            Assert.Equal(pair1.ScriptPubKeyBytes, database.GetCollection <OutPointData>(CollectionName).FindAll().First().ScriptPubKeyBytes);

            // It should still be possible to retrieve pair1 from the cache (it will pull it from disk).
            Assert.True(cache.TryGetOutPointData(outPoint1, out OutPointData pair1AfterEviction));

            Assert.NotNull(pair1AfterEviction);
            Assert.Equal(pair1.ScriptPubKeyBytes, pair1AfterEviction.ScriptPubKeyBytes);
            Assert.Equal(pair1.Money, pair1AfterEviction.Money);
        }