From fcca9113774218a2cc7d8fce02ee5674442db6a6 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 17 Jan 2022 13:33:07 +0900 Subject: [PATCH 01/65] Move difficulty adjustment code to separate module --- frontend/src/app/app.module.ts | 2 + .../difficulty/difficulty.component.html | 78 +++++++++ .../difficulty/difficulty.component.scss | 150 ++++++++++++++++++ .../difficulty/difficulty.component.ts | 111 +++++++++++++ .../app/dashboard/dashboard.component.html | 85 +--------- .../app/dashboard/dashboard.component.scss | 78 --------- .../src/app/dashboard/dashboard.component.ts | 90 ----------- 7 files changed, 343 insertions(+), 251 deletions(-) create mode 100644 frontend/src/app/components/difficulty/difficulty.component.html create mode 100644 frontend/src/app/components/difficulty/difficulty.component.scss create mode 100644 frontend/src/app/components/difficulty/difficulty.component.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index fd7ab3a3d..3e2c40b25 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -46,6 +46,7 @@ import { SharedModule } from './shared/shared.module'; import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; import { FeesBoxComponent } from './components/fees-box/fees-box.component'; import { DashboardComponent } from './dashboard/dashboard.component'; +import { DifficultyComponent } from './components/difficulty/difficulty.component'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle, faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons'; @@ -97,6 +98,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; StatusViewComponent, FeesBoxComponent, DashboardComponent, + DifficultyComponent, ApiDocsComponent, CodeTemplateComponent, TermsOfServiceComponent, diff --git a/frontend/src/app/components/difficulty/difficulty.component.html b/frontend/src/app/components/difficulty/difficulty.component.html new file mode 100644 index 000000000..5064c1c08 --- /dev/null +++ b/frontend/src/app/components/difficulty/difficulty.component.html @@ -0,0 +1,78 @@ +
Difficulty Adjustment
+
+
+
+
+
+
Remaining
+
+ + {{ i }} blocks + {{ i }} block +
+
+
+
+
Estimate
+
+ + + + + + + {{ epochData.change | absolute | number: '1.2-2' }} + % +
+ +
+
+
+ Previous: + + + + + + + + {{ epochData.previousRetarget | absolute | number: '1.2-2' }} % +
+
+
+
Current Period
+
{{ epochData.progress | number: '1.2-2' }} %
+
+
 
