diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index dd5dea399..d072fe496 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -134,7 +134,8 @@ class DatabaseMigration { } if (databaseSchemaVersion < 9) { - await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)') + await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)'); + await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)'); } connection.release(); diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 18d82669f..f15c52c88 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -97,9 +97,8 @@ class Mining { const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps(); const hashrates: any[] = []; const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - const lastWeekMidnight = this.getDateMidnight(new Date()); - lastWeekMidnight.setDate(lastWeekMidnight.getDate() - 7); - let toTimestamp = Math.round(lastWeekMidnight.getTime() / 1000); + const lastMidnight = this.getDateMidnight(new Date()); + let toTimestamp = Math.round((lastMidnight.getTime() - 604800) / 1000); const totalWeekIndexed = (await BlocksRepository.$blockCount(null, null)) / 1008; let indexedThisRun = 0; @@ -167,7 +166,6 @@ class Mining { this.weeklyHashrateIndexingStarted = false; throw e; } - } /** diff --git a/backend/src/database.ts b/backend/src/database.ts index b0d39b301..9f2655016 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -11,6 +11,7 @@ export class DB { password: config.DATABASE.PASSWORD, connectionLimit: 10, supportBigNumbers: true, + timezone: '+00:00', }); } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index d91777880..844f62bad 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -53,15 +53,17 @@ class BlocksRepository { // logger.debug(query); await connection.query(query, params); + connection.release(); } catch (e: any) { + connection.release(); if (e.errno === 1062) { // ER_DUP_ENTRY logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`); } else { + connection.release(); logger.err('$saveBlockInDatabase() error' + (e instanceof Error ? e.message : e)); + throw e; } } - - connection.release(); } /** @@ -73,20 +75,26 @@ class BlocksRepository { } const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(` - SELECT height - FROM blocks - WHERE height <= ? AND height >= ? - ORDER BY height DESC; - `, [startHeight, endHeight]); - connection.release(); + try { + const [rows]: any[] = await connection.query(` + SELECT height + FROM blocks + WHERE height <= ? AND height >= ? + ORDER BY height DESC; + `, [startHeight, endHeight]); + connection.release(); - const indexedBlockHeights: number[] = []; - rows.forEach((row: any) => { indexedBlockHeights.push(row.height); }); - const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse(); - const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1); + const indexedBlockHeights: number[] = []; + rows.forEach((row: any) => { indexedBlockHeights.push(row.height); }); + const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse(); + const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1); - return missingBlocksHeights; + return missingBlocksHeights; + } catch (e) { + connection.release(); + logger.err('$getMissingBlocksBetweenHeights() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -111,10 +119,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getEmptyBlocks() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -143,10 +157,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows[0].blockCount; + return rows[0].blockCount; + } catch (e) { + connection.release(); + logger.err('$blockCount() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -177,10 +197,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows[0]; + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$blockCountBetweenTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -194,23 +220,26 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - if (rows.length <= 0) { - return -1; + if (rows.length <= 0) { + return -1; + } + + return rows[0].blockTimestamp; + } catch (e) { + connection.release(); + logger.err('$oldestBlockTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows[0].blockTimestamp; } /** * Get blocks mined by a specific mining pool */ - public async $getBlocksByPool( - poolId: number, - startHeight: number | null = null - ): Promise { + public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise { const params: any[] = []; let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward FROM blocks @@ -227,14 +256,20 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - for (const block of rows) { - delete block['blockTimestamp']; + for (const block of rows) { + delete block['blockTimestamp']; + } + + return rows; + } catch (e) { + connection.release(); + logger.err('$getBlocksByPool() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows; } /** @@ -242,19 +277,25 @@ class BlocksRepository { */ public async $getBlockByHeight(height: number): Promise { const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(` - SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes - FROM blocks - JOIN pools ON blocks.pool_id = pools.id - WHERE height = ${height}; - `); - connection.release(); + try { + const [rows]: any[] = await connection.query(` + SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes + FROM blocks + JOIN pools ON blocks.pool_id = pools.id + WHERE height = ${height}; + `); + connection.release(); - if (rows.length <= 0) { - return null; + if (rows.length <= 0) { + return null; + } + + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$getBlockByHeight() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows[0]; } /** @@ -297,21 +338,34 @@ class BlocksRepository { ORDER BY t.height `; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - for (let row of rows) { - delete row['rn']; + for (let row of rows) { + delete row['rn']; + } + + return rows; + } catch (e) { + connection.release(); + logger.err('$getBlocksDifficulty() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows; } public async $getOldestIndexedBlockHeight(): Promise { const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); - connection.release(); - return rows[0].minHeight; + try { + const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); + connection.release(); + + return rows[0].minHeight; + } catch (e) { + connection.release(); + logger.err('$getOldestIndexedBlockHeight() error' + (e instanceof Error ? e.message : e)); + throw e; + } } } diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 367733cfa..3523004d5 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -24,12 +24,12 @@ class HashratesRepository { try { // logger.debug(query); await connection.query(query); + connection.release(); } catch (e: any) { + connection.release(); logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e)); throw e; } - - connection.release(); } public async $getNetworkDailyHashrate(interval: string | null): Promise { @@ -51,20 +51,33 @@ class HashratesRepository { query += ` ORDER by hashrate_timestamp`; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getNetworkDailyHashrate() error' + (e instanceof Error ? e.message : e)); + throw e; + } } public async $getWeeklyHashrateTimestamps(): Promise { const connection = await DB.pool.getConnection(); - let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; - const [rows]: any[] = await connection.query(query); - connection.release(); + const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; - return rows.map(row => row.timestamp); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); + + return rows.map(row => row.timestamp); + } catch (e) { + connection.release(); + logger.err('$getWeeklyHashrateTimestamps() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -91,26 +104,44 @@ class HashratesRepository { query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsWeeklyHashrate() error' + (e instanceof Error ? e.message : e)); + throw e; + } } public async $setLatestRunTimestamp(key: string, val: any = null) { const connection = await DB.pool.getConnection(); const query = `UPDATE state SET number = ? WHERE name = ?`; - await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); - connection.release(); + try { + await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); + connection.release(); + } catch (e) { + connection.release(); + } } public async $getLatestRunTimestamp(key: string): Promise { const connection = await DB.pool.getConnection(); const query = `SELECT number FROM state WHERE name = ?`; - const [rows] = await connection.query(query, [key]); - connection.release(); - return rows[0]['number']; + + try { + const [rows] = await connection.query(query, [key]); + connection.release(); + + return rows[0]['number']; + } catch (e) { + connection.release(); + logger.err('$setLatestRunTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; + } } } diff --git a/backend/src/repositories/PoolsRepository.ts b/backend/src/repositories/PoolsRepository.ts index ea617322a..3f904888d 100644 --- a/backend/src/repositories/PoolsRepository.ts +++ b/backend/src/repositories/PoolsRepository.ts @@ -43,26 +43,38 @@ class PoolsRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query); - connection.release(); + try { + const [rows] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsInfo() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** * Get basic pool info and block count between two timestamp */ public async $getPoolsInfoBetween(from: number, to: number): Promise { - let query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName + const query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName FROM pools LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?) GROUP BY pools.id`; const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, [from, to]); - connection.release(); + try { + const [rows] = await connection.query(query, [from, to]); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsInfoBetween() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -76,13 +88,19 @@ class PoolsRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, [poolId]); - connection.release(); + try { + const [rows] = await connection.query(query, [poolId]); + connection.release(); - rows[0].regexes = JSON.parse(rows[0].regexes); - rows[0].addresses = JSON.parse(rows[0].addresses); + rows[0].regexes = JSON.parse(rows[0].regexes); + rows[0].addresses = JSON.parse(rows[0].addresses); - return rows[0]; + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$getPool() error' + (e instanceof Error ? e.message : e)); + throw e; + } } }