Import mining pools into the database - Increment db schema to 3
This commit is contained in:
		
							parent
							
								
									bc925a409f
								
							
						
					
					
						commit
						2848f56c2b
					
				| @ -3,10 +3,10 @@ import config from '../config'; | |||||||
| import { DB } from '../database'; | import { DB } from '../database'; | ||||||
| import logger from '../logger'; | import logger from '../logger'; | ||||||
| 
 | 
 | ||||||
| const sleep = (ms: number) => new Promise( res => setTimeout(res, ms)); | const sleep = (ms: number) => new Promise(res => setTimeout(res, ms)); | ||||||
| 
 | 
 | ||||||
| class DatabaseMigration { | class DatabaseMigration { | ||||||
|   private static currentVersion = 2; |   private static currentVersion = 3; | ||||||
|   private queryTimeout = 120000; |   private queryTimeout = 120000; | ||||||
|   private statisticsAddedIndexed = false; |   private statisticsAddedIndexed = false; | ||||||
| 
 | 
 | ||||||
| @ -83,6 +83,9 @@ class DatabaseMigration { | |||||||
|       if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) { |       if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) { | ||||||
|         await this.$executeQuery(connection, `CREATE INDEX added ON statistics (added);`); |         await this.$executeQuery(connection, `CREATE INDEX added ON statistics (added);`); | ||||||
|       } |       } | ||||||
|  |       if (databaseSchemaVersion < 3) { | ||||||
|  |         await this.$executeQuery(connection, this.getCreatePoolsTableQuery(), await this.$checkIfTableExists('pools')); | ||||||
|  |       } | ||||||
|       connection.release(); |       connection.release(); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       connection.release(); |       connection.release(); | ||||||
| @ -335,6 +338,17 @@ class DatabaseMigration { | |||||||
|       final_tx int(11) NOT NULL |       final_tx int(11) NOT NULL | ||||||
|     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 |     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   private getCreatePoolsTableQuery(): string { | ||||||
|  |     return `CREATE TABLE IF NOT EXISTS pools (
 | ||||||
|  |       id int(11) NOT NULL AUTO_INCREMENT, | ||||||
|  |       name varchar(50) NOT NULL, | ||||||
|  |       link varchar(255) NOT NULL, | ||||||
|  |       addresses text NOT NULL, | ||||||
|  |       regexes text NOT NULL, | ||||||
|  |       PRIMARY KEY (id) | ||||||
|  |     ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`;
 | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default new DatabaseMigration(); | export default new DatabaseMigration(); | ||||||
							
								
								
									
										118
									
								
								backend/src/api/pools-parser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								backend/src/api/pools-parser.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | import {readFileSync} from 'fs'; | ||||||
|  | import { DB } from '../database'; | ||||||
|  | import logger from '../logger'; | ||||||
|  | 
 | ||||||
|  | interface Pool { | ||||||
|  |   name: string, | ||||||
|  |   link: string, | ||||||
|  |   regexes: string[], | ||||||
|  |   addresses: string[], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class PoolsParser { | ||||||
|  |   /** | ||||||
|  |    * Parse the pools.json file, consolidate the data and dump it into the database | ||||||
|  |    */ | ||||||
|  |   public async migratePoolsJson() { | ||||||
|  |     logger.info('Importing pools.json to the database'); | ||||||
|  |     let connection = await DB.pool.getConnection(); | ||||||
|  | 
 | ||||||
|  |     // Check if the pools table does not have data already, for now we do not support updating it
 | ||||||
|  |     // but that will come in a later version
 | ||||||
|  |     let [rows] = await connection.query<any>({ sql: 'SELECT count(id) as count from pools;', timeout: 120000 }); | ||||||
|  |     if (rows[0].count !== 0) { | ||||||
|  |       logger.info('Pools table already contain data, updating it is not yet supported, skipping.'); | ||||||
|  |       connection.release(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     logger.info('Open ../frontend/cypress/fixtures/pools.json'); | ||||||
|  |     const fileContent: string = readFileSync('../frontend/cypress/fixtures/pools.json','utf8'); | ||||||
|  |     const poolsJson: object = JSON.parse(fileContent); | ||||||
|  | 
 | ||||||
|  |     // First we save every entries without paying attention to pool duplication
 | ||||||
|  |     let poolsDuplicated: Pool[] = []; | ||||||
|  | 
 | ||||||
|  |     logger.info('Parse coinbase_tags'); | ||||||
|  |     const coinbaseTags = Object.entries(poolsJson['coinbase_tags']); | ||||||
|  |     for (let i = 0; i < coinbaseTags.length; ++i) { | ||||||
|  |       poolsDuplicated.push({ | ||||||
|  |         'name': (<Pool>coinbaseTags[i][1]).name, | ||||||
|  |         'link': (<Pool>coinbaseTags[i][1]).link, | ||||||
|  |         'regexes': [coinbaseTags[i][0]], | ||||||
|  |         'addresses': [], | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     logger.info('Parse payout_addresses'); | ||||||
|  |     const addressesTags = Object.entries(poolsJson['payout_addresses']); | ||||||
|  |     for (let i = 0; i < addressesTags.length; ++i) { | ||||||
|  |       poolsDuplicated.push({ | ||||||
|  |         'name': (<Pool>addressesTags[i][1]).name, | ||||||
|  |         'link': (<Pool>addressesTags[i][1]).link, | ||||||
|  |         'regexes': [], | ||||||
|  |         'addresses': [addressesTags[i][0]], | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Then, we find unique mining pool names
 | ||||||
|  |     logger.info('Identify unique mining pools'); | ||||||
|  |     let poolNames : string[] = []; | ||||||
|  |     for (let i = 0; i < poolsDuplicated.length; ++i) { | ||||||
|  |       if (poolNames.indexOf(poolsDuplicated[i].name) === -1) { | ||||||
|  |         poolNames.push(poolsDuplicated[i].name); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     logger.info(`Found ${poolNames.length} unique mining pools`); | ||||||
|  | 
 | ||||||
|  |     // Finally, we generate the final consolidated pools data
 | ||||||
|  |     let finalPoolData: Pool[] = []; | ||||||
|  |     for (let i = 0; i < poolNames.length; ++i) { | ||||||
|  |       let allAddresses: string[] = []; | ||||||
|  |       let allRegexes: string[] = []; | ||||||
|  |       let match = poolsDuplicated.filter((pool: Pool) => pool.name === poolNames[i]); | ||||||
|  | 
 | ||||||
|  |       for (let y = 0; y < match.length; ++y) { | ||||||
|  |         allAddresses = allAddresses.concat(match[y].addresses); | ||||||
|  |         allRegexes = allRegexes.concat(match[y].regexes); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       finalPoolData.push({ | ||||||
|  |         'name': poolNames[i].replace("'", "''"), | ||||||
|  |         'link': match[0].link, | ||||||
|  |         'regexes': allRegexes, | ||||||
|  |         'addresses': allAddresses, | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Manually add the 'unknown pool'
 | ||||||
|  |     finalPoolData.push({ | ||||||
|  |       'name': 'Unknown', | ||||||
|  |       'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction', | ||||||
|  |       regexes: [], | ||||||
|  |       addresses: [], | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     // Dump everything into the database
 | ||||||
|  |     logger.info(`Insert mining pool info into the database`); | ||||||
|  |     let query: string = 'INSERT INTO pools(name, link, regexes, addresses) VALUES '; | ||||||
|  |     for (let i = 0; i < finalPoolData.length; ++i) { | ||||||
|  |       query += `('${finalPoolData[i].name}', '${finalPoolData[i].link}',
 | ||||||
|  |         '${JSON.stringify(finalPoolData[i].regexes)}', '${JSON.stringify(finalPoolData[i].addresses)}'),`;
 | ||||||
|  |     } | ||||||
|  |     query = query.slice(0, -1) + ';'; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       await connection.query<any>({ sql: 'DELETE FROM pools;', timeout: 120000 }); // We clear the table before insertion
 | ||||||
|  |       await connection.query<any>({ sql: query, timeout: 120000 }); | ||||||
|  |       connection.release(); | ||||||
|  |       logger.info('Import completed'); | ||||||
|  |     } catch (e) { | ||||||
|  |       connection.release(); | ||||||
|  |       logger.info(`Unable to import pools in the database!`); | ||||||
|  |       throw e; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default new PoolsParser(); | ||||||
| @ -22,6 +22,7 @@ import loadingIndicators from './api/loading-indicators'; | |||||||
| import mempool from './api/mempool'; | import mempool from './api/mempool'; | ||||||
| import elementsParser from './api/liquid/elements-parser'; | import elementsParser from './api/liquid/elements-parser'; | ||||||
| import databaseMigration from './api/database-migration'; | import databaseMigration from './api/database-migration'; | ||||||
|  | import poolsParser from './api/pools-parser'; | ||||||
| import syncAssets from './sync-assets'; | import syncAssets from './sync-assets'; | ||||||
| import icons from './api/liquid/icons'; | import icons from './api/liquid/icons'; | ||||||
| import { Common } from './api/common'; | import { Common } from './api/common'; | ||||||
| @ -88,6 +89,7 @@ class Server { | |||||||
|       await checkDbConnection(); |       await checkDbConnection(); | ||||||
|       try { |       try { | ||||||
|         await databaseMigration.$initializeOrMigrateDatabase(); |         await databaseMigration.$initializeOrMigrateDatabase(); | ||||||
|  |         await poolsParser.migratePoolsJson(); | ||||||
|       } catch (e) { |       } catch (e) { | ||||||
|         throw new Error(e instanceof Error ? e.message : 'Error'); |         throw new Error(e instanceof Error ? e.message : 'Error'); | ||||||
|       } |       } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user