+
+
+
+
+
+
+ + +
+
+
Remaining
+
+
+
+
+
+
+
Estimate
+
+
+
+
+
+
+
Current Period
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/difficulty/difficulty.component.scss b/frontend/src/app/components/difficulty/difficulty.component.scss new file mode 100644 index 000000000..f66e2c8e5 --- /dev/null +++ b/frontend/src/app/components/difficulty/difficulty.component.scss @@ -0,0 +1,150 @@ +.difficulty-adjustment-container { + display: flex; + flex-direction: row; + justify-content: space-around; + height: 76px; + .shared-block { + color: #ffffff66; + font-size: 12px; + } + .item { + padding: 0 5px; + width: 100%; + &:nth-child(1) { + display: none; + @media (min-width: 485px) { + display: table-cell; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: table-cell; + } + } + } + .card-text { + font-size: 22px; + margin-top: -9px; + position: relative; + } +} + + +.difficulty-skeleton { + display: flex; + justify-content: space-between; + @media (min-width: 376px) { + flex-direction: row; + } + .item { + max-width: 150px; + margin: 0; + width: -webkit-fill-available; + @media (min-width: 376px) { + margin: 0 auto 0px; + } + &:first-child{ + display: none; + @media (min-width: 485px) { + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + &:last-child { + margin-bottom: 0; + } + } + .card-text { + .skeleton-loader { + width: 100%; + display: block; + &:first-child { + margin: 14px auto 0; + max-width: 80px; + } + &:last-child { + margin: 10px auto 0; + max-width: 120px; + } + } + } +} + +.card { + background-color: #1d1f31; + height: 100%; +} + +.card-title { + color: #4a68b9; + font-size: 1rem; +} + +.progress { + display: inline-flex; + width: 100%; + background-color: #2d3348; + height: 1.1rem; + max-width: 180px; +} + +.skeleton-loader { + max-width: 100%; +} + +.more-padding { + padding: 18px; +} + +.small-bar { + height: 8px; + top: -4px; + max-width: 120px; +} + +.loading-container { + min-height: 76px; +} + +.main-title { + position: relative; + color: #ffffff91; + margin-top: -13px; + font-size: 10px; + text-transform: uppercase; + font-weight: 500; + text-align: center; + padding-bottom: 3px; +} + +.card-wrapper { + .card { + height: auto !important; + } + .card-body { + display: flex; + flex: inherit; + text-align: center; + flex-direction: column; + justify-content: space-around; + padding: 22px 20px; + } +} + +.retarget-sign { + margin-right: -3px; + font-size: 14px; + top: -2px; + position: relative; +} + +.previous-retarget-sign { + margin-right: -2px; + font-size: 10px; +} diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts new file mode 100644 index 000000000..312c1b2d0 --- /dev/null +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -0,0 +1,111 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { combineLatest, Observable, timer } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { StateService } from '../..//services/state.service'; + +interface EpochProgress { + base: string; + change: number; + progress: string; + remainingBlocks: number; + newDifficultyHeight: number; + colorAdjustments: string; + colorPreviousAdjustments: string; + timeAvg: string; + remainingTime: number; + previousRetarget: number; +} + +@Component({ + selector: 'app-difficulty', + templateUrl: './difficulty.component.html', + styleUrls: ['./difficulty.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DifficultyComponent implements OnInit { + isLoadingWebSocket$: Observable; + difficultyEpoch$: Observable; + + constructor( + public stateService: StateService, + ) { } + + ngOnInit(): void { + this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; + this.difficultyEpoch$ = timer(0, 1000) + .pipe( + switchMap(() => combineLatest([ + this.stateService.blocks$.pipe(map(([block]) => block)), + this.stateService.lastDifficultyAdjustment$, + this.stateService.previousRetarget$ + ])), + map(([block, DATime, previousRetarget]) => { + const now = new Date().getTime() / 1000; + const diff = now - DATime; + const blocksInEpoch = block.height % 2016; + const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`; + const remainingBlocks = 2016 - blocksInEpoch; + const newDifficultyHeight = block.height + remainingBlocks; + + let change = 0; + if (remainingBlocks < 1870) { + if (blocksInEpoch > 0) { + change = (600 / (diff / blocksInEpoch ) - 1) * 100; + } + if (change > 300) { + change = 300; + } + if (change < -75) { + change = -75; + } + } + + const timeAvgDiff = change * 0.1; + + let timeAvgMins = 10; + if (timeAvgDiff > 0) { + timeAvgMins -= Math.abs(timeAvgDiff); + } else { + timeAvgMins += Math.abs(timeAvgDiff); + } + + const timeAvg = timeAvgMins.toFixed(0); + const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000); + + let colorAdjustments = '#ffffff66'; + if (change > 0) { + colorAdjustments = '#3bcc49'; + } + if (change < 0) { + colorAdjustments = '#dc3545'; + } + + let colorPreviousAdjustments = '#dc3545'; + if (previousRetarget) { + if (previousRetarget >= 0) { + colorPreviousAdjustments = '#3bcc49'; + } + if (previousRetarget === 0) { + colorPreviousAdjustments = '#ffffff66'; + } + } else { + colorPreviousAdjustments = '#ffffff66'; + } + + return { + base: `${progress}%`, + change, + progress, + remainingBlocks, + timeAvg, + colorAdjustments, + colorPreviousAdjustments, + blocksInEpoch, + newDifficultyHeight, + remainingTime, + previousRetarget, + }; + }) + ); + } +} diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 25cacb428..b3cbd5fa1 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -11,7 +11,7 @@
- +
@@ -38,7 +38,7 @@
- +
@@ -228,84 +228,3 @@ - - -
Difficulty Adjustment
-
-
-
-
-
-
Remaining
-
- - {{ i }} blocks - {{ i }} block -
-
-
-
-
Estimate
-
- - - - - - - {{ epochData.change | absolute | number: '1.2-2' }} - % -
- -
-
-
- Previous: - - - - - - - - {{ epochData.previousRetarget | absolute | number: '1.2-2' }} % -
-
-
-
Current Period
-
{{ epochData.progress | number: '1.2-2' }} %
-
-
 
-
-
-
-
-
-
-
- - -
-
-
Remaining
-
-
-
-
-
-
-
Estimate
-
-
-
-
-
-
-
Current Period
-
-
-
-
-
-
-
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 541bd2129..39ca2101a 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -243,84 +243,6 @@ max-width: 120px; } -.difficulty-adjustment-container { - display: flex; - flex-direction: row; - justify-content: space-around; - height: 76px; - .shared-block { - color: #ffffff66; - font-size: 12px; - } - .item { - padding: 0 5px; - width: 100%; - &:nth-child(1) { - display: none; - @media (min-width: 485px) { - display: table-cell; - } - @media (min-width: 768px) { - display: none; - } - @media (min-width: 992px) { - display: table-cell; - } - } - } - .card-text { - font-size: 22px; - margin-top: -9px; - position: relative; - } -} - - -.difficulty-skeleton { - display: flex; - justify-content: space-between; - @media (min-width: 376px) { - flex-direction: row; - } - .item { - max-width: 150px; - margin: 0; - width: -webkit-fill-available; - @media (min-width: 376px) { - margin: 0 auto 0px; - } - &:first-child{ - display: none; - @media (min-width: 485px) { - display: block; - } - @media (min-width: 768px) { - display: none; - } - @media (min-width: 992px) { - display: block; - } - } - &:last-child { - margin-bottom: 0; - } - } - .card-text { - .skeleton-loader { - width: 100%; - display: block; - &:first-child { - margin: 14px auto 0; - max-width: 80px; - } - &:last-child { - margin: 10px auto 0; - max-width: 120px; - } - } - } -} - .loading-container { min-height: 76px; } diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 3dbec5ce3..a582baba3 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -15,19 +15,6 @@ interface MempoolBlocksData { size: number; } -interface EpochProgress { - base: string; - change: number; - progress: string; - remainingBlocks: number; - newDifficultyHeight: number; - colorAdjustments: string; - colorPreviousAdjustments: string; - timeAvg: string; - remainingTime: number; - previousRetarget: number; -} - interface MempoolInfoData { memPoolInfo: MempoolInfo; vBytesPerSecond: number; @@ -51,7 +38,6 @@ export class DashboardComponent implements OnInit { network$: Observable; mempoolBlocksData$: Observable; mempoolInfoData$: Observable; - difficultyEpoch$: Observable; mempoolLoadingStatus$: Observable; vBytesPerSecondLimit = 1667; blocks$: Observable; @@ -126,82 +112,6 @@ export class DashboardComponent implements OnInit { }) ); - this.difficultyEpoch$ = timer(0, 1000) - .pipe( - switchMap(() => combineLatest([ - this.stateService.blocks$.pipe(map(([block]) => block)), - this.stateService.lastDifficultyAdjustment$, - this.stateService.previousRetarget$ - ])), - map(([block, DATime, previousRetarget]) => { - const now = new Date().getTime() / 1000; - const diff = now - DATime; - const blocksInEpoch = block.height % 2016; - const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`; - const remainingBlocks = 2016 - blocksInEpoch; - const newDifficultyHeight = block.height + remainingBlocks; - - let change = 0; - if (remainingBlocks < 1870) { - if (blocksInEpoch > 0) { - change = (600 / (diff / blocksInEpoch ) - 1) * 100; - } - if (change > 300) { - change = 300; - } - if (change < -75) { - change = -75; - } - } - - const timeAvgDiff = change * 0.1; - - let timeAvgMins = 10; - if (timeAvgDiff > 0) { - timeAvgMins -= Math.abs(timeAvgDiff); - } else { - timeAvgMins += Math.abs(timeAvgDiff); - } - - const timeAvg = timeAvgMins.toFixed(0); - const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000); - - let colorAdjustments = '#ffffff66'; - if (change > 0) { - colorAdjustments = '#3bcc49'; - } - if (change < 0) { - colorAdjustments = '#dc3545'; - } - - let colorPreviousAdjustments = '#dc3545'; - if (previousRetarget) { - if (previousRetarget >= 0) { - colorPreviousAdjustments = '#3bcc49'; - } - if (previousRetarget === 0) { - colorPreviousAdjustments = '#ffffff66'; - } - } else { - colorPreviousAdjustments = '#ffffff66'; - } - - return { - base: `${progress}%`, - change, - progress, - remainingBlocks, - timeAvg, - colorAdjustments, - colorPreviousAdjustments, - blocksInEpoch, - newDifficultyHeight, - remainingTime, - previousRetarget, - }; - }) - ); - this.mempoolBlocksData$ = this.stateService.mempoolBlocks$ .pipe( map((mempoolBlocks) => { From d4719245f56dc2ba47eae0ee47e3f7976824dfe3 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Tue, 18 Jan 2022 21:55:09 +0100 Subject: [PATCH 02/65] dont use hardcoded genesis coinbase and block hash fixes #1128 --- backend/src/api/bitcoin/bitcoin-api.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 79505d5c3..a2df267dc 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -21,11 +21,6 @@ class BitcoinApi implements AbstractBitcoinApi { return this.$addPrevouts(txInMempool); } - // Special case to fetch the Coinbase transaction - if (txId === '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b') { - return this.$returnCoinbaseTransaction(); - } - return this.bitcoindClient.getRawTransaction(txId, true) .then((transaction: IBitcoinApi.Transaction) => { if (skipConversion) { @@ -35,6 +30,11 @@ class BitcoinApi implements AbstractBitcoinApi { return transaction; } return this.$convertTransaction(transaction, addPrevout); + }) + .catch((e: Error) => { + if (e.message.startsWith('The genesis block coinbase')) { + return this.$returnCoinbaseTransaction(); + } }); } @@ -238,12 +238,14 @@ class BitcoinApi implements AbstractBitcoinApi { } protected $returnCoinbaseTransaction(): Promise { - return this.bitcoindClient.getBlock('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', 2) - .then((block: IBitcoinApi.Block) => { - return this.$convertTransaction(Object.assign(block.tx[0], { - confirmations: blocks.getCurrentBlockHeight() + 1, - blocktime: 1231006505 }), false); - }); + return this.bitcoindClient.getBlockHash(0).then((hash: string) => + this.bitcoindClient.getBlock(hash, 2) + .then((block: IBitcoinApi.Block) => { + return this.$convertTransaction(Object.assign(block.tx[0], { + confirmations: blocks.getCurrentBlockHeight() + 1, + blocktime: block.time }), false); + }) + ); } private $getMempoolEntry(txid: string): Promise { From fac40b1515c3681ff9ef62b5f91908070a836d33 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Wed, 19 Jan 2022 08:27:51 +0100 Subject: [PATCH 03/65] rethrow the error if it wasnt the genesis coinbase --- backend/src/api/bitcoin/bitcoin-api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index a2df267dc..b0a04116f 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -35,6 +35,7 @@ class BitcoinApi implements AbstractBitcoinApi { if (e.message.startsWith('The genesis block coinbase')) { return this.$returnCoinbaseTransaction(); } + throw e; }); } From 2848f56c2b975c39533294a6f7309224e4c3e1c2 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 19 Jan 2022 18:50:52 +0900 Subject: [PATCH 04/65] Import mining pools into the database - Increment db schema to 3 --- backend/src/api/database-migration.ts | 20 ++++- backend/src/api/pools-parser.ts | 118 ++++++++++++++++++++++++++ backend/src/index.ts | 2 + 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 backend/src/api/pools-parser.ts diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 2ac97636e..9fdd4d210 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -3,10 +3,10 @@ import config from '../config'; import { DB } from '../database'; 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 { - private static currentVersion = 2; + private static currentVersion = 3; private queryTimeout = 120000; private statisticsAddedIndexed = false; @@ -83,6 +83,9 @@ class DatabaseMigration { if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) { 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(); } catch (e) { connection.release(); @@ -335,6 +338,17 @@ class DatabaseMigration { final_tx int(11) NOT NULL ) 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(); \ No newline at end of file diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts new file mode 100644 index 000000000..c17336288 --- /dev/null +++ b/backend/src/api/pools-parser.ts @@ -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({ 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': (coinbaseTags[i][1]).name, + 'link': (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': (addressesTags[i][1]).name, + 'link': (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({ sql: 'DELETE FROM pools;', timeout: 120000 }); // We clear the table before insertion + await connection.query({ 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(); \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index f6615d1c8..9e4dcee35 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -22,6 +22,7 @@ import loadingIndicators from './api/loading-indicators'; import mempool from './api/mempool'; import elementsParser from './api/liquid/elements-parser'; import databaseMigration from './api/database-migration'; +import poolsParser from './api/pools-parser'; import syncAssets from './sync-assets'; import icons from './api/liquid/icons'; import { Common } from './api/common'; @@ -88,6 +89,7 @@ class Server { await checkDbConnection(); try { await databaseMigration.$initializeOrMigrateDatabase(); + await poolsParser.migratePoolsJson(); } catch (e) { throw new Error(e instanceof Error ? e.message : 'Error'); } From 979c52d3c413532411e0ca713333f12592e21c69 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 13:53:08 +0900 Subject: [PATCH 05/65] Add pools.json to EXTERNAL_ASSETS - Now supports updating the table --- backend/mempool-config.sample.json | 4 +- backend/src/api/pools-parser.ts | 94 +++++++++++++++++++----------- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index ea656c1de..1b55f38f4 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -14,7 +14,9 @@ "MEMPOOL_BLOCKS_AMOUNT": 8, "PRICE_FEED_UPDATE_INTERVAL": 3600, "USE_SECOND_NODE_FOR_MINFEE": false, - "EXTERNAL_ASSETS": [] + "EXTERNAL_ASSETS": [ + "https://mempool.space/resources/pools.json" + ] }, "CORE_RPC": { "HOST": "127.0.0.1", diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index c17336288..5e6a38e49 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -14,20 +14,14 @@ class PoolsParser { * Parse the pools.json file, consolidate the data and dump it into the database */ public async migratePoolsJson() { + const connection = await DB.pool.getConnection(); 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({ 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; - } + // Get existing pools from the db + const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); // We clear the table before insertion - logger.info('Open ../frontend/cypress/fixtures/pools.json'); - const fileContent: string = readFileSync('../frontend/cypress/fixtures/pools.json','utf8'); + logger.info('Open ./pools.json'); + const fileContent: string = readFileSync('./pools.json','utf8'); const poolsJson: object = JSON.parse(fileContent); // First we save every entries without paying attention to pool duplication @@ -65,7 +59,8 @@ class PoolsParser { logger.info(`Found ${poolNames.length} unique mining pools`); // Finally, we generate the final consolidated pools data - let finalPoolData: Pool[] = []; + let finalPoolDataAdd: Pool[] = []; + let finalPoolDataUpdate: Pool[] = []; for (let i = 0; i < poolNames.length; ++i) { let allAddresses: string[] = []; let allRegexes: string[] = []; @@ -76,34 +71,65 @@ class PoolsParser { allRegexes = allRegexes.concat(match[y].regexes); } - finalPoolData.push({ - 'name': poolNames[i].replace("'", "''"), - 'link': match[0].link, - 'regexes': allRegexes, - 'addresses': allAddresses, - }) + const finalPoolName = poolNames[i].replace("'", "''"); // To support single quote in names when doing db queries + + if (existingPools.find((pool) => { return pool.name === poolNames[i]}) !== undefined) { + logger.debug(`Update '${finalPoolName} mining pool`); + finalPoolDataUpdate.push({ + 'name': finalPoolName, + 'link': match[0].link, + 'regexes': allRegexes, + 'addresses': allAddresses, + }) + } else { + logger.debug(`Add '${finalPoolName} mining pool`); + finalPoolDataAdd.push({ + 'name': finalPoolName, + '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)}'),`; + if (existingPools.find((pool) => { return pool.name === "Uknown"}) !== undefined) { + finalPoolDataAdd.push({ + 'name': 'Unknown', + 'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction', + regexes: [], + addresses: [], + }) + } + + logger.info(`Update pools table now`); + + // Add new mining pools into the database + let queryAdd: string = 'INSERT INTO pools(name, link, regexes, addresses) VALUES '; + for (let i = 0; i < finalPoolDataAdd.length; ++i) { + queryAdd += `('${finalPoolDataAdd[i].name}', '${finalPoolDataAdd[i].link}', + '${JSON.stringify(finalPoolDataAdd[i].regexes)}', '${JSON.stringify(finalPoolDataAdd[i].addresses)}'),`; + } + queryAdd = queryAdd.slice(0, -1) + ';'; + + // Add new mining pools into the database + let updateQueries: string[] = []; + for (let i = 0; i < finalPoolDataUpdate.length; ++i) { + updateQueries.push(` + UPDATE pools + SET name='${finalPoolDataUpdate[i].name}', link='${finalPoolDataUpdate[i].link}', + regexes='${JSON.stringify(finalPoolDataUpdate[i].regexes)}', addresses='${JSON.stringify(finalPoolDataUpdate[i].addresses)}' + WHERE name='${finalPoolDataUpdate[i].name}' + ;`); } - query = query.slice(0, -1) + ';'; try { - await connection.query({ sql: 'DELETE FROM pools;', timeout: 120000 }); // We clear the table before insertion - await connection.query({ sql: query, timeout: 120000 }); + if (finalPoolDataAdd.length > 0) { + await connection.query({ sql: queryAdd, timeout: 120000 }); + } + updateQueries.forEach(async query => { + await connection.query({ sql: query, timeout: 120000 }); + }); connection.release(); logger.info('Import completed'); } catch (e) { From 1210643e8ec9aaaa6952f000bdf9c82bdc5dc0e1 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 16:34:14 +0900 Subject: [PATCH 06/65] Fix linter issues and typo --- backend/src/api/pools-parser.ts | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index 5e6a38e49..da048f2d3 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -3,10 +3,10 @@ import { DB } from '../database'; import logger from '../logger'; interface Pool { - name: string, - link: string, - regexes: string[], - addresses: string[], + name: string; + link: string; + regexes: string[]; + addresses: string[]; } class PoolsParser { @@ -18,14 +18,14 @@ class PoolsParser { logger.info('Importing pools.json to the database'); // Get existing pools from the db - const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); // We clear the table before insertion + const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); logger.info('Open ./pools.json'); - const fileContent: string = readFileSync('./pools.json','utf8'); + const fileContent: string = readFileSync('./pools.json', 'utf8'); const poolsJson: object = JSON.parse(fileContent); // First we save every entries without paying attention to pool duplication - let poolsDuplicated: Pool[] = []; + const poolsDuplicated: Pool[] = []; logger.info('Parse coinbase_tags'); const coinbaseTags = Object.entries(poolsJson['coinbase_tags']); @@ -50,7 +50,7 @@ class PoolsParser { // Then, we find unique mining pool names logger.info('Identify unique mining pools'); - let poolNames : string[] = []; + const poolNames: string[] = []; for (let i = 0; i < poolsDuplicated.length; ++i) { if (poolNames.indexOf(poolsDuplicated[i].name) === -1) { poolNames.push(poolsDuplicated[i].name); @@ -59,47 +59,47 @@ class PoolsParser { logger.info(`Found ${poolNames.length} unique mining pools`); // Finally, we generate the final consolidated pools data - let finalPoolDataAdd: Pool[] = []; - let finalPoolDataUpdate: Pool[] = []; + const finalPoolDataAdd: Pool[] = []; + const finalPoolDataUpdate: 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]); + const 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); } - const finalPoolName = poolNames[i].replace("'", "''"); // To support single quote in names when doing db queries + const finalPoolName = poolNames[i].replace(`'`, `''`); // To support single quote in names when doing db queries - if (existingPools.find((pool) => { return pool.name === poolNames[i]}) !== undefined) { - logger.debug(`Update '${finalPoolName} mining pool`); + if (existingPools.find((pool) => pool.name === poolNames[i]) !== undefined) { + logger.debug(`Update '${finalPoolName}' mining pool`); finalPoolDataUpdate.push({ 'name': finalPoolName, 'link': match[0].link, 'regexes': allRegexes, 'addresses': allAddresses, - }) + }); } else { - logger.debug(`Add '${finalPoolName} mining pool`); + logger.debug(`Add '${finalPoolName}' mining pool`); finalPoolDataAdd.push({ 'name': finalPoolName, 'link': match[0].link, 'regexes': allRegexes, 'addresses': allAddresses, - }) + }); } } // Manually add the 'unknown pool' - if (existingPools.find((pool) => { return pool.name === "Uknown"}) !== undefined) { + if (existingPools.find((pool) => pool.name === 'Unknown') !== undefined) { finalPoolDataAdd.push({ 'name': 'Unknown', 'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction', regexes: [], addresses: [], - }) + }); } logger.info(`Update pools table now`); @@ -113,7 +113,7 @@ class PoolsParser { queryAdd = queryAdd.slice(0, -1) + ';'; // Add new mining pools into the database - let updateQueries: string[] = []; + const updateQueries: string[] = []; for (let i = 0; i < finalPoolDataUpdate.length; ++i) { updateQueries.push(` UPDATE pools @@ -141,4 +141,4 @@ class PoolsParser { } -export default new PoolsParser(); \ No newline at end of file +export default new PoolsParser(); From 8d1cc40459679d10f0ce12b44a3452fc2208d827 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 16:56:25 +0900 Subject: [PATCH 07/65] Fix add 'Unknown' pool logic --- backend/src/api/pools-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index da048f2d3..9bfcd3366 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -93,7 +93,7 @@ class PoolsParser { } // Manually add the 'unknown pool' - if (existingPools.find((pool) => pool.name === 'Unknown') !== undefined) { + if (existingPools.find((pool) => pool.name === 'Unknown') === undefined) { finalPoolDataAdd.push({ 'name': 'Unknown', 'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction', From 19a564062bed4ced0a5e407a744218941ca5c77d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 22:59:10 +0900 Subject: [PATCH 08/65] Add pools.json file in default config.ts - Handle file exception - Only import pools for MAINNET --- backend/src/api/pools-parser.ts | 82 ++++++++++++++++++++------------- backend/src/config.ts | 4 +- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index 9bfcd3366..bb7779089 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -1,6 +1,7 @@ -import {readFileSync} from 'fs'; +import { readFileSync } from 'fs'; import { DB } from '../database'; import logger from '../logger'; +import config from '../config'; interface Pool { name: string; @@ -14,20 +15,26 @@ class PoolsParser { * Parse the pools.json file, consolidate the data and dump it into the database */ public async migratePoolsJson() { - const connection = await DB.pool.getConnection(); - logger.info('Importing pools.json to the database'); + if (config.MEMPOOL.NETWORK !== 'mainnet') { + return; + } - // Get existing pools from the db - const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); + logger.debug('Importing pools.json to the database, open ./pools.json'); - logger.info('Open ./pools.json'); - const fileContent: string = readFileSync('./pools.json', 'utf8'); - const poolsJson: object = JSON.parse(fileContent); + let poolsJson: object = {}; + try { + const fileContent: string = readFileSync('./pools.json', 'utf8'); + poolsJson = JSON.parse(fileContent); + } catch (e) { + logger.err('Unable to open ./pools.json, does the file exist?'); + await this.insertUnknownPool(); + return; + } // First we save every entries without paying attention to pool duplication const poolsDuplicated: Pool[] = []; - logger.info('Parse coinbase_tags'); + logger.debug('Parse coinbase_tags'); const coinbaseTags = Object.entries(poolsJson['coinbase_tags']); for (let i = 0; i < coinbaseTags.length; ++i) { poolsDuplicated.push({ @@ -37,7 +44,7 @@ class PoolsParser { 'addresses': [], }); } - logger.info('Parse payout_addresses'); + logger.debug('Parse payout_addresses'); const addressesTags = Object.entries(poolsJson['payout_addresses']); for (let i = 0; i < addressesTags.length; ++i) { poolsDuplicated.push({ @@ -49,14 +56,18 @@ class PoolsParser { } // Then, we find unique mining pool names - logger.info('Identify unique mining pools'); + logger.debug('Identify unique mining pools'); const 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`); + logger.debug(`Found ${poolNames.length} unique mining pools`); + + // Get existing pools from the db + const connection = await DB.pool.getConnection(); + const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); // Finally, we generate the final consolidated pools data const finalPoolDataAdd: Pool[] = []; @@ -92,23 +103,13 @@ class PoolsParser { } } - // Manually add the 'unknown pool' - if (existingPools.find((pool) => pool.name === 'Unknown') === undefined) { - finalPoolDataAdd.push({ - 'name': 'Unknown', - 'link': 'https://learnmeabitcoin.com/technical/coinbase-transaction', - regexes: [], - addresses: [], - }); - } - - logger.info(`Update pools table now`); + logger.debug(`Update pools table now`); // Add new mining pools into the database let queryAdd: string = 'INSERT INTO pools(name, link, regexes, addresses) VALUES '; for (let i = 0; i < finalPoolDataAdd.length; ++i) { queryAdd += `('${finalPoolDataAdd[i].name}', '${finalPoolDataAdd[i].link}', - '${JSON.stringify(finalPoolDataAdd[i].regexes)}', '${JSON.stringify(finalPoolDataAdd[i].addresses)}'),`; + '${JSON.stringify(finalPoolDataAdd[i].regexes)}', '${JSON.stringify(finalPoolDataAdd[i].addresses)}'),`; } queryAdd = queryAdd.slice(0, -1) + ';'; @@ -116,11 +117,11 @@ class PoolsParser { const updateQueries: string[] = []; for (let i = 0; i < finalPoolDataUpdate.length; ++i) { updateQueries.push(` - UPDATE pools - SET name='${finalPoolDataUpdate[i].name}', link='${finalPoolDataUpdate[i].link}', - regexes='${JSON.stringify(finalPoolDataUpdate[i].regexes)}', addresses='${JSON.stringify(finalPoolDataUpdate[i].addresses)}' - WHERE name='${finalPoolDataUpdate[i].name}' - ;`); + UPDATE pools + SET name='${finalPoolDataUpdate[i].name}', link='${finalPoolDataUpdate[i].link}', + regexes='${JSON.stringify(finalPoolDataUpdate[i].regexes)}', addresses='${JSON.stringify(finalPoolDataUpdate[i].addresses)}' + WHERE name='${finalPoolDataUpdate[i].name}' + ;`); } try { @@ -130,15 +131,34 @@ class PoolsParser { updateQueries.forEach(async query => { await connection.query({ sql: query, timeout: 120000 }); }); + await this.insertUnknownPool(); connection.release(); - logger.info('Import completed'); + logger.info('Mining pools.json import completed'); } catch (e) { connection.release(); - logger.info(`Unable to import pools in the database!`); + logger.err(`Unable to import pools in the database!`); throw e; } } + /** + * Manually add the 'unknown pool' + */ + private async insertUnknownPool() { + const connection = await DB.pool.getConnection(); + try { + const [rows]: any[] = await connection.query({ sql: 'SELECT name from pools where name="Unknown"', timeout: 120000 }); + if (rows.length === 0) { + logger.debug('Manually inserting "Unknown" mining pool into the databse'); + await connection.query({ + sql: `INSERT INTO pools(name, link, regexes, addresses) + VALUES("Unknown", "https://learnmeabitcoin.com/technical/coinbase-transaction", "[]", "[]"); + `}); + } + } catch (e) { + logger.err('Unable to insert "Unknown" mining pool'); + } + } } export default new PoolsParser(); diff --git a/backend/src/config.ts b/backend/src/config.ts index 4c2888834..3cc928327 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -79,7 +79,9 @@ const defaults: IConfig = { 'MEMPOOL_BLOCKS_AMOUNT': 8, 'PRICE_FEED_UPDATE_INTERVAL': 3600, 'USE_SECOND_NODE_FOR_MINFEE': false, - 'EXTERNAL_ASSETS': [], + 'EXTERNAL_ASSETS': [ + 'https://mempool.space/resources/pools.json' + ] }, 'ESPLORA': { 'REST_API_URL': 'http://127.0.0.1:3000', From a1a2e9363fc7137980ba37a2e80889539a50cd4a Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 23:07:20 +0900 Subject: [PATCH 09/65] Make sure to release all db connections --- backend/src/api/pools-parser.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index bb7779089..2b0901551 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -67,7 +67,14 @@ class PoolsParser { // Get existing pools from the db const connection = await DB.pool.getConnection(); - const [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); + let existingPools: any[] = []; + try { + existingPools = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); + } catch (e) { + logger.err('Unable to get existing pools from the database, skipping pools.json import'); + connection.release(); + return; + } // Finally, we generate the final consolidated pools data const finalPoolDataAdd: Pool[] = []; @@ -117,11 +124,11 @@ class PoolsParser { const updateQueries: string[] = []; for (let i = 0; i < finalPoolDataUpdate.length; ++i) { updateQueries.push(` - UPDATE pools - SET name='${finalPoolDataUpdate[i].name}', link='${finalPoolDataUpdate[i].link}', - regexes='${JSON.stringify(finalPoolDataUpdate[i].regexes)}', addresses='${JSON.stringify(finalPoolDataUpdate[i].addresses)}' - WHERE name='${finalPoolDataUpdate[i].name}' - ;`); + UPDATE pools + SET name='${finalPoolDataUpdate[i].name}', link='${finalPoolDataUpdate[i].link}', + regexes='${JSON.stringify(finalPoolDataUpdate[i].regexes)}', addresses='${JSON.stringify(finalPoolDataUpdate[i].addresses)}' + WHERE name='${finalPoolDataUpdate[i].name}' + ;`); } try { @@ -158,6 +165,8 @@ class PoolsParser { } catch (e) { logger.err('Unable to insert "Unknown" mining pool'); } + + connection.release(); } } From 87175869dd741ca366eddce5bdcc36928157e552 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 20 Jan 2022 23:31:32 +0900 Subject: [PATCH 10/65] Fix typescript miss use --- backend/src/api/pools-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index 2b0901551..e3f5f0b89 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -67,9 +67,9 @@ class PoolsParser { // Get existing pools from the db const connection = await DB.pool.getConnection(); - let existingPools: any[] = []; + let existingPools; try { - existingPools = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); + [existingPools] = await connection.query({ sql: 'SELECT * FROM pools;', timeout: 120000 }); } catch (e) { logger.err('Unable to get existing pools from the database, skipping pools.json import'); connection.release(); From a2b167fc07c26cae84203110615dfa06ab035ae5 Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 20 Jan 2022 19:47:18 +0400 Subject: [PATCH 11/65] Display Liquid asset icons --- .../src/app/components/asset/asset.component.html | 14 +++++++------- .../src/app/components/asset/asset.component.scss | 14 ++++++++++++++ .../src/app/components/asset/asset.component.ts | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/components/asset/asset.component.html b/frontend/src/app/components/asset/asset.component.html index c79e260b6..dba70dc5d 100644 --- a/frontend/src/app/components/asset/asset.component.html +++ b/frontend/src/app/components/asset/asset.component.html @@ -35,13 +35,6 @@ Issuance TX {{ asset.issuance_txin.txid | shortenString : 13 }} - - -
-
-
- - @@ -69,6 +62,13 @@
Pegged in {{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}
+
+
+ + + + +
diff --git a/frontend/src/app/components/asset/asset.component.scss b/frontend/src/app/components/asset/asset.component.scss index 270ad97e3..3fe93c9d2 100644 --- a/frontend/src/app/components/asset/asset.component.scss +++ b/frontend/src/app/components/asset/asset.component.scss @@ -50,3 +50,17 @@ h1 { } } +.assetIcon { + max-height: 150px; + margin: 25px; + @media (min-width: 768px) { + max-height: 300px; + margin: 0; + } +} + +.icon-holder { + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/frontend/src/app/components/asset/asset.component.ts b/frontend/src/app/components/asset/asset.component.ts index 74b074d97..ecb216052 100644 --- a/frontend/src/app/components/asset/asset.component.ts +++ b/frontend/src/app/components/asset/asset.component.ts @@ -32,6 +32,7 @@ export class AssetComponent implements OnInit, OnDestroy { isNativeAsset = false; error: any; mainSubscription: Subscription; + imageError = false; totalConfirmedTxCount = 0; loadedConfirmedTxCount = 0; From 88a9e22abebd1d94541868835e4bad1b2339ca2c Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 20 Jan 2022 22:56:49 +0400 Subject: [PATCH 12/65] Liquid asset loading fixes --- frontend/src/app/assets/assets.component.html | 2 +- .../app/components/asset/asset.component.html | 53 +++++++++++-------- .../app/components/asset/asset.component.scss | 15 ++++-- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/assets/assets.component.html b/frontend/src/app/assets/assets.component.html index 5d5118af0..c8962cd15 100644 --- a/frontend/src/app/assets/assets.component.html +++ b/frontend/src/app/assets/assets.component.html @@ -43,7 +43,7 @@ Name Ticker - Issuer domain + Issuer domain Asset ID diff --git a/frontend/src/app/components/asset/asset.component.html b/frontend/src/app/components/asset/asset.component.html index dba70dc5d..c0a2bfbf5 100644 --- a/frontend/src/app/components/asset/asset.component.html +++ b/frontend/src/app/components/asset/asset.component.html @@ -66,7 +66,7 @@
- +
@@ -109,28 +109,39 @@ - -
- - - - - - - - - - - - -
-
-
-
- - +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
diff --git a/frontend/src/app/components/asset/asset.component.scss b/frontend/src/app/components/asset/asset.component.scss index 3fe93c9d2..bb2b7f783 100644 --- a/frontend/src/app/components/asset/asset.component.scss +++ b/frontend/src/app/components/asset/asset.component.scss @@ -51,10 +51,10 @@ h1 { } .assetIcon { - max-height: 150px; + height: 150px; margin: 25px; @media (min-width: 768px) { - max-height: 300px; + height: 250px; margin: 0; } } @@ -63,4 +63,13 @@ h1 { display: flex; justify-content: center; align-items: center; -} \ No newline at end of file +} + +.defaultIcon { + margin: 25px; + height: 150px; +} + +.defaultIcon.skeleton { + opacity: 0.5; +} From 35e69f2e3d116ea746e7c644239fa92f0d0411d4 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 11:27:50 -0800 Subject: [PATCH 13/65] Fix Liquid proxy settings --- frontend/proxy.conf.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js index faae04499..4a0489c77 100644 --- a/frontend/proxy.conf.js +++ b/frontend/proxy.conf.js @@ -61,10 +61,7 @@ PROXY_CONFIG = [ }, { context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'], - target: "https://liquid.network/testnet", - pathRewrite: { - "^/api/liquidtestnet/": "/liquidtestnet/api" - }, + target: "https://liquid.network", ws: true, secure: false, changeOrigin: true @@ -73,7 +70,9 @@ PROXY_CONFIG = [ if (configContent && configContent.BASE_MODULE == "liquid") { PROXY_CONFIG.push({ - context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'], + context: ['/resources/pools.json', + '/resources/assets.json', '/resources/assets.minimal.json', + '/resources/assets-testnet.json', '/resources/assets-testnet.minimal.json'], target: "https://liquid.network", secure: false, changeOrigin: true, From 36b4812e937d772ace6e2d768fdd03344cc96a15 Mon Sep 17 00:00:00 2001 From: softsimon Date: Fri, 21 Jan 2022 00:16:18 +0400 Subject: [PATCH 14/65] Asset name overflow fix --- frontend/src/app/components/asset/asset.component.html | 2 +- frontend/src/app/components/asset/asset.component.scss | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/asset/asset.component.html b/frontend/src/app/components/asset/asset.component.html index c0a2bfbf5..9723a45e5 100644 --- a/frontend/src/app/components/asset/asset.component.html +++ b/frontend/src/app/components/asset/asset.component.html @@ -21,7 +21,7 @@ Name - {{ assetContract[2] }} ({{ assetContract[1] }}) + {{ assetContract[2] }} ({{ assetContract[1] }}) Precision diff --git a/frontend/src/app/components/asset/asset.component.scss b/frontend/src/app/components/asset/asset.component.scss index bb2b7f783..45e68042d 100644 --- a/frontend/src/app/components/asset/asset.component.scss +++ b/frontend/src/app/components/asset/asset.component.scss @@ -73,3 +73,8 @@ h1 { .defaultIcon.skeleton { opacity: 0.5; } + +.assetName { + word-break: break-word; + white-space: normal; +} From a8c04624f08f4550c2ae3b4e2c909936845ad7e6 Mon Sep 17 00:00:00 2001 From: softsimon Date: Fri, 21 Jan 2022 01:32:19 +0400 Subject: [PATCH 15/65] Fixing liqud asset precision fixes #1166 --- .../transactions-list/transactions-list.component.html | 2 +- .../transactions-list/transactions-list.component.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 36aed7fe6..680b6be53 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -270,7 +270,7 @@ - {{ item.value / 100000000 | number: '1.0-' + assetsMinimal[item.asset][3] }} {{ assetsMinimal[item.asset][1] }} + {{ item.value / pow(10, assetsMinimal[item.asset][3]) | number: '1.' + assetsMinimal[item.asset][3] + '-' + assetsMinimal[item.asset][3] }} {{ assetsMinimal[item.asset][1] }}
{{ assetsMinimal[item.asset][0] }}
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index ecddf4436..e1fb7d2e6 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -119,6 +119,10 @@ export class TransactionsListComponent implements OnInit, OnChanges { return '0x' + (str.length % 2 ? '0' : '') + str; } + pow(base: number, exponent: number): number { + return Math.pow(base, exponent); + } + toggleDetails() { this.displayDetails = !this.displayDetails; this.ref.markForCheck(); From 80e4141612de46f30687473d1245b1e775eb498c Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:27:52 -0800 Subject: [PATCH 16/65] Update Cypress to v9.3.1 --- frontend/package-lock.json | 60 ++++++++++++++++++++------------------ frontend/package.json | 2 +- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index befb9d3ac..9768eb6df 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -70,7 +70,7 @@ }, "optionalDependencies": { "@cypress/schematic": "^1.3.0", - "cypress": "^9.1.1", + "cypress": "^9.3.1", "cypress-fail-on-console-error": "^2.1.3", "cypress-wait-until": "^1.7.1", "mock-socket": "^9.0.3", @@ -3801,9 +3801,9 @@ } }, "node_modules/@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "optional": true }, "node_modules/@types/sizzle": { @@ -5746,17 +5746,18 @@ } }, "node_modules/cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", "optional": true, "dependencies": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", "string-width": "^4.2.0" }, "engines": { "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "1.4.0" } }, "node_modules/cli-truncate": { @@ -6786,25 +6787,26 @@ "devOptional": true }, "node_modules/cypress": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.1.1.tgz", - "integrity": "sha512-yWcYD8SEQ8F3okFbRPqSDj5V0xhrZBT5QRIH+P1J2vYvtEmZ4KGciHE7LCcZZLILOrs7pg4WNCqkj/XRvReQlQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "hasInstallScript": true, "optional": true, "dependencies": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.0", + "cli-table3": "~0.6.1", "commander": "^5.1.0", "common-tags": "^1.8.0", "dayjs": "^1.10.4", @@ -20861,9 +20863,9 @@ } }, "@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "optional": true }, "@types/sizzle": { @@ -22536,13 +22538,12 @@ "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==" }, "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", "optional": true, "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", + "colors": "1.4.0", "string-width": "^4.2.0" } }, @@ -23373,24 +23374,25 @@ "devOptional": true }, "cypress": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.1.1.tgz", - "integrity": "sha512-yWcYD8SEQ8F3okFbRPqSDj5V0xhrZBT5QRIH+P1J2vYvtEmZ4KGciHE7LCcZZLILOrs7pg4WNCqkj/XRvReQlQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "optional": true, "requires": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.0", + "cli-table3": "~0.6.1", "commander": "^5.1.0", "common-tags": "^1.8.0", "dayjs": "^1.10.4", diff --git a/frontend/package.json b/frontend/package.json index 44ca13e90..d23e7b3b7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -117,7 +117,7 @@ }, "optionalDependencies": { "@cypress/schematic": "^1.3.0", - "cypress": "^9.1.1", + "cypress": "^9.3.1", "cypress-fail-on-console-error": "^2.1.3", "cypress-wait-until": "^1.7.1", "mock-socket": "^9.0.3", From 9f2d0c5172969dad594b8445d850b5fae141b990 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:29:45 -0800 Subject: [PATCH 17/65] Update config script defaults --- frontend/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 44ca13e90..3debb384a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,9 +42,9 @@ "lint": "ng lint", "e2e": "npm run generate-config && ng e2e", "e2e:ci": "npm run cypress:run:ci", - "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool && npm run generate-config", - "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid && npm run generate-config", - "config:defaults:bisq": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=bisq && npm run generate-config", + "config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", + "config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true LIQUID_TESTNET_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid BLOCK_WEIGHT_UNITS=300000 && npm run generate-config", + "config:defaults:bisq": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=bisq BLOCK_WEIGHT_UNITS=4000000 && npm run generate-config", "dev:ssr": "npm run generate-config && ng run mempool:serve-ssr", "serve:ssr": "node server.run.js", "build:ssr": "npm run build && ng run mempool:server:production && ./node_modules/typescript/bin/tsc server.run.ts", From cf5cce23f341bf0aaf2a4cd7d77ca6e93242014b Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:30:03 -0800 Subject: [PATCH 18/65] Add Liquid Testnet tests --- .../liquidtestnet/liquidtestnet.spec.ts | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 frontend/cypress/integration/liquidtestnet/liquidtestnet.spec.ts diff --git a/frontend/cypress/integration/liquidtestnet/liquidtestnet.spec.ts b/frontend/cypress/integration/liquidtestnet/liquidtestnet.spec.ts new file mode 100644 index 000000000..bef477d4b --- /dev/null +++ b/frontend/cypress/integration/liquidtestnet/liquidtestnet.spec.ts @@ -0,0 +1,179 @@ +describe('Liquid Testnet', () => { + const baseModule = Cypress.env("BASE_MODULE"); + const basePath = '/testnet'; + + beforeEach(() => { + cy.intercept('/liquidtestnet/api/block/**').as('block'); + cy.intercept('/liquidtestnet/api/blocks/').as('blocks'); + cy.intercept('/liquidtestnet/api/tx/**/outspends').as('outspends'); + cy.intercept('/liquidtestnet/api/block/**/txs/**').as('block-txs'); + cy.intercept('/resources/pools.json').as('pools'); + + Cypress.Commands.add('waitForBlockData', () => { + cy.wait('@socket'); + cy.wait('@block'); + cy.wait('@outspends'); + }); + }); + + if (baseModule === 'liquid') { + + it('check first mempool block after skeleton loads', () => { + cy.visit(`${basePath}`); + cy.waitForSkeletonGone(); + cy.get('#mempool-block-0 > .blockLink').should('exist'); + }); + + it('loads the dashboard', () => { + cy.visit(`${basePath}`); + cy.waitForSkeletonGone(); + }); + + it('loads the blocks page', () => { + cy.visit(`${basePath}/blocks`); + cy.waitForSkeletonGone(); + }); + + it('loads a specific block page', () => { + cy.visit(`${basePath}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`); + cy.waitForSkeletonGone(); + }); + + it('loads the graphs page', () => { + cy.visit(`${basePath}/graphs`); + cy.waitForSkeletonGone(); + }); + + it('loads the tv page - desktop', () => { + cy.visit(`${basePath}`); + cy.waitForSkeletonGone(); + cy.get('li:nth-of-type(3) > a').click().then(() => { + cy.wait(1000); + }); + }); + + it('loads the graphs page - mobile', () => { + cy.visit(`${basePath}`) + cy.waitForSkeletonGone(); + cy.get('li:nth-of-type(3) > a').click().then(() => { + cy.viewport('iphone-6'); + cy.wait(1000); + cy.get('.tv-only').should('not.exist'); + }); + }); + + it.skip('renders unconfidential addresses correctly on mobile', () => { + cy.viewport('iphone-6'); + cy.visit(`${basePath}/address/__TODO__`); + cy.waitForSkeletonGone(); + //TODO: Add proper IDs for these selectors + const firstRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(1) > .table > tbody'; + const thirdRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(3)'; + cy.get(firstRowSelector).invoke('css', 'width').then(firstRowWidth => { + cy.get(thirdRowSelector).invoke('css', 'width').then(thirdRowWidth => { + expect(parseInt(firstRowWidth)).to.be.lessThan(parseInt(thirdRowWidth)); + }); + }); + }); + + describe('assets', () => { + it('shows the assets screen', () => { + cy.visit(`${basePath}/assets`); + cy.waitForSkeletonGone(); + cy.get('table tr').should('have.length.at.least', 5); + }); + + it('allows searching assets', () => { + cy.visit(`${basePath}/assets`); + cy.waitForSkeletonGone(); + cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => { + cy.get('table tr').should('have.length', 1); + }); + }); + + it('shows a specific asset ID', () => { + cy.visit(`${basePath}/assets`); + cy.waitForSkeletonGone(); + cy.get('.container-xl input').click().type('Liquid CAD').then(() => { + cy.get('table tr td:nth-of-type(1) a').click(); + }); + }); + }); + + describe('unblinded TX', () => { + it('should not show an unblinding error message for regular txs', () => { + cy.visit(`${basePath}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`); + cy.waitForSkeletonGone(); + cy.get('.error-unblinded' ).should('not.exist'); + }); + + it('show unblinded TX', () => { + cy.visit(`${basePath}/tx/c3d908ab77891e4c569b0df71aae90f4720b157019ebb20db176f4f9c4d626b8#blinded=100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,df290ead654d7d110ebc5aaf0bcf11d5b5d360431a467f1cde0a856fde986893,33cb3a2fd2e76643843691cf44a78c5cd28ec652a414da752160ad63fbd37bc9,49741,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,edb0713bcbfcb3daabf601cb50978439667d208e15fed8a5ebbfea5696cda1d5,4de70115501e8c7d6bd763e229bf42781edeacf6e75e1d7bdfa4c63104bc508a`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vin tr:nth-child(1) .amount').should('contain.text', '0.00100000 tL-BTC'); + cy.get('#table-tx-vin tr').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', '0.00050000 tL-BTC'); + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', '0.00049741 tL-BTC'); + cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); + }); + + it('show empty unblinded TX', () => { + cy.visit(`${basePath}/tx/c3d908ab77891e4c569b0df71aae90f4720b157019ebb20db176f4f9c4d626b8#blinded=`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vin tr:nth-child(1)').should('have.class', ''); + cy.get('#table-tx-vin tr:nth-child(1) .amount').should('contain.text', 'Confidential'); + cy.get('#table-tx-vout tr:nth-child(1)').should('have.class', ''); + cy.get('#table-tx-vout tr:nth-child(2)').should('have.class', ''); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', 'Confidential'); + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', 'Confidential'); + }); + + it('show invalid unblinded TX hex', () => { + cy.visit(`${basePath}/tx/2477f220eef1d03f8ffa4a2861c275d155c3562adf0d79523aeeb0c59ee611ba#blinded=5000`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vin tr').should('have.class', ''); + cy.get('#table-tx-vout tr').should('have.class', ''); + cy.get('.error-unblinded' ).contains('Error: Invalid blinding data (invalid hex)'); + }); + + it('show first unblinded vout', () => { + cy.visit(`${basePath}/tx/0877bc0c7aa5c2b8d0e4b15450425879b8783c40e341806037a605ef836fb886#blinded=5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,328de54e90e867a9154b4f1eb7fcab86267e880fa2ee9e53b41a91e61dab86e6,8885831e6b089eaf06889d53a24843f0da533d300a7b1527b136883a6819f3ae,5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,aca78b953615d69ae0ae68c4c5c3c0ee077c10bc20ad3f0c5960706004e6cb56,d2ec175afe5f761e2dbd443faf46abbb7091f341deb3387e5787d812bdb2df9f,100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,4b54a4ca809b3844f34dd88b68617c4c866d92a02211f02ba355755bac20a1c6,eddd02e92b0cfbad8cab89828570a50f2c643bb2a54d886c86e25ce47e818685,99729,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,8b86d565c9549eb0352bb81ee576d01d064435b64fddcc045decebeb1d9913ce,b082ce3448d40d47b5b39f15d72b285f4a1046b636b56c25f32f498ece29d062,10000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,62b04ee86198d6b41681cdd0acb450ab366af727a010aaee8ba0b9e69ff43896,3f98429bca9b538dc943c22111f25d9c4448d45a63ff0f4e58b22fd434c0365e`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vout tr:nth-child(1)').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', '0.00099729 tL-BTC'); + }); + + it('show second unblinded vout (asset)', () => { + cy.visit(`${basePath}/tx/0877bc0c7aa5c2b8d0e4b15450425879b8783c40e341806037a605ef836fb886#blinded=5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,328de54e90e867a9154b4f1eb7fcab86267e880fa2ee9e53b41a91e61dab86e6,8885831e6b089eaf06889d53a24843f0da533d300a7b1527b136883a6819f3ae,5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,aca78b953615d69ae0ae68c4c5c3c0ee077c10bc20ad3f0c5960706004e6cb56,d2ec175afe5f761e2dbd443faf46abbb7091f341deb3387e5787d812bdb2df9f,100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,4b54a4ca809b3844f34dd88b68617c4c866d92a02211f02ba355755bac20a1c6,eddd02e92b0cfbad8cab89828570a50f2c643bb2a54d886c86e25ce47e818685,99729,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,8b86d565c9549eb0352bb81ee576d01d064435b64fddcc045decebeb1d9913ce,b082ce3448d40d47b5b39f15d72b285f4a1046b636b56c25f32f498ece29d062,10000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,62b04ee86198d6b41681cdd0acb450ab366af727a010aaee8ba0b9e69ff43896,3f98429bca9b538dc943c22111f25d9c4448d45a63ff0f4e58b22fd434c0365e`); + cy.get('#table-tx-vout tr:nth-child(2)').should('have.class', 'assetBox'); + //TODO Update after the precision bug fix is merged + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', '0 TEST'); + }); + + it('should link to the asset page from the unblinded tx', () => { + cy.visit(`${basePath}/tx/0877bc0c7aa5c2b8d0e4b15450425879b8783c40e341806037a605ef836fb886#blinded=5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,328de54e90e867a9154b4f1eb7fcab86267e880fa2ee9e53b41a91e61dab86e6,8885831e6b089eaf06889d53a24843f0da533d300a7b1527b136883a6819f3ae,5000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,aca78b953615d69ae0ae68c4c5c3c0ee077c10bc20ad3f0c5960706004e6cb56,d2ec175afe5f761e2dbd443faf46abbb7091f341deb3387e5787d812bdb2df9f,100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,4b54a4ca809b3844f34dd88b68617c4c866d92a02211f02ba355755bac20a1c6,eddd02e92b0cfbad8cab89828570a50f2c643bb2a54d886c86e25ce47e818685,99729,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,8b86d565c9549eb0352bb81ee576d01d064435b64fddcc045decebeb1d9913ce,b082ce3448d40d47b5b39f15d72b285f4a1046b636b56c25f32f498ece29d062,10000,38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5,62b04ee86198d6b41681cdd0acb450ab366af727a010aaee8ba0b9e69ff43896,3f98429bca9b538dc943c22111f25d9c4448d45a63ff0f4e58b22fd434c0365e`); + cy.get('#table-tx-vout tr:nth-child(2) .amount a').click().then(() => { + cy.waitForSkeletonGone(); + cy.url().should('contain', '/asset/38fca2d939696061a8f76d4e6b5eecd54e3b4221c846f24a6b279e79952850a5'); + }); + }); + + it('show invalid error unblinded TX', () => { + cy.visit(`${basePath}/tx/c3d908ab77891e4c569b0df71aae90f4720b157019ebb20db176f4f9c4d626b8#blinded=100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,df290ead654d7d110ebc5aaf0bcf11d5b5d360431a467f1cde0a856fde986893,33cb3a2fd2e76643843691cf44a78c5cd28ec652a414da752160ad63fbd37bc9,49741,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,edb0713bcbfcb3daabf601cb50978439667d208e15fed8a5ebbfea5696cda1d5,4de70115501e8c7d6bd763e229bf42781edeacf6e75e1d7bdfa4c63104bc508c`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vin tr').should('have.class', 'assetBox'); + cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.'); + }); + + it('shows asset peg in/out and burn transactions', () => { + cy.visit(`${basePath}/asset/ac3e0ff248c5051ffd61e00155b7122e5ebc04fd397a0ecbdd4f4e4a56232926`); + cy.waitForSkeletonGone(); + cy.get('#table-tx-vout tr').not('.assetBox'); + cy.get('#table-tx-vin tr').not('.assetBox'); + }); + + }); + } else { + it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`); + } +}); From 6637477ac9ae817bbb494d4618ba133f638f94f6 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:30:22 -0800 Subject: [PATCH 19/65] Update Liquid tests --- .../cypress/integration/liquid/liquid.spec.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/cypress/integration/liquid/liquid.spec.ts b/frontend/cypress/integration/liquid/liquid.spec.ts index 5661340f4..b705655d3 100644 --- a/frontend/cypress/integration/liquid/liquid.spec.ts +++ b/frontend/cypress/integration/liquid/liquid.spec.ts @@ -146,15 +146,22 @@ describe('Liquid', () => { it('show unblinded TX', () => { cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`); cy.waitForSkeletonGone(); + cy.get('#table-tx-vin tr:nth-child(1) .amount').should('contain.text', '0.02465000 L-BTC'); cy.get('#table-tx-vin tr').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', '0.00100000 L-BTC'); + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', '0.02364760 L-BTC'); cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); }); it('show empty unblinded TX', () => { cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`); cy.waitForSkeletonGone(); - cy.get('#table-tx-vin tr').should('have.class', ''); - cy.get('#table-tx-vout tr').should('have.class', ''); + cy.get('#table-tx-vin tr:nth-child(1)').should('have.class', ''); + cy.get('#table-tx-vin tr:nth-child(1) .amount').should('contain.text', 'Confidential'); + cy.get('#table-tx-vout tr:nth-child(1)').should('have.class', ''); + cy.get('#table-tx-vout tr:nth-child(2)').should('have.class', ''); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', 'Confidential'); + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', 'Confidential'); }); it('show invalid unblinded TX hex', () => { @@ -168,12 +175,14 @@ describe('Liquid', () => { it('show first unblinded vout', () => { cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`); cy.waitForSkeletonGone(); - cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(1)').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(1) .amount').should('contain.text', '0.00100000 L-BTC'); }); it('show second unblinded vout', () => { cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`); - cy.get('#table-tx-vout tr').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(2').should('have.class', 'assetBox'); + cy.get('#table-tx-vout tr:nth-child(2) .amount').should('contain.text', '0.02364760 L-BTC'); }); it('show invalid error unblinded TX', () => { From c88518797148c1719cd530a5f7914cb1785e3b4c Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:31:14 -0800 Subject: [PATCH 20/65] Add an amount class vins and vouts to improve testing --- .../transactions-list/transactions-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 36aed7fe6..6eb262377 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -71,7 +71,7 @@ - +
@@ -170,7 +170,7 @@ - +
From 7bb95ff17708e3af68b203c091cb545dc6be0e8c Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Thu, 20 Jan 2022 21:43:13 -0800 Subject: [PATCH 21/65] Update Cypress GHA spec list --- .github/workflows/cypress.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 1b565e71a..832efcbbf 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -31,6 +31,10 @@ jobs: wait-on-timeout: 120 record: true parallel: true + spec: | + cypress/integration/mainnet/*.spec.ts + cypress/integration/signet/*.spec.ts + cypress/integration/testnet/*.spec.ts group: Tests on ${{ matrix.browser }} (Mempool) browser: ${{ matrix.browser }} ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}' @@ -50,7 +54,9 @@ jobs: wait-on-timeout: 120 record: true parallel: true - spec: cypress/integration/liquid/liquid.spec.ts + spec: | + cypress/integration/liquid/liquid.spec.ts + cypress/integration/liquidtestnet/liquidtestnet.spec.ts group: Tests on ${{ matrix.browser }} (Liquid) browser: ${{ matrix.browser }} ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}' From 056049615485831b27b4aec605566f37234b0751 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> Date: Fri, 21 Jan 2022 23:12:18 -0800 Subject: [PATCH 22/65] Fix broken link on the Bisq transaction page --- .../app/bisq/bisq-transaction/bisq-transaction.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html index 57862fed6..2b3964caa 100644 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html +++ b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.html @@ -7,7 +7,7 @@
- + {{ bisqTx.id | shortenString : 24 }} {{ bisqTx.id }} From a5ca0cda1427580bea1dc28c4f5750cf9e3f1d4e Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Sat, 22 Jan 2022 14:21:46 -0800 Subject: [PATCH 23/65] Add ids to nav bar items, liquid and bisq components --- .../bisq-transactions.component.html | 4 ++-- .../bisq-master-page.component.html | 12 +++++------ .../liquid-master-page.component.html | 12 +++++------ .../master-page/master-page.component.html | 20 +++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html index f85c29a9b..d1064972e 100644 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html +++ b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.html @@ -1,7 +1,7 @@

BSQ Transactions

-
+
@@ -39,7 +39,7 @@ {{ tx.blockHeight }} - + diff --git a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html index d77f96423..cacd38f39 100644 --- a/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html +++ b/frontend/src/app/components/bisq-master-page/bisq-master-page.component.html @@ -27,22 +27,22 @@