public async Task DeleteLastBlockchainFileAsync() { const string deleteFromTransactionOutput = @" DELETE TransactionOutput FROM TransactionOutput INNER JOIN BitcoinTransaction ON BitcoinTransaction.BitcoinTransactionId = TransactionOutput.BitcoinTransactionId INNER JOIN Block ON Block.BlockId = BitcoinTransaction.BlockId WHERE Block.BlockchainFileId >= @MaxBlockchainFileId"; const string deleteFromTransactionInput = @" DELETE TransactionInput FROM TransactionInput INNER JOIN BitcoinTransaction ON BitcoinTransaction.BitcoinTransactionId = TransactionInput.BitcoinTransactionId INNER JOIN Block ON Block.BlockId = BitcoinTransaction.BlockId WHERE Block.BlockchainFileId >= @MaxBlockchainFileId"; const string deleteFromBitcoinTransaction = @" DELETE BitcoinTransaction FROM BitcoinTransaction INNER JOIN Block ON Block.BlockId = BitcoinTransaction.BlockId WHERE Block.BlockchainFileId >= @MaxBlockchainFileId"; const string deleteFromBlock = @" DELETE Block FROM Block WHERE Block.BlockchainFileId >= @MaxBlockchainFileId"; const string deleteFromBlockchainFile = "DELETE FROM BlockchainFile WHERE BlockchainFile.BlockchainFileId >= @MaxBlockchainFileId"; int lastBlockchainFileId = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT MAX(BlockchainFileId) from BlockchainFile")); await this.adoNetLayer.ExecuteStatementNoResultAsync(deleteFromTransactionOutput, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, lastBlockchainFileId)); await this.adoNetLayer.ExecuteStatementNoResultAsync(deleteFromTransactionInput, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, lastBlockchainFileId)); await this.adoNetLayer.ExecuteStatementNoResultAsync(deleteFromBitcoinTransaction, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, lastBlockchainFileId)); await this.adoNetLayer.ExecuteStatementNoResultAsync(deleteFromBlock, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, lastBlockchainFileId)); await this.adoNetLayer.ExecuteStatementNoResultAsync(deleteFromBlockchainFile, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, lastBlockchainFileId)); }
public void AddBlockchainFile(BlockchainFile blockchainFile) { this.adoNetLayer.ExecuteStatementNoResult( "INSERT INTO BlockchainFile(BlockchainFileId, BlockchainFileName) VALUES (@BlockchainFileId, @BlockchainFileName)", AdoNetLayer.CreateInputParameter("@BlockchainFileId", SqlDbType.Int, blockchainFile.BlockchainFileId), AdoNetLayer.CreateInputParameter("@BlockchainFileName", SqlDbType.NVarChar, blockchainFile.BlockchainFileName)); }
public ValidationDataSetInfo <ValidationBlockDataSet> GetValidationBlockSampleDataSet(long maxBlockchainFileId, int sampleRatio) { const string sqlCommandText = @" SELECT BlockId, BlockchainFileId, BlockVersion, BlockHash, PreviousBlockHash, BlockTimestamp, TransactionCount, TransactionInputCount, TotalInputBtc, TransactionOutputCount, TotalOutputBtc, TransactionFeeBtc, TotalUnspentOutputBtc FROM View_BlockAggregated WHERE BlockId <= (SELECT MAX(BlockId) FROM Block WHERE BlockchainFileId <= @MaxBlockchainFileId) AND BlockId % @SampleRatio = 0 ORDER BY BlockId"; return(this.GetValidationDataSetInfo <ValidationBlockDataSet>( sqlCommandText, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.BigInt, maxBlockchainFileId), AdoNetLayer.CreateInputParameter("@SampleRatio", SqlDbType.Int, sampleRatio))); }
public BitcoinDataLayer(string connectionString, int commandTimeout = DefaultDbCommandTimeout) { this.sqlConnection = new SqlConnection(connectionString); this.sqlConnection.Open(); this.adoNetLayer = new AdoNetLayer(this.sqlConnection, commandTimeout); }
public ValidationDataSetInfo <ValidationTransactionOutputDataSet> GetValidationTransactionOutputSampleDataSet(int maxBlockchainFileId, int sampleRatio) { const string sqlCommandText = @" SELECT TransactionOutput.TransactionOutputId, TransactionOutput.BitcoinTransactionId, TransactionOutput.OutputIndex, TransactionOutput.OutputValueBtc, TransactionOutput.OutputScript, CASE WHEN EXISTS (SELECT * FROM TransactionInput WHERE SourceTransactionOutputId = TransactionOutput.OutputIndex) THEN 1 ELSE 0 END AS IsSpent FROM TransactionOutput WHERE TransactionOutput.TransactionOutputId <= ( SELECT MAX(TransactionOutputId) FROM TransactionOutput INNER JOIN BitcoinTransaction ON BitcoinTransaction.BitcoinTransactionId = TransactionOutput.BitcoinTransactionId INNER JOIN Block ON Block.BlockId = BitcoinTransaction.BlockId WHERE Block.BlockchainFileId <= @MaxBlockchainFileId) AND TransactionOutput.TransactionOutputId % @SampleRatio = 0 ORDER BY TransactionOutput.TransactionOutputId"; return(this.GetValidationDataSetInfo <ValidationTransactionOutputDataSet>( sqlCommandText, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.BigInt, maxBlockchainFileId), AdoNetLayer.CreateInputParameter("@SampleRatio", SqlDbType.Int, sampleRatio))); }
public ValidationDataSetInfo <ValidationTransactionDataSet> GetValidationTransactionSampleDataSet(int maxBlockchainFileId, int sampleRatio) { const string sqlCommandText = @" SELECT BitcoinTransactionId, BlockId, TransactionHash, TransactionVersion, TransactionLockTime, TransactionInputCount, TotalInputBtc, TransactionOutputCount, TotalOutputBtc, TransactionFeeBtc, TotalUnspentOutputBtc FROM View_TransactionAggregated WHERE BitcoinTransactionId <= ( SELECT MAX(BitcoinTransactionId) FROM BitcoinTransaction INNER JOIN Block ON Block.BlockId = BitcoinTransaction.BlockId WHERE Block.BlockchainFileId <= @MaxBlockchainFileId) AND BitcoinTransactionId % @SampleRatio = 0 ORDER BY BitcoinTransactionId"; return(this.GetValidationDataSetInfo <ValidationTransactionDataSet>( sqlCommandText, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.BigInt, maxBlockchainFileId), AdoNetLayer.CreateInputParameter("@SampleRatio", SqlDbType.Int, sampleRatio))); }
public void GetDatabaseEntitiesCount(out int blockchainFileCount, out int blockCount, out int transactionCount, out int transactionInputCount, out int transactionOutputCount) { blockchainFileCount = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT COUNT(1) from BlockchainFile")); blockCount = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT COUNT(1) from Block")); transactionCount = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT COUNT(1) from BitcoinTransaction")); transactionInputCount = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT COUNT(1) from TransactionInput")); transactionOutputCount = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT COUNT(1) from TransactionOutput")); }
private long GetMaxTransactionNum() { string sqlCommandGetMaxTransactionId = @"SELECT TOP 1 BitcoinTransaction.BitcoinTransactionId FROM BitcoinTransaction ORDER BY BitcoinTransactionId desc"; return(AdoNetLayer.ConvertDbValue <long>(adoNetLayer.ExecuteScalar(sqlCommandGetMaxTransactionId))); }
public void GetMaximumIdValues(out int blockchainFileId, out long blockId, out long bitcoinTransactionId, out long transactionInputId, out long transactionOutputId) { blockchainFileId = AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT MAX(BlockchainFileId) from BlockchainFile"), -1); blockId = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar("SELECT MAX(BlockId) from Block"), -1); bitcoinTransactionId = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar("SELECT MAX(BitcoinTransactionId) from BitcoinTransaction"), -1); transactionInputId = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar("SELECT MAX(TransactionInputId) from TransactionInput"), -1); transactionOutputId = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar("SELECT MAX(TransactionOutputId) from TransactionOutput"), -1); }
public void UpdateTransactionOutputAddressId() { const string sqlGetFirstTXOutUnsetAddressId = @" SELECT TOP 1 TransactionOutputId FROM TransactionOutput WHERE TransactionOutputId > @lastTransactionOutputId AND OutputAddressId = -1 AND OutputAddress != '0'"; const string sqlUpdateAddressId = @" UPDATE TransactionOutput SET OutputAddressId = @mOutputAddressId WHERE TransactionOutputId >= @currentTransactionOutputId AND OutputAddress = ( SELECT OutputAddress FROM TransactionOutput WHERE TransactionOutputId = @currentTransactionOutputId)"; const string sqlGetMaxOutputAddressId = @" SELECT MAX(OutputAddressId) FROM TransactionOutput"; long MaxOutputAddressId = AdoNetLayer.ConvertDbValue <long>( this.adoNetLayer.ExecuteScalar(sqlGetMaxOutputAddressId)); long OutputAddressId = MaxOutputAddressId + 1; //得到TransactionOutput的数量 long MaxTransactionOutputId = AdoNetLayer.ConvertDbValue <long>( this.adoNetLayer.ExecuteScalar( @"select TOP 1 TransactionOutputId from TransactionOutput order by TransactionOutputId desc")); long TransactionOutputCount = -1; long SolveCount = 0; Console.WriteLine("开始为地址编号......耗时也许会很久"); while (TransactionOutputCount < MaxTransactionOutputId) { //找到第一个OutputAddressId=-1 的 TxOutId即第一个未被编号的地址。 TransactionOutputCount = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar(sqlGetFirstTXOutUnsetAddressId, AdoNetLayer.CreateInputParameter("@lastTransactionOutputId", SqlDbType.BigInt, TransactionOutputCount))); this.adoNetLayer.ExecuteStatementNoResult(sqlUpdateAddressId, AdoNetLayer.CreateInputParameter("mOutputAddressId", SqlDbType.BigInt, OutputAddressId), AdoNetLayer.CreateInputParameter("currentTransactionOutputId", SqlDbType.BigInt, TransactionOutputCount)); SolveCount++; if (SolveCount % 100000 == 0) { Console.Write("\r已经处理了{0}个地址", SolveCount); } OutputAddressId++; } }
public SqlDataReader GetTransactionGraphEdgeReader(long bottomBound, long topBound) { string sqlGetEdge = @" SELECT Edge.SourceId, Edge.TargetId, Edge.Value FROM Edge WHERE EdgeId >= @BottomBound AND EdgeId < @TopBound"; return(this.adoNetLayer.ExecuteStatementReader(sqlGetEdge, AdoNetLayer.CreateInputParameter("@BottomBound", SqlDbType.BigInt, bottomBound), AdoNetLayer.CreateInputParameter("@TopBound", SqlDbType.BigInt, topBound))); }
public void CreateNewDatabase() { string connectionString = this.databaseConnection.MasterConnectionString; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); AdoNetLayer adoNetLayer = new AdoNetLayer(sqlConnection); adoNetLayer.ExecuteStatementNoResult(string.Format(CultureInfo.InvariantCulture, "CREATE DATABASE {0}", this.databaseConnection.DatabaseName)); } }
public bool DatabaseExists() { string connectionString = this.databaseConnection.MasterConnectionString; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); AdoNetLayer adoNetLayer = new AdoNetLayer(sqlConnection); return AdoNetLayer.ConvertDbValue<int>(adoNetLayer.ExecuteScalar( "SELECT CASE WHEN EXISTS (SELECT * FROM sys.databases WHERE [Name] = @DatabaseName) THEN 1 ELSE 0 END AS DatabaseExists", AdoNetLayer.CreateInputParameter("@DatabaseName", SqlDbType.NVarChar, this.databaseConnection.DatabaseName))) == 1; } }
public bool DatabaseExists() { string connectionString = this.databaseConnection.MasterConnectionString; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); AdoNetLayer adoNetLayer = new AdoNetLayer(sqlConnection); return(AdoNetLayer.ConvertDbValue <int>(adoNetLayer.ExecuteScalar( "SELECT CASE WHEN EXISTS (SELECT * FROM sys.databases WHERE [Name] = @DatabaseName) THEN 1 ELSE 0 END AS DatabaseExists", AdoNetLayer.CreateInputParameter("@DatabaseName", SqlDbType.NVarChar, this.databaseConnection.DatabaseName))) == 1); } }
public void ExecuteDatabaseSetupStatements() { string connectionString = this.databaseConnection.ConnectionString; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); AdoNetLayer adoNetLayer = new AdoNetLayer(sqlConnection); foreach (string sqlCommand in GetSqlSections()) { adoNetLayer.ExecuteStatementNoResult(sqlCommand); } } }
public void DeleteDatabase() { string connectionString = this.databaseConnection.MasterConnectionString; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); AdoNetLayer adoNetLayer = new AdoNetLayer(sqlConnection); string takeDbOffline = string.Format(CultureInfo.InvariantCulture, "ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE", this.databaseConnection.DatabaseName); string deleteDb = string.Format(CultureInfo.InvariantCulture, "DROP DATABASE [{0}]", this.databaseConnection.DatabaseName); adoNetLayer.ExecuteStatementNoResult(takeDbOffline); adoNetLayer.ExecuteStatementNoResult(deleteDb); } }
public void GenerateEdgeByBatch() { const int maxBatchSize = 10000000; Stopwatch updateTransactionSourceOutputWatch = new Stopwatch(); updateTransactionSourceOutputWatch.Start(); long BitcoinTransactionNum = this.GetMaxTransactionNum() + 1; long batchSize = BitcoinTransactionNum / 20; batchSize = batchSize >= 1 ? batchSize : 1; batchSize = batchSize <= maxBatchSize ? batchSize : maxBatchSize; string sqlCommandGenerateEdge = @"INSERT INTO Edge (SourceId, TargetId, Value) SELECT BitcoinTransaction.BitcoinTransactionId AS SourceId, T1.BitcoinTransactionId AS TargetId, TransactionOutput.OutputValueBtc AS Value FROM TransactionOutput INNER JOIN BitcoinTransaction ON BitcoinTransaction.BitcoinTransactionId = TransactionOutput.BitcoinTransactionId INNER JOIN TransactionInput ON TransactionInput.SourceTransactionHash = BitcoinTransaction.TransactionHash AND TransactionOutput.OutputIndex = TransactionInput.SourceTransactionOutputIndex INNER JOIN ( SELECT BitcoinTransaction.BitcoinTransactionId FROM BitcoinTransaction WHERE BitcoinTransaction.BitcoinTransactionId >= @leftBoundary and BitcoinTransaction.BitcoinTransactionId < @rightBoundary ) AS T1 ON T1.BitcoinTransactionId = TransactionInput.BitcoinTransactionId order by TargetId" ; long leftBoundary = 0; long rightBoundary = leftBoundary + batchSize; Console.Write("\rGenerateing Edges(this may take a long time)... {0}%", 100 * (rightBoundary - batchSize) / BitcoinTransactionNum); while (leftBoundary < BitcoinTransactionNum) { this.adoNetLayer.ExecuteStatementNoResult(sqlCommandGenerateEdge, AdoNetLayer.CreateInputParameter("@leftBoundary", SqlDbType.BigInt, leftBoundary), AdoNetLayer.CreateInputParameter("@rightBoundary", SqlDbType.BigInt, rightBoundary)); leftBoundary += batchSize; rightBoundary = leftBoundary + batchSize; Console.Write("\rGenerateing Edges(this may take a long time)... {0}%", 100 * (rightBoundary - batchSize) / BitcoinTransactionNum); } updateTransactionSourceOutputWatch.Stop(); Console.WriteLine("\rGenerate Graphic completed in {0:0.000} seconds. ", updateTransactionSourceOutputWatch.Elapsed.TotalSeconds); }
public ValidationDataSetInfo <ValidationBlockchainDataSet> GetValidationBlockchainDataSet(int maxBlockchainFileId) { const string sqlCommandText = @" SELECT COUNT(1) AS BlockCount, SUM(TransactionCount) AS TransactionCount, SUM(TransactionInputCount) AS TransactionInputCount, SUM(TotalInputBtc) AS TotalInputBtc, SUM(TransactionOutputCount) AS TransactionOutputCount, SUM(TotalOutputBtc) AS TotalOutputBtc, SUM(TransactionFeeBtc) AS TransactionFeeBtc, SUM(TotalUnspentOutputBtc) AS TotalUnspentOutputBtc FROM View_BlockAggregated WHERE BlockchainFileId <= @MaxBlockchainFileId"; return(this.GetValidationDataSetInfo <ValidationBlockchainDataSet>( sqlCommandText, AdoNetLayer.CreateInputParameter("@MaxBlockchainFileId", SqlDbType.Int, maxBlockchainFileId))); }
/// <summary> /// Applied after a series of blocks were deleted. /// This method will update the block IDs so that they are forming a consecutive sequence. /// </summary> /// <param name="blocksDeleted"> /// The list of IDs for blocks that were deleted. /// </param> public void CompactBlockIds(IEnumerable <long> blocksDeleted) { const string sqlCommandUpdateBlockBlockIdSection = @"UPDATE Block SET BlockId = BlockId - @DecrementAmount WHERE BlockId BETWEEN @BlockId1 AND @BlockId2"; const string sqlCommandUpdateTransactionBlockIdSection = @"UPDATE BitcoinTransaction SET BlockId = BlockId - @DecrementAmount WHERE BlockId BETWEEN @BlockId1 AND @BlockId2"; const string sqlCommandUpdateBlockBlockIdLastSection = @"UPDATE Block SET BlockId = BlockId - @DecrementAmount WHERE BlockId > @BlockId"; const string sqlCommandUpdateTransactionBlockIdLastSection = @"UPDATE BitcoinTransaction SET BlockId = BlockId - @DecrementAmount WHERE BlockId > @BlockId"; List <long> orderedBlocksDeleted = blocksDeleted.OrderBy(id => id).ToList(); int decrementAmount = 1; for (int i = 0; i < orderedBlocksDeleted.Count - 1; i++) { long blockId1 = orderedBlocksDeleted[i]; long blockId2 = orderedBlocksDeleted[i + 1]; this.adoNetLayer.ExecuteStatementNoResult( sqlCommandUpdateBlockBlockIdSection, AdoNetLayer.CreateInputParameter("@DecrementAmount", SqlDbType.Int, decrementAmount), AdoNetLayer.CreateInputParameter("@BlockId1", SqlDbType.BigInt, blockId1), AdoNetLayer.CreateInputParameter("@BlockId2", SqlDbType.BigInt, blockId2)); this.adoNetLayer.ExecuteStatementNoResult( sqlCommandUpdateTransactionBlockIdSection, AdoNetLayer.CreateInputParameter("@DecrementAmount", SqlDbType.Int, decrementAmount), AdoNetLayer.CreateInputParameter("@BlockId1", SqlDbType.BigInt, blockId1), AdoNetLayer.CreateInputParameter("@BlockId2", SqlDbType.BigInt, blockId2)); decrementAmount++; } long blockId = orderedBlocksDeleted[orderedBlocksDeleted.Count - 1]; this.adoNetLayer.ExecuteStatementNoResult( sqlCommandUpdateBlockBlockIdLastSection, AdoNetLayer.CreateInputParameter("@DecrementAmount", SqlDbType.BigInt, decrementAmount), AdoNetLayer.CreateInputParameter("@BlockId", SqlDbType.BigInt, blockId)); this.adoNetLayer.ExecuteStatementNoResult( sqlCommandUpdateTransactionBlockIdLastSection, AdoNetLayer.CreateInputParameter("@DecrementAmount", SqlDbType.BigInt, decrementAmount), AdoNetLayer.CreateInputParameter("@BlockId", SqlDbType.BigInt, blockId)); }
public void UpdateTransactionOutputAddressId() { const string sqlGetFirstTXOutUnsetAddressId = @" SELECT TOP 1 TransactionOutputId FROM TransactionOutput WHERE TransactionOutputId > @lastTransactionOutputId AND OutputAddressId = -1 AND OutputAddress != '0'"; const string sqlUpdateAddressId = @" UPDATE TransactionOutput SET OutputAddressId = @mOutputAddressId WHERE TransactionOutputId >= @currentTransactionOutputId AND OutputAddress = ( SELECT OutputAddress FROM TransactionOutput WHERE TransactionOutputId = @currentTransactionOutputId)"; long OutputAddressId = 0; long MaxTransactionOutputId = AdoNetLayer.ConvertDbValue <long>( this.adoNetLayer.ExecuteScalar( @"select TOP 1 TransactionOutputId from TransactionOutput order by TransactionOutputId desc")); long TransactionOutputCount = -1; while (TransactionOutputCount < MaxTransactionOutputId) { TransactionOutputCount = AdoNetLayer.ConvertDbValue <long>(this.adoNetLayer.ExecuteScalar(sqlGetFirstTXOutUnsetAddressId, AdoNetLayer.CreateInputParameter("@lastTransactionOutputId", SqlDbType.BigInt, TransactionOutputCount))); this.adoNetLayer.ExecuteStatementNoResult(sqlUpdateAddressId, AdoNetLayer.CreateInputParameter("mOutputAddressId", SqlDbType.BigInt, OutputAddressId), AdoNetLayer.CreateInputParameter("currentTransactionOutputId", SqlDbType.BigInt, TransactionOutputCount)); OutputAddressId++; } }
public long GetTransactionSourceOutputRowsToUpdate() { const string sqlCountRowsToUpdateCommand = @"SELECT COUNT(1) FROM TransactionInput WHERE SourceTransactionOutputId = -1"; return(AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar(sqlCountRowsToUpdateCommand))); }
public string GetLastKnownBlockchainFileName() { return(AdoNetLayer.ConvertDbValue <string>(this.adoNetLayer.ExecuteScalar("SELECT TOP 1 BlockchainFileName FROM BlockchainFile ORDER BY BlockchainFileId DESC"))); }
public bool IsSchemaSetup() { return(AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar( "SELECT CASE WHEN EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'BtcDbSettings') THEN 1 ELSE 0 END AS IsSchemaSetup")) == 1); }
public bool IsDatabaseEmpty() { return(AdoNetLayer.ConvertDbValue <int>(this.adoNetLayer.ExecuteScalar("SELECT CASE WHEN EXISTS (SELECT 1 FROM Block) THEN 0 ELSE 1 END AS IsEmpty")) == 1); }
public BitcoinAdoNetLayer(string connectionString, int commandTimeout = DefaultDbCommandTimeout) { sqlConnection = new SqlConnection(connectionString); sqlConnection.Open(); adoNetLayer = new AdoNetLayer(sqlConnection, commandTimeout); }