From 1339b98281b235b6e4a5a15db041df835b37a686 Mon Sep 17 00:00:00 2001 From: junderw Date: Sat, 27 Aug 2022 16:35:20 +0900 Subject: [PATCH 01/24] Feature: Readable RegExp constructor --- frontend/src/app/shared/common.utils.ts | 69 ++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 7d206f4b5..6cbe35386 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -119,6 +119,7 @@ export function convertRegion(input, to: 'name' | 'abbreviated'): string { } } + export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const rlat1 = lat1 * Math.PI / 180; const rlon1 = lon1 * Math.PI / 180; @@ -135,4 +136,70 @@ export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2 export function kmToMiles(km: number): number { return km * 0.62137119; -} \ No newline at end of file +} + +// all base58 characters +const BASE58_CHARS = '[a-km-zA-HJ-NP-Z1-9]'; +// all bech32 characters (after the separator) +const BECH32_CHARS = '[ac-hj-np-z02-9]'; +// All characters usable in bech32 human readable portion (before the 1 separator) +// Note: Technically the spec says "all US ASCII characters" but in practice only alphabet is used. +// Note: If HRP contains the separator (1) then the separator is "the last instance of separator" +const BECH32_HRP_CHARS = '[a-zA-Z0-9]'; +// Hex characters +const HEX_CHARS = '[a-fA-F0-9]'; +// A regex to say "A single 0 OR any number with no leading zeroes" +// (?: // Start a non-capturing group +// 0 // A single 0 +// | // OR +// [1-9][0-9]* // Any succession of numbers starting with 1-9 +// ) // End the non-capturing group. +const ZERO_INDEX_NUMBER_CHARS = '(?:0|[1-9][0-9]*)'; +export type RegexType = 'address' | 'blockhash' | 'transaction' | 'blockheight'; +export type Network = 'testnet' | 'signet' | 'liquid' | 'bisq' | 'mainnet'; +export function getRegex(type: RegexType, network: Network): RegExp { + let regex = '^'; // ^ = Start of string + switch (type) { + // Match a block height number + // [Testing Order]: any order is fine + case 'blockheight': + regex += ZERO_INDEX_NUMBER_CHARS; // block height is a 0 indexed number + break; + // Match a 32 byte block hash in hex. Assumes at least 32 bits of difficulty. + // [Testing Order]: Must always be tested before 'transaction' + case 'blockhash': + regex += '0{8}'; // Starts with exactly 8 zeroes in a row + regex += `${HEX_CHARS}{56}`; // Continues with exactly 56 hex letters/numbers + break; + // Match a 32 byte tx hash in hex. Contains optional output index specifier. + // [Testing Order]: Must always be tested after 'blockhash' + case 'transaction': + regex += `${HEX_CHARS}{64}`; // Exactly 64 hex letters/numbers + regex += '(?:'; // Start a non-capturing group + regex += ':'; // 1 instances of the symbol ":" + regex += ZERO_INDEX_NUMBER_CHARS; // A zero indexed number + regex += ')?'; // End the non-capturing group. This group appears 0 or 1 times + break; + case 'address': + // TODO + switch (network) { + case 'mainnet': + break; + case 'testnet': + break; + case 'signet': + break; + case 'liquid': + break; + case 'bisq': + break; + default: + throw new Error('Invalid Network (Unreachable error in TypeScript)'); + } + break; + default: + throw new Error('Invalid RegexType (Unreachable error in TypeScript)'); + } + regex += '$'; // $ = End of string + return new RegExp(regex); +} From c0d3f295eec2f483e8006b6f06de6fd8b3650a5b Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 28 Aug 2022 00:07:13 +0900 Subject: [PATCH 02/24] Finished Regex portion --- .../search-form/search-form.component.ts | 25 ++- frontend/src/app/shared/common.utils.ts | 191 ++++++++++++++---- 2 files changed, 178 insertions(+), 38 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index ab42fe1f7..8031195f0 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -9,6 +9,7 @@ import { ElectrsApiService } from '../../services/electrs-api.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { ApiService } from '../../services/api.service'; import { SearchResultsComponent } from './search-results/search-results.component'; +import { ADDRESS_REGEXES, getRegex } from '../../shared/common.utils'; @Component({ selector: 'app-search-form', @@ -38,6 +39,7 @@ export class SearchFormComponent implements OnInit { regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/; regexTransaction = /^([a-fA-F0-9]{64})(:\d+)?$/; regexBlockheight = /^[0-9]{1,9}$/; + focus$ = new Subject(); click$ = new Subject(); @@ -58,8 +60,13 @@ export class SearchFormComponent implements OnInit { private elementRef: ElementRef, ) { } - ngOnInit(): void { - this.stateService.networkChanged$.subscribe((network) => this.network = network); + + ngOnInit() { + this.stateService.networkChanged$.subscribe((network) => { + this.network = network; + // TODO: Eventually change network type here from string to enum of consts + this.regexAddress = getRegex('address', network as any); + }); this.searchForm = this.formBuilder.group({ searchText: ['', Validators.required], @@ -203,6 +210,20 @@ export class SearchFormComponent implements OnInit { this.isSearching = true; if (!this.regexTransaction.test(searchText) && this.regexAddress.test(searchText)) { this.navigate('/address/', searchText); + } else if ( + // If the search text matches any other network besides this one + ADDRESS_REGEXES + .filter(([, network]) => network !== this.network) + .some(([regex]) => regex.test(searchText)) + ) { + // Gather all network matches as string[] + const networks = ADDRESS_REGEXES.filter(([regex, network]) => + network !== this.network && + regex.test(searchText) + ).map(([, network]) => network); + // ############################################### + // TODO: Create the search items for the drop down + // ############################################### } else if (this.regexBlockhash.test(searchText) || this.regexBlockheight.test(searchText)) { this.navigate('/block/', searchText); } else if (this.regexTransaction.test(searchText)) { diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 6cbe35386..bbc9143c0 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -139,67 +139,186 @@ export function kmToMiles(km: number): number { } // all base58 characters -const BASE58_CHARS = '[a-km-zA-HJ-NP-Z1-9]'; +const BASE58_CHARS = `[a-km-zA-HJ-NP-Z1-9]`; + // all bech32 characters (after the separator) -const BECH32_CHARS = '[ac-hj-np-z02-9]'; -// All characters usable in bech32 human readable portion (before the 1 separator) -// Note: Technically the spec says "all US ASCII characters" but in practice only alphabet is used. -// Note: If HRP contains the separator (1) then the separator is "the last instance of separator" -const BECH32_HRP_CHARS = '[a-zA-Z0-9]'; +const BECH32_CHARS_LW = `[ac-hj-np-z02-9]`; +const BECH32_CHARS_UP = `[AC-HJ-NP-Z02-9]`; + // Hex characters -const HEX_CHARS = '[a-fA-F0-9]'; +const HEX_CHARS = `[a-fA-F0-9]`; + // A regex to say "A single 0 OR any number with no leading zeroes" -// (?: // Start a non-capturing group -// 0 // A single 0 -// | // OR -// [1-9][0-9]* // Any succession of numbers starting with 1-9 -// ) // End the non-capturing group. -const ZERO_INDEX_NUMBER_CHARS = '(?:0|[1-9][0-9]*)'; -export type RegexType = 'address' | 'blockhash' | 'transaction' | 'blockheight'; -export type Network = 'testnet' | 'signet' | 'liquid' | 'bisq' | 'mainnet'; -export function getRegex(type: RegexType, network: Network): RegExp { - let regex = '^'; // ^ = Start of string +// (?: // Start a non-capturing group +// 0 // A single 0 +// | // OR +// [1-9]\d* // Any succession of numbers starting with 1-9 +// ) // End the non-capturing group. +const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9]\d*)`; + +// Formatting of the address regex is for readability, +// We should ignore formatting it with automated formatting tools like prettier. +// +// prettier-ignore +const ADDRESS_CHARS = { + mainnet: { + base58: `[13]` // Starts with a single 1 or 3 + + BASE58_CHARS + + `{26,33}`, // Repeat the previous char 26-33 times. + // Version byte 0x00 (P2PKH) can be as short as 27 characters, up to 34 length + // P2SH must be 34 length + bech32: `(?:` + + `bc1` // Starts with bc1 + + BECH32_CHARS_LW + + `{6,100}` // As per bech32, 6 char checksum is minimum + + `|` + + `BC1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + testnet: { + base58: `[mn2]` // Starts with a single m, n, or 2 (P2PKH is m or n, 2 is P2SH) + + BASE58_CHARS + + `{33,34}`, // m|n is 34 length, 2 is 35 length (We match the first letter separately) + bech32: `(?:` + + `tb1` // Starts with bc1 + + BECH32_CHARS_LW + + `{6,100}` // As per bech32, 6 char checksum is minimum + + `|` + + `TB1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + signet: { + base58: `[mn2]` + + BASE58_CHARS + + `{33,34}`, + bech32: `(?:` + + `tb1` // Starts with tb1 + + BECH32_CHARS_LW + + `{6,100}` + + `|` + + `TB1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + liquid: { + base58: `[GHPQ]` // G|H is P2PKH, P|Q is P2SH + + BASE58_CHARS + + `{33}`, // All min-max lengths are 34 + bech32: `(?:` + + `(?:` // bech32 liquid starts with ex or lq + + `ex` + + `|` + + `lq` + + `)` + + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + + `{6,100}` + + `|` + + `(?:` // Same as above but all upper case + + `EX` + + `|` + + `LQ` + + `)` + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + bisq: { + base58: `B1` // bisq base58 addrs start with B1 + + BASE58_CHARS + + `{33}`, // always length 35 + bech32: `(?:` + + `bbc1` // Starts with bbc1 + + BECH32_CHARS_LW + + `{6,100}` + + `|` + + `BBC1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, +} +type RegexTypeNoAddr = `blockhash` | `transaction` | `blockheight`; +export type RegexType = `address` | RegexTypeNoAddr; + +export const NETWORKS = [`testnet`, `signet`, `liquid`, `bisq`, `mainnet`] as const; +export type Network = typeof NETWORKS[number]; // Turn const array into union type + +export const ADDRESS_REGEXES: [RegExp, string][] = NETWORKS + .map(network => [getRegex('address', network), network]) + +export function getRegex(type: RegexTypeNoAddr): RegExp; +export function getRegex(type: 'address', network: Network): RegExp; +export function getRegex(type: RegexType, network?: Network): RegExp { + let regex = `^`; // ^ = Start of string switch (type) { // Match a block height number // [Testing Order]: any order is fine - case 'blockheight': + case `blockheight`: regex += ZERO_INDEX_NUMBER_CHARS; // block height is a 0 indexed number break; // Match a 32 byte block hash in hex. Assumes at least 32 bits of difficulty. - // [Testing Order]: Must always be tested before 'transaction' - case 'blockhash': - regex += '0{8}'; // Starts with exactly 8 zeroes in a row + // [Testing Order]: Must always be tested before `transaction` + case `blockhash`: + regex += `0{8}`; // Starts with exactly 8 zeroes in a row regex += `${HEX_CHARS}{56}`; // Continues with exactly 56 hex letters/numbers break; // Match a 32 byte tx hash in hex. Contains optional output index specifier. - // [Testing Order]: Must always be tested after 'blockhash' - case 'transaction': + // [Testing Order]: Must always be tested after `blockhash` + case `transaction`: regex += `${HEX_CHARS}{64}`; // Exactly 64 hex letters/numbers - regex += '(?:'; // Start a non-capturing group - regex += ':'; // 1 instances of the symbol ":" + regex += `(?:`; // Start a non-capturing group + regex += `:`; // 1 instances of the symbol ":" regex += ZERO_INDEX_NUMBER_CHARS; // A zero indexed number - regex += ')?'; // End the non-capturing group. This group appears 0 or 1 times + regex += `)?`; // End the non-capturing group. This group appears 0 or 1 times break; - case 'address': - // TODO + // Match any one of the many address types + // [Testing Order]: While possible that a bech32 address happens to be 64 hex + // characters in the future (current lengths are not 64), it is highly unlikely + // Order therefore, does not matter. + case `address`: + if (!network) { + throw new Error(`Must pass network when type is address`); + } + regex += `(?:`; // Start a non-capturing group (each network has multiple options) switch (network) { - case 'mainnet': + case `mainnet`: + regex += ADDRESS_CHARS.mainnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.mainnet.bech32; break; - case 'testnet': + case `testnet`: + regex += ADDRESS_CHARS.testnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.testnet.bech32; break; - case 'signet': + case `signet`: + regex += ADDRESS_CHARS.signet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.signet.bech32; break; - case 'liquid': + case `liquid`: + regex += ADDRESS_CHARS.liquid.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.liquid.bech32; break; - case 'bisq': + case `bisq`: + regex += ADDRESS_CHARS.bisq.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.bisq.bech32; break; default: - throw new Error('Invalid Network (Unreachable error in TypeScript)'); + throw new Error(`Invalid Network ${network} (Unreachable error in TypeScript)`); } + regex += `)`; // End the non-capturing group break; default: - throw new Error('Invalid RegexType (Unreachable error in TypeScript)'); + throw new Error(`Invalid RegexType ${type} (Unreachable error in TypeScript)`); } - regex += '$'; // $ = End of string + regex += `$`; // $ = End of string return new RegExp(regex); } From 0a51b752e62d82b6986c8177cc63a48991383fcb Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 28 Aug 2022 16:07:46 +0900 Subject: [PATCH 03/24] Improve types and add liquidtestnet for regex --- frontend/src/app/shared/common.utils.ts | 38 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index bbc9143c0..288fc0362 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -160,7 +160,12 @@ const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9]\d*)`; // We should ignore formatting it with automated formatting tools like prettier. // // prettier-ignore -const ADDRESS_CHARS = { +const ADDRESS_CHARS: { + [k in Network]: { + base58: string; + bech32: string; + }; +} = { mainnet: { base58: `[13]` // Starts with a single 1 or 3 + BASE58_CHARS @@ -227,6 +232,28 @@ const ADDRESS_CHARS = { + `{6,100}` + `)`, }, + liquidtestnet: { + base58: `[89]` // ???(TODO: find version) is P2PKH, 8|9 is P2SH + + BASE58_CHARS + + `{33}`, // P2PKH is ???(TODO: find size), P2SH is 34 + bech32: `(?:` + + `(?:` // bech32 liquid testnet starts with tex or tlq + + `tex` // TODO: Why does mempool use this and not ert|el like in the elements source? + + `|` + + `tlq` // TODO: does this exist? + + `)` + + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + + `{6,100}` + + `|` + + `(?:` // Same as above but all upper case + + `TEX` + + `|` + + `TLQ` + + `)` + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, bisq: { base58: `B1` // bisq base58 addrs start with B1 + BASE58_CHARS @@ -245,10 +272,10 @@ const ADDRESS_CHARS = { type RegexTypeNoAddr = `blockhash` | `transaction` | `blockheight`; export type RegexType = `address` | RegexTypeNoAddr; -export const NETWORKS = [`testnet`, `signet`, `liquid`, `bisq`, `mainnet`] as const; +export const NETWORKS = [`testnet`, `signet`, `liquid`, `liquidtestnet`, `bisq`, `mainnet`] as const; export type Network = typeof NETWORKS[number]; // Turn const array into union type -export const ADDRESS_REGEXES: [RegExp, string][] = NETWORKS +export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS .map(network => [getRegex('address', network), network]) export function getRegex(type: RegexTypeNoAddr): RegExp; @@ -306,6 +333,11 @@ export function getRegex(type: RegexType, network?: Network): RegExp { regex += `|`; // OR regex += ADDRESS_CHARS.liquid.bech32; break; + case `liquidtestnet`: + regex += ADDRESS_CHARS.liquidtestnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.liquidtestnet.bech32; + break; case `bisq`: regex += ADDRESS_CHARS.bisq.base58; regex += `|`; // OR From 3d900a38497d7d6b588abba8d06c2af2c8c5d663 Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 28 Aug 2022 16:28:42 +0900 Subject: [PATCH 04/24] Fix: Prevent regex clash with channel IDs --- frontend/src/app/shared/common.utils.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 288fc0362..8b914beda 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -149,12 +149,13 @@ const BECH32_CHARS_UP = `[AC-HJ-NP-Z02-9]`; const HEX_CHARS = `[a-fA-F0-9]`; // A regex to say "A single 0 OR any number with no leading zeroes" -// (?: // Start a non-capturing group -// 0 // A single 0 -// | // OR -// [1-9]\d* // Any succession of numbers starting with 1-9 -// ) // End the non-capturing group. -const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9]\d*)`; +// Capped at 13 digits so as to not be confused with lightning channel IDs (which are around 17 digits) +// (?: // Start a non-capturing group +// 0 // A single 0 +// | // OR +// [1-9][0-9]{0,12} // Any succession of numbers up to 13 digits starting with 1-9 +// ) // End the non-capturing group. +const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9][0-9]{0,12})`; // Formatting of the address regex is for readability, // We should ignore formatting it with automated formatting tools like prettier. From d825143b3562c4ab493f56ae67a4eadcfde7a469 Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 4 Sep 2022 21:31:02 +0900 Subject: [PATCH 05/24] Search for full address in separate network if matches --- .../search-form/search-form.component.ts | 26 +- frontend/src/app/shared/common.utils.ts | 1 - .../pipes/relative-url/relative-url.pipe.ts | 4 +- frontend/src/app/shared/regex.utils.ts | 224 ++++++++++++++++++ 4 files changed, 235 insertions(+), 20 deletions(-) create mode 100644 frontend/src/app/shared/regex.utils.ts diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 8031195f0..a9e31221a 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -9,7 +9,7 @@ import { ElectrsApiService } from '../../services/electrs-api.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { ApiService } from '../../services/api.service'; import { SearchResultsComponent } from './search-results/search-results.component'; -import { ADDRESS_REGEXES, getRegex } from '../../shared/common.utils'; +import { findOtherNetworks, getRegex } from '../../shared/regex.utils'; @Component({ selector: 'app-search-form', @@ -208,22 +208,13 @@ export class SearchFormComponent implements OnInit { const searchText = result || this.searchForm.value.searchText.trim(); if (searchText) { this.isSearching = true; + + const otherNetworks = findOtherNetworks(searchText, this.network as any); if (!this.regexTransaction.test(searchText) && this.regexAddress.test(searchText)) { this.navigate('/address/', searchText); - } else if ( - // If the search text matches any other network besides this one - ADDRESS_REGEXES - .filter(([, network]) => network !== this.network) - .some(([regex]) => regex.test(searchText)) - ) { - // Gather all network matches as string[] - const networks = ADDRESS_REGEXES.filter(([regex, network]) => - network !== this.network && - regex.test(searchText) - ).map(([, network]) => network); - // ############################################### - // TODO: Create the search items for the drop down - // ############################################### + } else if (otherNetworks.length > 0) { + // Change the network to the first match + this.navigate('/address/', searchText, undefined, otherNetworks[0]); } else if (this.regexBlockhash.test(searchText) || this.regexBlockheight.test(searchText)) { this.navigate('/block/', searchText); } else if (this.regexTransaction.test(searchText)) { @@ -252,8 +243,9 @@ export class SearchFormComponent implements OnInit { } } - navigate(url: string, searchText: string, extras?: any): void { - this.router.navigate([this.relativeUrlPipe.transform(url), searchText], extras); + + navigate(url: string, searchText: string, extras?: any, swapNetwork?: string) { + this.router.navigate([this.relativeUrlPipe.transform(url, swapNetwork), searchText], extras); this.searchTriggered.emit(); this.searchForm.setValue({ searchText: '', diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 8b914beda..e50ba13b7 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -119,7 +119,6 @@ export function convertRegion(input, to: 'name' | 'abbreviated'): string { } } - export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const rlat1 = lat1 * Math.PI / 180; const rlon1 = lon1 * Math.PI / 180; diff --git a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts index d7fe612fe..83f5f20df 100644 --- a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts +++ b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts @@ -10,8 +10,8 @@ export class RelativeUrlPipe implements PipeTransform { private stateService: StateService, ) { } - transform(value: string): string { - let network = this.stateService.network; + transform(value: string, swapNetwork?: string): string { + let network = swapNetwork || this.stateService.network; if (this.stateService.env.BASE_MODULE === 'liquid' && network === 'liquidtestnet') { network = 'testnet'; } else if (this.stateService.env.BASE_MODULE !== 'mempool') { diff --git a/frontend/src/app/shared/regex.utils.ts b/frontend/src/app/shared/regex.utils.ts new file mode 100644 index 000000000..bac256c8d --- /dev/null +++ b/frontend/src/app/shared/regex.utils.ts @@ -0,0 +1,224 @@ +// all base58 characters +const BASE58_CHARS = `[a-km-zA-HJ-NP-Z1-9]`; + +// all bech32 characters (after the separator) +const BECH32_CHARS_LW = `[ac-hj-np-z02-9]`; +const BECH32_CHARS_UP = `[AC-HJ-NP-Z02-9]`; + +// Hex characters +const HEX_CHARS = `[a-fA-F0-9]`; + +// A regex to say "A single 0 OR any number with no leading zeroes" +// Capped at 13 digits so as to not be confused with lightning channel IDs (which are around 17 digits) +// (?: // Start a non-capturing group +// 0 // A single 0 +// | // OR +// [1-9][0-9]{0,12} // Any succession of numbers up to 13 digits starting with 1-9 +// ) // End the non-capturing group. +const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9][0-9]{0,12})`; + +// Formatting of the address regex is for readability, +// We should ignore formatting it with automated formatting tools like prettier. +// +// prettier-ignore +const ADDRESS_CHARS: { + [k in Network]: { + base58: string; + bech32: string; + }; +} = { + mainnet: { + base58: `[13]` // Starts with a single 1 or 3 + + BASE58_CHARS + + `{26,33}`, // Repeat the previous char 26-33 times. + // Version byte 0x00 (P2PKH) can be as short as 27 characters, up to 34 length + // P2SH must be 34 length + bech32: `(?:` + + `bc1` // Starts with bc1 + + BECH32_CHARS_LW + + `{6,100}` // As per bech32, 6 char checksum is minimum + + `|` + + `BC1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + testnet: { + base58: `[mn2]` // Starts with a single m, n, or 2 (P2PKH is m or n, 2 is P2SH) + + BASE58_CHARS + + `{33,34}`, // m|n is 34 length, 2 is 35 length (We match the first letter separately) + bech32: `(?:` + + `tb1` // Starts with bc1 + + BECH32_CHARS_LW + + `{6,100}` // As per bech32, 6 char checksum is minimum + + `|` + + `TB1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + signet: { + base58: `[mn2]` + + BASE58_CHARS + + `{33,34}`, + bech32: `(?:` + + `tb1` // Starts with tb1 + + BECH32_CHARS_LW + + `{6,100}` + + `|` + + `TB1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + liquid: { + base58: `[GHPQ]` // G|H is P2PKH, P|Q is P2SH + + BASE58_CHARS + + `{33}`, // All min-max lengths are 34 + bech32: `(?:` + + `(?:` // bech32 liquid starts with ex or lq + + `ex` + + `|` + + `lq` + + `)` + + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + + `{6,100}` + + `|` + + `(?:` // Same as above but all upper case + + `EX` + + `|` + + `LQ` + + `)` + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + liquidtestnet: { + base58: `[89]` // ???(TODO: find version) is P2PKH, 8|9 is P2SH + + BASE58_CHARS + + `{33}`, // P2PKH is ???(TODO: find size), P2SH is 34 + bech32: `(?:` + + `(?:` // bech32 liquid testnet starts with tex or tlq + + `tex` // TODO: Why does mempool use this and not ert|el like in the elements source? + + `|` + + `tlq` // TODO: does this exist? + + `)` + + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + + `{6,100}` + + `|` + + `(?:` // Same as above but all upper case + + `TEX` + + `|` + + `TLQ` + + `)` + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, + bisq: { + base58: `B1` // bisq base58 addrs start with B1 + + BASE58_CHARS + + `{33}`, // always length 35 + bech32: `(?:` + + `bbc1` // Starts with bbc1 + + BECH32_CHARS_LW + + `{6,100}` + + `|` + + `BBC1` // All upper case version + + BECH32_CHARS_UP + + `{6,100}` + + `)`, + }, +} +type RegexTypeNoAddr = `blockhash` | `transaction` | `blockheight`; +export type RegexType = `address` | RegexTypeNoAddr; + +export const NETWORKS = [`testnet`, `signet`, `liquid`, `liquidtestnet`, `bisq`, `mainnet`] as const; +export type Network = typeof NETWORKS[number]; // Turn const array into union type + +export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS + .map(network => [getRegex('address', network), network]) + +export function findOtherNetworks(address: string, skipNetwork: Network): Network[] { + return ADDRESS_REGEXES.filter(([regex, network]) => + network !== skipNetwork && + regex.test(address) + ).map(([, network]) => network); +} + +export function getRegex(type: RegexTypeNoAddr): RegExp; +export function getRegex(type: 'address', network: Network): RegExp; +export function getRegex(type: RegexType, network?: Network): RegExp { + let regex = `^`; // ^ = Start of string + switch (type) { + // Match a block height number + // [Testing Order]: any order is fine + case `blockheight`: + regex += ZERO_INDEX_NUMBER_CHARS; // block height is a 0 indexed number + break; + // Match a 32 byte block hash in hex. Assumes at least 32 bits of difficulty. + // [Testing Order]: Must always be tested before `transaction` + case `blockhash`: + regex += `0{8}`; // Starts with exactly 8 zeroes in a row + regex += `${HEX_CHARS}{56}`; // Continues with exactly 56 hex letters/numbers + break; + // Match a 32 byte tx hash in hex. Contains optional output index specifier. + // [Testing Order]: Must always be tested after `blockhash` + case `transaction`: + regex += `${HEX_CHARS}{64}`; // Exactly 64 hex letters/numbers + regex += `(?:`; // Start a non-capturing group + regex += `:`; // 1 instances of the symbol ":" + regex += ZERO_INDEX_NUMBER_CHARS; // A zero indexed number + regex += `)?`; // End the non-capturing group. This group appears 0 or 1 times + break; + // Match any one of the many address types + // [Testing Order]: While possible that a bech32 address happens to be 64 hex + // characters in the future (current lengths are not 64), it is highly unlikely + // Order therefore, does not matter. + case `address`: + if (!network) { + throw new Error(`Must pass network when type is address`); + } + regex += `(?:`; // Start a non-capturing group (each network has multiple options) + switch (network) { + case `mainnet`: + regex += ADDRESS_CHARS.mainnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.mainnet.bech32; + break; + case `testnet`: + regex += ADDRESS_CHARS.testnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.testnet.bech32; + break; + case `signet`: + regex += ADDRESS_CHARS.signet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.signet.bech32; + break; + case `liquid`: + regex += ADDRESS_CHARS.liquid.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.liquid.bech32; + break; + case `liquidtestnet`: + regex += ADDRESS_CHARS.liquidtestnet.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.liquidtestnet.bech32; + break; + case `bisq`: + regex += ADDRESS_CHARS.bisq.base58; + regex += `|`; // OR + regex += ADDRESS_CHARS.bisq.bech32; + break; + default: + throw new Error(`Invalid Network ${network} (Unreachable error in TypeScript)`); + } + regex += `)`; // End the non-capturing group + break; + default: + throw new Error(`Invalid RegexType ${type} (Unreachable error in TypeScript)`); + } + regex += `$`; // $ = End of string + return new RegExp(regex); +} From 2c59992d3fbc28f64312802c622564b97a946cd6 Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 4 Sep 2022 21:53:52 +0900 Subject: [PATCH 06/24] Fix E2E error --- .../search-form/search-form.component.ts | 2 +- frontend/src/app/shared/common.utils.ts | 218 ------------------ 2 files changed, 1 insertion(+), 219 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index a9e31221a..18b4048ef 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -65,7 +65,7 @@ export class SearchFormComponent implements OnInit { this.stateService.networkChanged$.subscribe((network) => { this.network = network; // TODO: Eventually change network type here from string to enum of consts - this.regexAddress = getRegex('address', network as any); + this.regexAddress = getRegex('address', network as any || 'mainnet'); }); this.searchForm = this.formBuilder.group({ diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index e50ba13b7..87c952c31 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -136,221 +136,3 @@ export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2 export function kmToMiles(km: number): number { return km * 0.62137119; } - -// all base58 characters -const BASE58_CHARS = `[a-km-zA-HJ-NP-Z1-9]`; - -// all bech32 characters (after the separator) -const BECH32_CHARS_LW = `[ac-hj-np-z02-9]`; -const BECH32_CHARS_UP = `[AC-HJ-NP-Z02-9]`; - -// Hex characters -const HEX_CHARS = `[a-fA-F0-9]`; - -// A regex to say "A single 0 OR any number with no leading zeroes" -// Capped at 13 digits so as to not be confused with lightning channel IDs (which are around 17 digits) -// (?: // Start a non-capturing group -// 0 // A single 0 -// | // OR -// [1-9][0-9]{0,12} // Any succession of numbers up to 13 digits starting with 1-9 -// ) // End the non-capturing group. -const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9][0-9]{0,12})`; - -// Formatting of the address regex is for readability, -// We should ignore formatting it with automated formatting tools like prettier. -// -// prettier-ignore -const ADDRESS_CHARS: { - [k in Network]: { - base58: string; - bech32: string; - }; -} = { - mainnet: { - base58: `[13]` // Starts with a single 1 or 3 - + BASE58_CHARS - + `{26,33}`, // Repeat the previous char 26-33 times. - // Version byte 0x00 (P2PKH) can be as short as 27 characters, up to 34 length - // P2SH must be 34 length - bech32: `(?:` - + `bc1` // Starts with bc1 - + BECH32_CHARS_LW - + `{6,100}` // As per bech32, 6 char checksum is minimum - + `|` - + `BC1` // All upper case version - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, - testnet: { - base58: `[mn2]` // Starts with a single m, n, or 2 (P2PKH is m or n, 2 is P2SH) - + BASE58_CHARS - + `{33,34}`, // m|n is 34 length, 2 is 35 length (We match the first letter separately) - bech32: `(?:` - + `tb1` // Starts with bc1 - + BECH32_CHARS_LW - + `{6,100}` // As per bech32, 6 char checksum is minimum - + `|` - + `TB1` // All upper case version - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, - signet: { - base58: `[mn2]` - + BASE58_CHARS - + `{33,34}`, - bech32: `(?:` - + `tb1` // Starts with tb1 - + BECH32_CHARS_LW - + `{6,100}` - + `|` - + `TB1` // All upper case version - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, - liquid: { - base58: `[GHPQ]` // G|H is P2PKH, P|Q is P2SH - + BASE58_CHARS - + `{33}`, // All min-max lengths are 34 - bech32: `(?:` - + `(?:` // bech32 liquid starts with ex or lq - + `ex` - + `|` - + `lq` - + `)` - + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. - + `{6,100}` - + `|` - + `(?:` // Same as above but all upper case - + `EX` - + `|` - + `LQ` - + `)` - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, - liquidtestnet: { - base58: `[89]` // ???(TODO: find version) is P2PKH, 8|9 is P2SH - + BASE58_CHARS - + `{33}`, // P2PKH is ???(TODO: find size), P2SH is 34 - bech32: `(?:` - + `(?:` // bech32 liquid testnet starts with tex or tlq - + `tex` // TODO: Why does mempool use this and not ert|el like in the elements source? - + `|` - + `tlq` // TODO: does this exist? - + `)` - + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. - + `{6,100}` - + `|` - + `(?:` // Same as above but all upper case - + `TEX` - + `|` - + `TLQ` - + `)` - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, - bisq: { - base58: `B1` // bisq base58 addrs start with B1 - + BASE58_CHARS - + `{33}`, // always length 35 - bech32: `(?:` - + `bbc1` // Starts with bbc1 - + BECH32_CHARS_LW - + `{6,100}` - + `|` - + `BBC1` // All upper case version - + BECH32_CHARS_UP - + `{6,100}` - + `)`, - }, -} -type RegexTypeNoAddr = `blockhash` | `transaction` | `blockheight`; -export type RegexType = `address` | RegexTypeNoAddr; - -export const NETWORKS = [`testnet`, `signet`, `liquid`, `liquidtestnet`, `bisq`, `mainnet`] as const; -export type Network = typeof NETWORKS[number]; // Turn const array into union type - -export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS - .map(network => [getRegex('address', network), network]) - -export function getRegex(type: RegexTypeNoAddr): RegExp; -export function getRegex(type: 'address', network: Network): RegExp; -export function getRegex(type: RegexType, network?: Network): RegExp { - let regex = `^`; // ^ = Start of string - switch (type) { - // Match a block height number - // [Testing Order]: any order is fine - case `blockheight`: - regex += ZERO_INDEX_NUMBER_CHARS; // block height is a 0 indexed number - break; - // Match a 32 byte block hash in hex. Assumes at least 32 bits of difficulty. - // [Testing Order]: Must always be tested before `transaction` - case `blockhash`: - regex += `0{8}`; // Starts with exactly 8 zeroes in a row - regex += `${HEX_CHARS}{56}`; // Continues with exactly 56 hex letters/numbers - break; - // Match a 32 byte tx hash in hex. Contains optional output index specifier. - // [Testing Order]: Must always be tested after `blockhash` - case `transaction`: - regex += `${HEX_CHARS}{64}`; // Exactly 64 hex letters/numbers - regex += `(?:`; // Start a non-capturing group - regex += `:`; // 1 instances of the symbol ":" - regex += ZERO_INDEX_NUMBER_CHARS; // A zero indexed number - regex += `)?`; // End the non-capturing group. This group appears 0 or 1 times - break; - // Match any one of the many address types - // [Testing Order]: While possible that a bech32 address happens to be 64 hex - // characters in the future (current lengths are not 64), it is highly unlikely - // Order therefore, does not matter. - case `address`: - if (!network) { - throw new Error(`Must pass network when type is address`); - } - regex += `(?:`; // Start a non-capturing group (each network has multiple options) - switch (network) { - case `mainnet`: - regex += ADDRESS_CHARS.mainnet.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.mainnet.bech32; - break; - case `testnet`: - regex += ADDRESS_CHARS.testnet.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.testnet.bech32; - break; - case `signet`: - regex += ADDRESS_CHARS.signet.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.signet.bech32; - break; - case `liquid`: - regex += ADDRESS_CHARS.liquid.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.liquid.bech32; - break; - case `liquidtestnet`: - regex += ADDRESS_CHARS.liquidtestnet.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.liquidtestnet.bech32; - break; - case `bisq`: - regex += ADDRESS_CHARS.bisq.base58; - regex += `|`; // OR - regex += ADDRESS_CHARS.bisq.bech32; - break; - default: - throw new Error(`Invalid Network ${network} (Unreachable error in TypeScript)`); - } - regex += `)`; // End the non-capturing group - break; - default: - throw new Error(`Invalid RegexType ${type} (Unreachable error in TypeScript)`); - } - regex += `$`; // $ = End of string - return new RegExp(regex); -} From 213800f563a2731a991cfbe2ce76b3ec419a92ad Mon Sep 17 00:00:00 2001 From: softsimon Date: Wed, 19 Jul 2023 16:46:02 +0900 Subject: [PATCH 07/24] Merge error fix --- .../app/components/search-form/search-form.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 18b4048ef..eb8adbd83 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -35,10 +35,10 @@ export class SearchFormComponent implements OnInit { } } - regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[A-z]{2,5}1[a-zA-HJ-NP-Z0-9]{39,59})$/; - regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/; - regexTransaction = /^([a-fA-F0-9]{64})(:\d+)?$/; - regexBlockheight = /^[0-9]{1,9}$/; + regexAddress = getRegex('address', 'mainnet'); // Default to mainnet + regexBlockhash = getRegex('blockhash'); + regexTransaction = getRegex('transaction'); + regexBlockheight = getRegex('blockheight'); focus$ = new Subject(); click$ = new Subject(); From bd34d71d8b82bc784da1f6be3da17d3c076c7cfa Mon Sep 17 00:00:00 2001 From: natsee Date: Sat, 30 Dec 2023 19:19:07 +0100 Subject: [PATCH 08/24] Update regexes in regex.utils.ts --- .../search-form/search-form.component.ts | 18 ++- .../pipes/relative-url/relative-url.pipe.ts | 1 + frontend/src/app/shared/regex.utils.ts | 121 +++++++++++++----- 3 files changed, 105 insertions(+), 35 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index cb64df7f4..4ed6b1d64 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -38,11 +38,11 @@ export class SearchFormComponent implements OnInit { } regexAddress = getRegex('address', 'mainnet'); // Default to mainnet - regexBlockhash = getRegex('blockhash'); + regexBlockhash = getRegex('blockhash', 'mainnet'); regexTransaction = getRegex('transaction'); regexBlockheight = getRegex('blockheight'); - regexDate = /^(?:\d{4}[-/]\d{1,2}[-/]\d{1,2}(?: \d{1,2}:\d{2})?)$/; - regexUnixTimestamp = /^\d{10}$/; + regexDate = getRegex('date'); + regexUnixTimestamp = getRegex('timestamp'); focus$ = new Subject(); click$ = new Subject(); @@ -72,6 +72,7 @@ export class SearchFormComponent implements OnInit { this.network = network; // TODO: Eventually change network type here from string to enum of consts this.regexAddress = getRegex('address', network as any || 'mainnet'); + this.regexBlockhash = getRegex('blockhash', network as any || 'mainnet'); }); this.router.events.subscribe((e: NavigationStart) => { // Reset search focus when changing page @@ -181,8 +182,8 @@ export class SearchFormComponent implements OnInit { const lightningResults = result[1]; const matchesBlockHeight = this.regexBlockheight.test(searchText) && parseInt(searchText) <= this.stateService.latestBlockHeight; - const matchesDateTime = this.regexDate.test(searchText) && new Date(searchText).toString() !== 'Invalid Date'; - const matchesUnixTimestamp = this.regexUnixTimestamp.test(searchText); + const matchesDateTime = this.regexDate.test(searchText) && new Date(searchText).toString() !== 'Invalid Date' && new Date(searchText).getTime() <= Date.now() && new Date(searchText).getTime() >= 1231006505000; + const matchesUnixTimestamp = this.regexUnixTimestamp.test(searchText) && parseInt(searchText) <= Math.floor(Date.now() / 1000) && parseInt(searchText) >= 1231006505; // 1231006505 is the timestamp of the genesis block const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText); const matchesBlockHash = this.regexBlockhash.test(searchText); const matchesAddress = !matchesTxId && this.regexAddress.test(searchText); @@ -237,7 +238,7 @@ export class SearchFormComponent implements OnInit { if (searchText) { this.isSearching = true; - const otherNetworks = findOtherNetworks(searchText, this.network as any); + const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet'); if (!this.regexTransaction.test(searchText) && this.regexAddress.test(searchText)) { this.navigate('/address/', searchText); } else if (otherNetworks.length > 0) { @@ -269,6 +270,11 @@ export class SearchFormComponent implements OnInit { } else if (this.regexDate.test(searchText) || this.regexUnixTimestamp.test(searchText)) { let timestamp: number; this.regexDate.test(searchText) ? timestamp = Math.floor(new Date(searchText).getTime() / 1000) : timestamp = searchText; + // Check if timestamp is too far in the future or before the genesis block + if (timestamp > Math.floor(Date.now() / 1000) || timestamp < 1231006505) { + this.isSearching = false; + return; + } this.apiService.getBlockDataFromTimestamp$(timestamp).subscribe( (data) => { this.navigate('/block/', data.hash); }, (error) => { console.log(error); this.isSearching = false; } diff --git a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts index 83f5f20df..4211765df 100644 --- a/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts +++ b/frontend/src/app/shared/pipes/relative-url/relative-url.pipe.ts @@ -12,6 +12,7 @@ export class RelativeUrlPipe implements PipeTransform { transform(value: string, swapNetwork?: string): string { let network = swapNetwork || this.stateService.network; + if (network === 'mainnet') network = ''; if (this.stateService.env.BASE_MODULE === 'liquid' && network === 'liquidtestnet') { network = 'testnet'; } else if (this.stateService.env.BASE_MODULE !== 'mempool') { diff --git a/frontend/src/app/shared/regex.utils.ts b/frontend/src/app/shared/regex.utils.ts index bac256c8d..d0cd08f24 100644 --- a/frontend/src/app/shared/regex.utils.ts +++ b/frontend/src/app/shared/regex.utils.ts @@ -9,13 +9,16 @@ const BECH32_CHARS_UP = `[AC-HJ-NP-Z02-9]`; const HEX_CHARS = `[a-fA-F0-9]`; // A regex to say "A single 0 OR any number with no leading zeroes" -// Capped at 13 digits so as to not be confused with lightning channel IDs (which are around 17 digits) +// Capped at 9 digits so as to not be confused with lightning channel IDs (which are around 17 digits) // (?: // Start a non-capturing group // 0 // A single 0 // | // OR -// [1-9][0-9]{0,12} // Any succession of numbers up to 13 digits starting with 1-9 +// [1-9][0-9]{0,8} // Any succession of numbers up to 9 digits starting with 1-9 // ) // End the non-capturing group. -const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9][0-9]{0,12})`; +const ZERO_INDEX_NUMBER_CHARS = `(?:0|[1-9][0-9]{0,8})`; + +// Simple digits only regex +const NUMBER_CHARS = `[0-9]`; // Formatting of the address regex is for readability, // We should ignore formatting it with automated formatting tools like prettier. @@ -48,7 +51,7 @@ const ADDRESS_CHARS: { + BASE58_CHARS + `{33,34}`, // m|n is 34 length, 2 is 35 length (We match the first letter separately) bech32: `(?:` - + `tb1` // Starts with bc1 + + `tb1` // Starts with tb1 + BECH32_CHARS_LW + `{6,100}` // As per bech32, 6 char checksum is minimum + `|` @@ -76,18 +79,18 @@ const ADDRESS_CHARS: { + BASE58_CHARS + `{33}`, // All min-max lengths are 34 bech32: `(?:` - + `(?:` // bech32 liquid starts with ex or lq - + `ex` + + `(?:` // bech32 liquid starts with ex1 or lq1 + + `ex1` + `|` - + `lq` + + `lq1` + `)` + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + `{6,100}` + `|` + `(?:` // Same as above but all upper case - + `EX` + + `EX1` + `|` - + `LQ` + + `LQ1` + `)` + BECH32_CHARS_UP + `{6,100}` @@ -99,39 +102,39 @@ const ADDRESS_CHARS: { + `{33}`, // P2PKH is ???(TODO: find size), P2SH is 34 bech32: `(?:` + `(?:` // bech32 liquid testnet starts with tex or tlq - + `tex` // TODO: Why does mempool use this and not ert|el like in the elements source? + + `tex1` // TODO: Why does mempool use this and not ert|el like in the elements source? + `|` - + `tlq` // TODO: does this exist? + + `tlq1` // TODO: does this exist? + `)` + BECH32_CHARS_LW // blech32 and bech32 are the same alphabet and protocol, different checksums. + `{6,100}` + `|` + `(?:` // Same as above but all upper case - + `TEX` + + `TEX1` + `|` - + `TLQ` + + `TLQ1` + `)` + BECH32_CHARS_UP + `{6,100}` + `)`, }, bisq: { - base58: `B1` // bisq base58 addrs start with B1 + base58: `(?:[bB][13]` // b or B at the start, followed by a single 1 or 3 + BASE58_CHARS - + `{33}`, // always length 35 + + `{26,33})`, bech32: `(?:` - + `bbc1` // Starts with bbc1 + + `[bB]bc1` // b or B at the start, followed by bc1 + BECH32_CHARS_LW - + `{6,100}` + + `{6,100}` + `|` - + `BBC1` // All upper case version + + `[bB]BC1` // b or B at the start, followed by BC1 + BECH32_CHARS_UP + `{6,100}` + `)`, }, } -type RegexTypeNoAddr = `blockhash` | `transaction` | `blockheight`; -export type RegexType = `address` | RegexTypeNoAddr; +type RegexTypeNoAddrNoBlockHash = | `transaction` | `blockheight` | `date` | `timestamp`; +export type RegexType = `address` | `blockhash` | RegexTypeNoAddrNoBlockHash; export const NETWORKS = [`testnet`, `signet`, `liquid`, `liquidtestnet`, `bisq`, `mainnet`] as const; export type Network = typeof NETWORKS[number]; // Turn const array into union type @@ -139,15 +142,15 @@ export type Network = typeof NETWORKS[number]; // Turn const array into union ty export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS .map(network => [getRegex('address', network), network]) -export function findOtherNetworks(address: string, skipNetwork: Network): Network[] { - return ADDRESS_REGEXES.filter(([regex, network]) => - network !== skipNetwork && - regex.test(address) - ).map(([, network]) => network); +export function findOtherNetworks(address: string, skipNetwork: Network): {network: Network, address: string}[] { + return ADDRESS_REGEXES + .filter(([regex, network]) => network !== skipNetwork && regex.test(address)) + .map(([, network]) => ({ network, address })); } -export function getRegex(type: RegexTypeNoAddr): RegExp; +export function getRegex(type: RegexTypeNoAddrNoBlockHash): RegExp; export function getRegex(type: 'address', network: Network): RegExp; +export function getRegex(type: 'blockhash', network: Network): RegExp; export function getRegex(type: RegexType, network?: Network): RegExp { let regex = `^`; // ^ = Start of string switch (type) { @@ -156,11 +159,37 @@ export function getRegex(type: RegexType, network?: Network): RegExp { case `blockheight`: regex += ZERO_INDEX_NUMBER_CHARS; // block height is a 0 indexed number break; - // Match a 32 byte block hash in hex. Assumes at least 32 bits of difficulty. + // Match a 32 byte block hash in hex. // [Testing Order]: Must always be tested before `transaction` case `blockhash`: - regex += `0{8}`; // Starts with exactly 8 zeroes in a row - regex += `${HEX_CHARS}{56}`; // Continues with exactly 56 hex letters/numbers + if (!network) { + throw new Error(`Must pass network when type is blockhash`); + } + let leadingZeroes: number; + switch (network) { + case `mainnet`: + leadingZeroes = 8; // Assumes at least 32 bits of difficulty + break; + case `testnet`: + leadingZeroes = 8; // Assumes at least 32 bits of difficulty + break; + case `signet`: + leadingZeroes = 5; + break; + case `liquid`: + leadingZeroes = 8; // We are not interested in Liquid block hashes + break; + case `liquidtestnet`: + leadingZeroes = 8; // We are not interested in Liquid block hashes + break; + case `bisq`: + leadingZeroes = 8; // Assumes at least 32 bits of difficulty + break; + default: + throw new Error(`Invalid Network ${network} (Unreachable error in TypeScript)`); + } + regex += `0{${leadingZeroes}}`; + regex += `${HEX_CHARS}{${64 - leadingZeroes}}`; // Exactly 64 hex letters/numbers break; // Match a 32 byte tx hash in hex. Contains optional output index specifier. // [Testing Order]: Must always be tested after `blockhash` @@ -185,16 +214,28 @@ export function getRegex(type: RegexType, network?: Network): RegExp { regex += ADDRESS_CHARS.mainnet.base58; regex += `|`; // OR regex += ADDRESS_CHARS.mainnet.bech32; + regex += `|`; // OR + regex += `04${HEX_CHARS}{128}`; // Uncompressed pubkey + regex += `|`; // OR + regex += `(?:02|03)${HEX_CHARS}{64}`; // Compressed pubkey break; case `testnet`: regex += ADDRESS_CHARS.testnet.base58; regex += `|`; // OR regex += ADDRESS_CHARS.testnet.bech32; + regex += `|`; // OR + regex += `04${HEX_CHARS}{128}`; // Uncompressed pubkey + regex += `|`; // OR + regex += `(?:02|03)${HEX_CHARS}{64}`; // Compressed pubkey break; case `signet`: regex += ADDRESS_CHARS.signet.base58; regex += `|`; // OR regex += ADDRESS_CHARS.signet.bech32; + regex += `|`; // OR + regex += `04${HEX_CHARS}{128}`; // Uncompressed pubkey + regex += `|`; // OR + regex += `(?:02|03)${HEX_CHARS}{64}`; // Compressed pubkey break; case `liquid`: regex += ADDRESS_CHARS.liquid.base58; @@ -216,6 +257,28 @@ export function getRegex(type: RegexType, network?: Network): RegExp { } regex += `)`; // End the non-capturing group break; + // Match a date in the format YYYY-MM-DD (optional: HH:MM) + // [Testing Order]: any order is fine + case `date`: + regex += `(?:`; // Start a non-capturing group + regex += `${NUMBER_CHARS}{4}`; // Exactly 4 digits + regex += `[-/]`; // 1 instance of the symbol "-" or "/" + regex += `${NUMBER_CHARS}{1,2}`; // Exactly 4 digits + regex += `[-/]`; // 1 instance of the symbol "-" or "/" + regex += `${NUMBER_CHARS}{1,2}`; // Exactly 4 digits + regex += `(?:`; // Start a non-capturing group + regex += ` `; // 1 instance of the symbol " " + regex += `${NUMBER_CHARS}{1,2}`; // Exactly 4 digits + regex += `:`; // 1 instance of the symbol ":" + regex += `${NUMBER_CHARS}{1,2}`; // Exactly 4 digits + regex += `)?`; // End the non-capturing group. This group appears 0 or 1 times + regex += `)`; // End the non-capturing group + break; + // Match a unix timestamp + // [Testing Order]: any order is fine + case `timestamp`: + regex += `${NUMBER_CHARS}{10}`; // Exactly 10 digits + break; default: throw new Error(`Invalid RegexType ${type} (Unreachable error in TypeScript)`); } From 3e1b85e32ca7526f9772a495b14ef8bd6cfb3546 Mon Sep 17 00:00:00 2001 From: natsee Date: Sun, 31 Dec 2023 23:23:53 +0100 Subject: [PATCH 09/24] Add search addresses from cross networks feature --- .../search-form/search-form.component.ts | 48 ++++++++++--------- .../search-results.component.html | 16 +++++-- .../search-results.component.scss | 4 ++ .../search-results.component.ts | 2 +- frontend/src/app/shared/regex.utils.ts | 37 ++++++++++++++ 5 files changed, 79 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 4ed6b1d64..ed46f1e88 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -2,14 +2,14 @@ import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Output, ViewC import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { EventType, NavigationStart, Router } from '@angular/router'; import { AssetsService } from '../../services/assets.service'; -import { StateService } from '../../services/state.service'; +import { Env, StateService } from '../../services/state.service'; import { Observable, of, Subject, zip, BehaviorSubject, combineLatest } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap, catchError, map, startWith, tap } from 'rxjs/operators'; import { ElectrsApiService } from '../../services/electrs-api.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { ApiService } from '../../services/api.service'; import { SearchResultsComponent } from './search-results/search-results.component'; -import { findOtherNetworks, getRegex } from '../../shared/regex.utils'; +import { Network, findOtherNetworks, getRegex, getTargetUrl, needBaseModuleChange } from '../../shared/regex.utils'; @Component({ selector: 'app-search-form', @@ -19,7 +19,7 @@ import { findOtherNetworks, getRegex } from '../../shared/regex.utils'; }) export class SearchFormComponent implements OnInit { @Input() hamburgerOpen = false; - + env: Env; network = ''; assets: object = {}; isSearching = false; @@ -68,6 +68,7 @@ export class SearchFormComponent implements OnInit { } ngOnInit(): void { + this.env = this.stateService.env; this.stateService.networkChanged$.subscribe((network) => { this.network = network; // TODO: Eventually change network type here from string to enum of consts @@ -103,9 +104,6 @@ export class SearchFormComponent implements OnInit { const searchText$ = this.searchForm.get('searchText').valueChanges .pipe( map((text) => { - if (this.network === 'bisq' && text.match(/^(b)[^c]/i)) { - return text.substr(1); - } return text.trim(); }), tap((text) => { @@ -139,9 +137,6 @@ export class SearchFormComponent implements OnInit { ); }), map((result: any[]) => { - if (this.network === 'bisq') { - result[0] = result[0].map((address: string) => 'B' + address); - } return result; }), tap(() => { @@ -171,6 +166,7 @@ export class SearchFormComponent implements OnInit { blockHeight: false, txId: false, address: false, + otherNetworks: [], addresses: [], nodes: [], channels: [], @@ -186,10 +182,13 @@ export class SearchFormComponent implements OnInit { const matchesUnixTimestamp = this.regexUnixTimestamp.test(searchText) && parseInt(searchText) <= Math.floor(Date.now() / 1000) && parseInt(searchText) >= 1231006505; // 1231006505 is the timestamp of the genesis block const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText); const matchesBlockHash = this.regexBlockhash.test(searchText); - const matchesAddress = !matchesTxId && this.regexAddress.test(searchText); + let matchesAddress = !matchesTxId && this.regexAddress.test(searchText); + const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet'); - if (matchesAddress && this.network === 'bisq') { - searchText = 'B' + searchText; + // Add B prefix to addresses in Bisq network + if (!matchesAddress && this.network === 'bisq' && getRegex('address', 'mainnet').test(searchText)) { + searchText = 'B' + searchText; + matchesAddress = !matchesTxId && this.regexAddress.test(searchText); } if (matchesDateTime && searchText.indexOf('/') !== -1) { @@ -205,7 +204,8 @@ export class SearchFormComponent implements OnInit { txId: matchesTxId, blockHash: matchesBlockHash, address: matchesAddress, - addresses: addressPrefixSearchResults, + addresses: matchesAddress && addressPrefixSearchResults.length === 1 && searchText === addressPrefixSearchResults[0] ? [] : addressPrefixSearchResults, // If there is only one address and it matches the search text, don't show it in the dropdown + otherNetworks: otherNetworks, nodes: lightningResults.nodes, channels: lightningResults.channels, }; @@ -230,6 +230,8 @@ export class SearchFormComponent implements OnInit { this.navigate('/lightning/node/', result.public_key); } else if (result.short_id) { this.navigate('/lightning/channel/', result.id); + } else if (result.network) { + this.navigate('/address/', result.address, undefined, result.network); } } @@ -238,12 +240,8 @@ export class SearchFormComponent implements OnInit { if (searchText) { this.isSearching = true; - const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet'); if (!this.regexTransaction.test(searchText) && this.regexAddress.test(searchText)) { this.navigate('/address/', searchText); - } else if (otherNetworks.length > 0) { - // Change the network to the first match - this.navigate('/address/', searchText, undefined, otherNetworks[0]); } else if (this.regexBlockhash.test(searchText)) { this.navigate('/block/', searchText); } else if (this.regexBlockheight.test(searchText)) { @@ -288,11 +286,15 @@ export class SearchFormComponent implements OnInit { navigate(url: string, searchText: string, extras?: any, swapNetwork?: string) { - this.router.navigate([this.relativeUrlPipe.transform(url, swapNetwork), searchText], extras); - this.searchTriggered.emit(); - this.searchForm.setValue({ - searchText: '', - }); - this.isSearching = false; + if (needBaseModuleChange(this.env.BASE_MODULE as 'liquid' | 'bisq' | 'mempool', swapNetwork as Network)) { + window.location.href = getTargetUrl(swapNetwork as Network, searchText, this.env); + } else { + this.router.navigate([this.relativeUrlPipe.transform(url, swapNetwork), searchText], extras); + this.searchTriggered.emit(); + this.searchForm.setValue({ + searchText: '', + }); + this.isSearching = false; + } } } diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.html b/frontend/src/app/components/search-form/search-results/search-results.component.html index d4f68edbd..adc92b0bf 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.html +++ b/frontend/src/app/components/search-form/search-results/search-results.component.html @@ -1,4 +1,4 @@ -
@@ -245,7 +247,9 @@
- + + + @@ -452,5 +456,24 @@ + + + + + + + + + + + + + + + + +
Total fees
Weight
Transactions
+
+

diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 4025a45d0..12ce14512 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -87,8 +87,8 @@ New fee Status - - + + @@ -158,8 +158,8 @@ {{ currency }} Fee - - + + @@ -199,6 +199,28 @@ + + + +
+
+
+
+ + +
+ + + + +
+
+
+
+ + +
+
From 0ec40eafb401629095573342fb2989f594620365 Mon Sep 17 00:00:00 2001 From: natsee Date: Fri, 12 Jan 2024 17:21:07 +0100 Subject: [PATCH 15/24] Remove hard-coded timestamp for regexes --- .../src/app/components/search-form/search-form.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index c00537f5a..745efce21 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -272,7 +272,7 @@ export class SearchFormComponent implements OnInit { let timestamp: number; this.regexDate.test(searchText) ? timestamp = Math.floor(new Date(searchText).getTime() / 1000) : timestamp = searchText; // Check if timestamp is too far in the future or before the genesis block - if (timestamp > Math.floor(Date.now() / 1000) || timestamp < 1231006505) { + if (timestamp > Math.floor(Date.now() / 1000)) { this.isSearching = false; return; } From 3b8de5057cc0eea4fdef84ede0a9d13ff10ec007 Mon Sep 17 00:00:00 2001 From: natsee Date: Fri, 12 Jan 2024 18:04:14 +0100 Subject: [PATCH 16/24] Check env flags before cross network address search --- .../search-form/search-form.component.ts | 11 +++++++-- .../search-results.component.html | 2 +- frontend/src/app/shared/regex.utils.ts | 23 +++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 745efce21..2a9983a24 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -186,7 +186,7 @@ export class SearchFormComponent implements OnInit { const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText); const matchesBlockHash = this.regexBlockhash.test(searchText); let matchesAddress = !matchesTxId && this.regexAddress.test(searchText); - const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet'); + const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet', this.env); // Add B prefix to addresses in Bisq network if (!matchesAddress && this.network === 'bisq' && getRegex('address', 'mainnet').test(searchText)) { @@ -234,7 +234,14 @@ export class SearchFormComponent implements OnInit { } else if (result.short_id) { this.navigate('/lightning/channel/', result.id); } else if (result.network) { - this.navigate('/address/', result.address, undefined, result.network); + if (result.isNetworkAvailable) { + this.navigate('/address/', result.address, undefined, result.network); + } else { + this.searchForm.setValue({ + searchText: '', + }); + this.isSearching = false; + } } } diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.html b/frontend/src/app/components/search-form/search-results/search-results.component.html index adc92b0bf..f83df6b93 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.html +++ b/frontend/src/app/components/search-form/search-results/search-results.component.html @@ -38,7 +38,7 @@
Other Networks Address
- diff --git a/frontend/src/app/shared/regex.utils.ts b/frontend/src/app/shared/regex.utils.ts index 607bd704a..128f7566e 100644 --- a/frontend/src/app/shared/regex.utils.ts +++ b/frontend/src/app/shared/regex.utils.ts @@ -144,10 +144,29 @@ export type Network = typeof NETWORKS[number]; // Turn const array into union ty export const ADDRESS_REGEXES: [RegExp, Network][] = NETWORKS .map(network => [getRegex('address', network), network]) -export function findOtherNetworks(address: string, skipNetwork: Network): {network: Network, address: string}[] { +export function findOtherNetworks(address: string, skipNetwork: Network, env: Env): { network: Network, address: string, isNetworkAvailable: boolean }[] { return ADDRESS_REGEXES .filter(([regex, network]) => network !== skipNetwork && regex.test(address)) - .map(([, network]) => ({ network, address })); + .map(([, network]) => ({ network, address, isNetworkAvailable: isNetworkAvailable(network, env) })); +} + +function isNetworkAvailable(network: Network, env: Env): boolean { + switch (network) { + case 'testnet': + return env.TESTNET_ENABLED === true; + case 'signet': + return env.SIGNET_ENABLED === true; + case 'liquid': + return env.LIQUID_ENABLED === true; + case 'liquidtestnet': + return env.LIQUID_TESTNET_ENABLED === true; + case 'bisq': + return env.BISQ_ENABLED === true; + case 'mainnet': + return true; // There is no "MAINNET_ENABLED" flag + default: + return false; + } } export function needBaseModuleChange(fromBaseModule: 'mempool' | 'liquid' | 'bisq', toNetwork: Network): boolean { From a06bf86b95c43118b13bceba2caa556e9a9db1a8 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Fri, 12 Jan 2024 13:06:11 -0800 Subject: [PATCH 17/24] Update Cypress to v13.6.2 --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 59bdac54a..f49fd614a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -58,7 +58,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.6.0", + "cypress": "^13.6.2", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", @@ -7083,9 +7083,9 @@ "peer": true }, "node_modules/cypress": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.0.tgz", - "integrity": "sha512-quIsnFmtj4dBUEJYU4OH0H12bABJpSujvWexC24Ju1gTlKMJbeT6tTO0vh7WNfiBPPjoIXLN+OUqVtiKFs6SGw==", + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz", + "integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -22272,9 +22272,9 @@ "peer": true }, "cypress": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.0.tgz", - "integrity": "sha512-quIsnFmtj4dBUEJYU4OH0H12bABJpSujvWexC24Ju1gTlKMJbeT6tTO0vh7WNfiBPPjoIXLN+OUqVtiKFs6SGw==", + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz", + "integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==", "optional": true, "requires": { "@cypress/request": "^3.0.0", diff --git a/frontend/package.json b/frontend/package.json index 8dbcbcf3e..330250871 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -110,7 +110,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.6.0", + "cypress": "^13.6.2", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", From fdd14fd6dcb4440b600062e725692a7532c248c4 Mon Sep 17 00:00:00 2001 From: wiz Date: Sat, 13 Jan 2024 12:22:23 +0900 Subject: [PATCH 18/24] ops: Disable disk cache for production mainnet --- production/mempool-config.mainnet.json | 1 + 1 file changed, 1 insertion(+) diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index 36310e59d..e3ce58ceb 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -3,6 +3,7 @@ "NETWORK": "mainnet", "BACKEND": "esplora", "HTTP_PORT": 8999, + "CACHE_ENABLED": false, "MINED_BLOCKS_CACHE": 144, "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", From 2ca6dc51da5c50e24753660a8d478b921b6b25af Mon Sep 17 00:00:00 2001 From: wiz Date: Sat, 13 Jan 2024 12:22:38 +0900 Subject: [PATCH 19/24] ops: Increase nginx proxy_read_timeout to 2m --- production/nginx/http-basic.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/production/nginx/http-basic.conf b/production/nginx/http-basic.conf index dc880cad5..fd5cc4b94 100644 --- a/production/nginx/http-basic.conf +++ b/production/nginx/http-basic.conf @@ -19,6 +19,8 @@ client_header_timeout 10s; keepalive_timeout 69s; # maximum time between packets nginx is allowed to pause when sending the client data send_timeout 69s; +# maximum time to wait for response from upstream backends +proxy_read_timeout 120s; # number of requests per connection, does not affect SPDY keepalive_requests 1337; From e4d7034d81b52ffc39e7ea25327d5241f9682c5d Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Sat, 13 Jan 2024 16:57:03 +0000 Subject: [PATCH 20/24] Translate frontend/src/locale/messages.xlf in nb 100% reviewed source file: 'frontend/src/locale/messages.xlf' on 'nb'. --- frontend/src/locale/messages.nb.xlf | 158 ++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/frontend/src/locale/messages.nb.xlf b/frontend/src/locale/messages.nb.xlf index a035d3765..7870acc32 100644 --- a/frontend/src/locale/messages.nb.xlf +++ b/frontend/src/locale/messages.nb.xlf @@ -59,6 +59,7 @@ + node_modules/src/ngb-config.ts 13 @@ -66,6 +67,7 @@ Slide of + Lysbilde av node_modules/src/ngb-config.ts 13 @@ -404,6 +406,7 @@ See current balance, pending transactions, and history of confirmed transactions for BSQ address . + Se gjeldende saldo, ventende transaksjoner og historikk for bekreftede transaksjoner for BSQ-adresse . src/app/bisq/bisq-address/bisq-address.component.ts 44 @@ -500,6 +503,7 @@ See all BSQ transactions in Bitcoin block (block hash ). + Se alle BSQ-transaksjoner i Bitcoin-blokk (blokkhash ). src/app/bisq/bisq-block/bisq-block.component.ts 92 @@ -627,6 +631,7 @@ See a list of recent Bitcoin blocks with BSQ transactions, total BSQ sent per block, and more. + Se en liste over nylige Bitcoin-blokker med BSQ-transaksjoner, totalt BSQ sendt per blokk og mer. src/app/bisq/bisq-blocks/bisq-blocks.component.ts 39 @@ -756,6 +761,7 @@ Markets + Markeder src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts 32 @@ -763,6 +769,7 @@ Explore the full Bitcoin ecosystem with The Mempool Open Source Project™. See Bisq market prices, trading activity, and more. + Utforsk hele Bitcoin-økosystemet med The Mempool Open Source Project™. Se Bisq markedspriser, handelsaktivitet og mer. src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts 33 @@ -848,6 +855,7 @@ Bisq market: + Bisq-markedet: src/app/bisq/bisq-market/bisq-market.component.ts 51 @@ -855,6 +863,7 @@ See price history, current buy/sell offers, and latest trades for the market on Bisq. + Se prishistorikk, gjeldende kjøps-/salgstilbud og siste handler for -markedet på Bisq. src/app/bisq/bisq-market/bisq-market.component.ts 52 @@ -990,6 +999,7 @@ See high-level stats on the BSQ economy: supply metrics, number of addresses, BSQ price, market cap, and more. + Se statistikk om BSQ-økonomien: forsyningsstatistikk, antall adresser, BSQ-pris, markedsverdi og mer. src/app/bisq/bisq-stats/bisq-stats.component.ts 29 @@ -1169,6 +1179,7 @@ Fee per weight unit + Avgift per vektenhet src/app/bisq/bisq-transaction/bisq-transaction.component.html 68 @@ -1237,6 +1248,7 @@ See inputs, outputs, transaction type, burnt amount, and more for transaction with txid . + Se innganger, utganger, transaksjonstype, brent beløp og mer for transaksjon med txid . src/app/bisq/bisq-transaction/bisq-transaction.component.ts 51 @@ -1426,6 +1438,7 @@ See recent BSQ transactions: amount, txid, associated Bitcoin block, transaction type, and more. + Se nylige BSQ-transaksjoner: beløp, txid, tilhørende Bitcoin-blokk, transaksjonstype og mer. src/app/bisq/bisq-transactions/bisq-transactions.component.ts 82 @@ -1466,6 +1479,7 @@ Become a Community Sponsor + Bli en samfunnssponsor src/app/components/about/about.component.html 39 @@ -1474,6 +1488,7 @@ Become an Enterprise Sponsor + Bli en bedriftssponsor src/app/components/about/about.component.html 46 @@ -1491,6 +1506,7 @@ Whale Sponsors + Hvalsponsorer src/app/components/about/about.component.html 202 @@ -1499,6 +1515,7 @@ Chad Sponsors + Chadsponsorer src/app/components/about/about.component.html 215 @@ -1507,6 +1524,7 @@ OG Sponsors ❤️ + OG Sponsorer ❤️ src/app/components/about/about.component.html 228 @@ -1589,6 +1607,7 @@ Learn more about The Mempool Open Source Project®: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more. + Lær mer om The Mempool Open Source Project®: bedriftssponsorer, individuelle sponsorer, integrasjoner, hvem som bidrar, FOSS-lisensiering og mer. src/app/components/about/about.component.ts 46 @@ -1818,7 +1837,8 @@ - See mempool transactions, confirmed transactions, balance, and more for address . + See mempool transactions, confirmed transactions, balance, and more for address . + Se mempool-transaksjoner, bekreftede transaksjoner, saldo og mer for adresse . src/app/components/address/address-preview.component.ts 72 @@ -2114,6 +2134,7 @@ Explore all the assets issued on the Liquid network like L-BTC, L-CAD, USDT, and more. + Utforsk alle eiendelene utstedt på Liquid-nettverket som L-BTC, L-CAD, USDT og mer. src/app/components/assets/assets-nav/assets-nav.component.ts 43 @@ -2276,6 +2297,7 @@ See Bitcoin feerates visualized over time, including minimum and maximum feerates per block along with feerates at various percentiles. + Se Bitcoin-gebyrer visualisert over tid, inkludert minimums- og maksimumsgebyrer per blokk sammen med gebyrer på forskjellige persentiler. src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts 67 @@ -2332,6 +2354,7 @@ See the average mining fees earned per Bitcoin block visualized in BTC and USD over time. + Se gjennomsnittlig utvinningsavgift tjent per Bitcoin-blokk visualisert i BTC og USD over tid. src/app/components/block-fees-graph/block-fees-graph.component.ts 68 @@ -2375,6 +2398,7 @@ Block Health + Blokkhelse src/app/components/block-health-graph/block-health-graph.component.html 6 @@ -2387,6 +2411,7 @@ Block Health + Blokkhelse src/app/components/block-health-graph/block-health-graph.component.ts 63 @@ -2394,6 +2419,7 @@ See Bitcoin block health visualized over time. Block health is a measure of how many expected transactions were included in an actual mined block. Expected transactions are determined using Mempool's re-implementation of Bitcoin Core's transaction selection algorithm. + Se Bitcoin-blokkhelse visualisert over tid. Blokkhelse er et mål på hvor mange forventede transaksjoner som ble inkludert i en faktisk utvunnet blokk. Forventede transaksjoner bestemmes ved hjelp av Mempools re-implementering av Bitcoin Cores transaksjonsvalgalgoritme. src/app/components/block-health-graph/block-health-graph.component.ts 64 @@ -2416,7 +2442,7 @@ src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts - 215 + 216 src/app/lightning/nodes-map/nodes-map.component.ts @@ -2425,6 +2451,7 @@ Health + Helse src/app/components/block-health-graph/block-health-graph.component.ts 190 @@ -2515,6 +2542,7 @@ Accelerated fee rate + Akselerert gebyrsats src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 33 @@ -2546,6 +2574,7 @@ Weight + Vekt src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 43 @@ -2603,6 +2632,7 @@ High sigop count + Høyt antall sigops src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 52 @@ -2620,6 +2650,7 @@ Recently CPFP'd + Nylig CPFPet src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 54 @@ -2637,6 +2668,7 @@ Conflicting + Motstridende src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 57 @@ -2645,6 +2677,7 @@ Accelerated + Akselerert src/app/components/block-overview-tooltip/block-overview-tooltip.component.html 58 @@ -2670,6 +2703,7 @@ See Bitcoin block rewards in BTC and USD visualized over time. Block rewards are the total funds miners earn from the block subsidy and fees. + Se Bitcoin-blokkbelønninger i BTC og USD visualisert over tid. Blokkbelønninger er de totale midlene utvinnerene tjener fra blokksubsidier og gebyr. src/app/components/block-rewards-graph/block-rewards-graph.component.ts 66 @@ -2694,6 +2728,7 @@ See Bitcoin block sizes (MB) and block weights (weight units) visualized over time. + Se Bitcoin-blokkstørrelser (MB) og blokkvekter (vektenheter) visualisert over tid. src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts 63 @@ -2809,6 +2844,7 @@ See size, weight, fee range, included transactions, and more for Liquid block (). + Se størrelse, vekt, gebyrområde, inkluderte transaksjoner og mer for Liquid blokk (). src/app/components/block-view/block-view.component.ts 112 @@ -2824,6 +2860,7 @@ See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin block (). + Se størrelse, vekt, gebyrområde, inkluderte transaksjoner, revisjon (forventet v faktisk) og mer for Bitcoin blokk ( ). src/app/components/block-view/block-view.component.ts 114 @@ -2912,6 +2949,7 @@ This block does not belong to the main chain, it has been replaced by: + Denne blokken tilhører ikke hovedkjeden, den er erstattet av: src/app/components/block/block.component.html 5 @@ -2947,6 +2985,7 @@ Stale + Foreldet src/app/components/block/block.component.html 30 @@ -3412,6 +3451,7 @@ Blocks + Blokker src/app/components/blocks-list/blocks-list.component.ts 59 @@ -3419,6 +3459,7 @@ See the most recent Liquid blocks along with basic stats such as block height, block size, and more. + Se de nyeste Liquid-blokkene sammen med grunnleggende statistikk som blokkhøyde, blokkstørrelse og mer. src/app/components/blocks-list/blocks-list.component.ts 62 @@ -3426,6 +3467,7 @@ See the most recent Bitcoin blocks along with basic stats such as block height, block reward, block size, and more. + Se de nyeste Bitcoin-blokkene sammen med grunnleggende statistikk som blokkhøyde, blokkbelønning, blokkstørrelse og mer. src/app/components/blocks-list/blocks-list.component.ts 64 @@ -3433,6 +3475,7 @@ Calculator + Kalkulator src/app/components/calculator/calculator.component.html 3 @@ -3470,6 +3513,7 @@ Memory Usage + Minnebruk src/app/components/clock/clock.component.html 65 @@ -3549,6 +3593,7 @@ blocks + blokker src/app/components/difficulty-mining/difficulty-mining.component.html 10,11 @@ -3852,6 +3897,7 @@ WU/s + WU/s src/app/components/footer/footer.component.html 14 @@ -4024,7 +4070,7 @@ src/app/lightning/nodes-channels-map/nodes-channels-map.component.html - 6 + 19 lightning.nodes-channels-world-map @@ -4072,6 +4118,7 @@ See hashrate and difficulty for the Bitcoin network visualized over time. + Se hashrate og vanskelighetsgrad for Bitcoin-nettverket visualisert over tid. src/app/components/hashrate-chart/hashrate-chart.component.ts 75 @@ -4179,6 +4226,7 @@ See stats for transactions in the mempool: fee range, aggregate size, and more. Mempool blocks are updated in real-time as the network receives new transactions. + Se statistikk for transaksjoner i mempoolen: gebyrområde, samlet størrelse og mer. Mempool-blokker oppdateres i sanntid etter hvert som nettverket mottar nye transaksjoner. src/app/components/mempool-block/mempool-block.component.ts 58 @@ -4202,6 +4250,7 @@ Count + Antall src/app/components/mempool-graph/mempool-graph.component.ts 325 @@ -4229,6 +4278,7 @@ Sign in + Logg inn src/app/components/menu/menu.component.html 10 @@ -4255,6 +4305,7 @@ Recent Blocks + Nylige blokker src/app/components/mining-dashboard/mining-dashboard.component.html 52 @@ -4280,6 +4331,7 @@ Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more. + Få sanntids Bitcoin-utvinningsstatistikk som hashrate, vanskeliggradsjustering, blokkbelønninger, gruppedominans og mer. src/app/components/mining-dashboard/mining-dashboard.component.ts 21 @@ -4296,6 +4348,7 @@ Pools Luck + Gruppeflaks src/app/components/pool-ranking/pool-ranking.component.html 9 @@ -4326,6 +4379,7 @@ Pools Count + Antall grupper src/app/components/pool-ranking/pool-ranking.component.html 17 @@ -4440,6 +4494,7 @@ Empty Blocks + Tomme blokker src/app/components/pool-ranking/pool-ranking.component.html 98 @@ -4465,6 +4520,7 @@ See the top Bitcoin mining pools ranked by number of blocks mined, over your desired timeframe. + Se de beste utvinningsgruppene rangert etter antall blokker utvunnet, over ønsket tidsramme. src/app/components/pool-ranking/pool-ranking.component.ts 59 @@ -4554,6 +4610,7 @@ See mining pool stats for : most recent mined blocks, hashrate over time, total block reward to date, known coinbase addresses, and more. + Se utvinningsgruppe-statistikk for : siste utvunnede blokker, hashrate over tid, total blokkbelønning hittil, kjente coinbaseadresser og mer. src/app/components/pool/pool-preview.component.ts 86 @@ -4620,6 +4677,7 @@ Blocks (24h) + Blokker (24 timer) src/app/components/pool/pool.component.html 146 @@ -4734,6 +4792,7 @@ Broadcast Transaction + Kringkast transaksjon src/app/components/push-transaction/push-transaction.component.ts 31 @@ -4741,6 +4800,7 @@ Broadcast a transaction to the network using the transaction's hash. + Kringkast en transaksjon til -nettverket ved å bruke transaksjonens hash. src/app/components/push-transaction/push-transaction.component.ts 32 @@ -4748,6 +4808,7 @@ RBF Replacements + RBF-erstatninger src/app/components/rbf-list/rbf-list.component.html 2 @@ -4760,6 +4821,7 @@ Full RBF + Full RBF src/app/components/rbf-list/rbf-list.component.html 24 @@ -4776,6 +4838,7 @@ There are no replacements in the mempool yet! + Det er ingen erstatninger i mempoolen ennå! src/app/components/rbf-list/rbf-list.component.html 34 @@ -4784,6 +4847,7 @@ See the most recent RBF replacements on the Bitcoin network, updated in real-time. + Se de siste RBF-erstatningene på Bitcoin-nettverket, oppdatert i sanntid. src/app/components/rbf-list/rbf-list.component.ts 59 @@ -4829,6 +4893,7 @@ Status + Status src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html 33 @@ -4846,6 +4911,7 @@ RBF + RBF src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html 36 @@ -5065,6 +5131,7 @@ Clock (Mempool) + Klokke (Mempool) src/app/components/statistics/statistics.component.html 17 @@ -5117,6 +5184,7 @@ Cap outliers + Fjern avvikere src/app/components/statistics/statistics.component.html 122 @@ -5125,6 +5193,7 @@ See mempool size (in MvB) and transactions per second (in vB/s) visualized over time. + Se mempoolstørrelse (i MvB) og transaksjoner per sekund (i vB/s) visualisert over tid. src/app/components/statistics/statistics.component.ts 67 @@ -5132,6 +5201,7 @@ See Bitcoin blocks and mempool congestion in real-time in a simplified format perfect for a TV. + Se Bitcoin-blokker og mempool i sanntid i et forenklet format perfekt for en TV. src/app/components/television/television.component.ts 40 @@ -5343,7 +5413,8 @@ transactions-list.coinbase - Get real-time status, addresses, fees, script info, and more for transaction with txid {txid}. + Get real-time status, addresses, fees, script info, and more for transaction with txid . + Få sanntidsstatus, adresser, gebyrer, skriptinformasjon og mer for transaksjon med txid . src/app/components/transaction/transaction-preview.component.ts 91 @@ -5385,6 +5456,7 @@ Accelerate + Akselerer src/app/components/transaction/transaction.component.html 123 @@ -5422,6 +5494,7 @@ RBF History + RBF historie src/app/components/transaction/transaction.component.html 218 @@ -5493,6 +5566,7 @@ Adjusted vsize + Justert vsize src/app/components/transaction/transaction.component.html 298 @@ -5511,6 +5585,7 @@ Sigops + Sigops src/app/components/transaction/transaction.component.html 320 @@ -5538,6 +5613,7 @@ Accelerated fee rate + Akselerert gebyrsats src/app/components/transaction/transaction.component.html 516 @@ -5893,6 +5969,7 @@ Recent Replacements + Nylige erstatninger src/app/dashboard/dashboard.component.html 79 @@ -5901,6 +5978,7 @@ Previous fee + Tidligere avgift src/app/dashboard/dashboard.component.html 86 @@ -5909,6 +5987,7 @@ New fee + Ny avgift src/app/dashboard/dashboard.component.html 87 @@ -5917,6 +5996,7 @@ Recent Transactions + Nylige transaksjoner src/app/dashboard/dashboard.component.html 153 @@ -5954,6 +6034,7 @@ Incoming Transactions + Innkommende transaksjoner src/app/dashboard/dashboard.component.html 259 @@ -5962,6 +6043,7 @@ mempool.space merely provides data about the Bitcoin network. It cannot help you with retrieving funds, wallet issues, etc.For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc). + mempool.space gir bare data om Bitcoin-nettverket. Det kan ikke hjelpe deg med å hente midler, lommebokproblemer osv.For slike forespørsler må du ta kontakt med enheten som bidro til transaksjonen (lommebokprogramvare, børsselskap osv.). src/app/docs/api-docs/api-docs.component.html 15,16 @@ -6056,6 +6138,7 @@ FAQ + FAQ src/app/docs/docs/docs.component.ts 45 @@ -6063,6 +6146,7 @@ Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more. + Få svar på vanlige spørsmål som: Hva er en mempool? Hvorfor bekreftes ikke transaksjonen min? Hvordan kan jeg kjøre min egen instans av Mempool Open Source Project? Og mer. src/app/docs/docs/docs.component.ts 46 @@ -6070,6 +6154,7 @@ REST API + REST API src/app/docs/docs/docs.component.ts 49 @@ -6077,6 +6162,7 @@ Documentation for the liquid.network REST API service: get info on addresses, transactions, assets, blocks, and more. + Dokumentasjon for liquid.network REST API-tjenesten: få informasjon om adresser, transaksjoner, eiendeler, blokker og mer. src/app/docs/docs/docs.component.ts 51 @@ -6084,6 +6170,7 @@ Documentation for the bisq.markets REST API service: get info on recent trades, current offers, transactions, network state, and more. + Dokumentasjon for bisq.markets REST API-tjenesten: få informasjon om nylige handler, nåværende tilbud, transaksjoner, nettverkstilstand og mer. src/app/docs/docs/docs.component.ts 53 @@ -6091,6 +6178,7 @@ Documentation for the mempool.space REST API service: get info on addresses, transactions, blocks, fees, mining, the Lightning network, and more. + Dokumentasjon for mempool.space REST API-tjenesten: få informasjon om adresser, transaksjoner, blokker, avgifter, utvinning, Lightning-nettverket og mer. src/app/docs/docs/docs.component.ts 55 @@ -6098,6 +6186,7 @@ WebSocket API + WebSocket API src/app/docs/docs/docs.component.ts 59 @@ -6105,6 +6194,7 @@ Documentation for the liquid.network WebSocket API service: get real-time info on blocks, mempools, transactions, addresses, and more. + Dokumentasjon for liquid.network WebSocket API-tjenesten: få sanntidsinformasjon om blokker, mempooler, transaksjoner, adresser og mer. src/app/docs/docs/docs.component.ts 61 @@ -6112,6 +6202,7 @@ Documentation for the mempool.space WebSocket API service: get real-time info on blocks, mempools, transactions, addresses, and more. + Dokumentasjon for mempool.space WebSocket API-tjenesten: få sanntidsinformasjon om blokker, mempooler, transaksjoner, adresser og mer. src/app/docs/docs/docs.component.ts 63 @@ -6119,6 +6210,7 @@ Electrum RPC + Electrum RPC src/app/docs/docs/docs.component.ts 67 @@ -6126,6 +6218,7 @@ Documentation for our Electrum RPC interface: get instant, convenient, and reliable access to an Esplora instance. + Dokumentasjon for vårt Electrum RPC-grensesnitt: få umiddelbar, praktisk og pålitelig tilgang til en Esplora-instans. src/app/docs/docs/docs.component.ts 68 @@ -6438,6 +6531,7 @@ Overview for Lightning channel . See channel capacity, the Lightning nodes involved, related on-chain transactions, and more. + Oversikt for Lightning-kanalen . Se kanalkapasitet, Lightning-nodene som er involvert, relaterte transaksjoner i kjeden og mer. src/app/lightning/channel/channel-preview.component.ts 37 @@ -6936,6 +7030,7 @@ Connect + Koble til src/app/lightning/group/group.component.html 73 @@ -6986,6 +7081,7 @@ Penalties + Straffer src/app/lightning/justice-list/justice-list.component.html 4 @@ -7062,7 +7158,8 @@ lightning.connectivity-ranking - Get stats on the Lightning network (aggregate capacity, connectivity, etc) and Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc). + Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc). + Få statistikk om Lightning-nettverket (samlet kapasitet, tilkobling osv.), Lightning-noder (kanaler, likviditet osv.) og Lightning-kanaler (status, avgifter osv.). src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts 28 @@ -7172,6 +7269,7 @@ Overview for the Lightning network node named . See channels, capacity, location, fee stats, and more. + Oversikt for Lightning-nettverksnoden . Se kanaler, kapasitet, plassering, gebyrstatistikk og mer. src/app/lightning/node/node-preview.component.ts 52 @@ -7232,6 +7330,7 @@ Decoded + Dekodet src/app/lightning/node/node.component.html 136 @@ -7344,7 +7443,7 @@ (Tor-noder ekskludert) src/app/lightning/nodes-channels-map/nodes-channels-map.component.html - 8 + 21 src/app/lightning/nodes-map/nodes-map.component.html @@ -7365,14 +7464,15 @@ Lightning-kanaler verdenskart src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts - 68 + 69 See the channels of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details. + Se kanalene til ikke-Tor Lightning-nettverksnoder visualisert på et verdenskart. Hold musepekeren/trykk på punkter på kartet for nodenavn og detaljer. src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts - 69 + 70 @@ -7380,7 +7480,7 @@ Ingen geolokaliseringsdata tilgjengelig src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts - 227 + 228 @@ -7394,6 +7494,7 @@ See the locations of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details. + Se plasseringen til ikke-Tor Lightning-nettverksnoder visualisert på et verdenskart. Hold musepekeren/trykk på punkter på kartet for nodenavn og detaljer. src/app/lightning/nodes-map/nodes-map.component.ts 51 @@ -7401,6 +7502,7 @@ See the number of Lightning network nodes visualized over time by network: clearnet only (IPv4, IPv6), darknet (Tor, I2p, cjdns), and both. + Se antall Lightning-nettverksnoder visualisert over tid etter nettverk: bare clearnet (IPv4, IPv6), darknet (Tor, I2p, cjdns) og begge deler. src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts 68 @@ -7469,6 +7571,7 @@ See a geographical breakdown of the Lightning network: how many Lightning nodes are hosted in countries around the world, aggregate BTC capacity for each country, and more. + Se en geografisk inndeling av Lightning-nettverket: hvor mange Lightning-noder er det i land rundt om i verden, samlet BTC-kapasitet for hvert land, og mer. src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts 47 @@ -7539,6 +7642,7 @@ Explore all the Lightning nodes hosted in and see an overview of each node's capacity, number of open channels, and more. + Utforsk alle Lightning-nodene som er i og se en oversikt over hver nodes kapasitet, antall åpne kanaler og mer. src/app/lightning/nodes-per-country/nodes-per-country.component.ts 36 @@ -7689,6 +7793,7 @@ Browse all Bitcoin Lightning nodes using the [AS] ISP and see aggregate stats like total number of nodes, total capacity, and more for the ISP. + Bla gjennom alle Bitcoin Lightning-noder ved å bruke [AS] ISP og se samlet statistikk som totalt antall noder, total kapasitet, og mer for Internett-leverandøren. src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts 45 @@ -7744,6 +7849,7 @@ See the oldest nodes on the Lightning network along with their capacity, number of channels, location, etc. + Se de eldste nodene på Lightning-nettverket sammen med deres kapasitet, antall kanaler, plassering osv. src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts 28 @@ -7751,6 +7857,7 @@ See Lightning nodes with the most BTC liquidity deployed along with high-level stats like number of open channels, location, node age, and more. + Se Lightning-noder med mest BTC-likviditet utplassert sammen med statistikk som antall åpne kanaler, plassering, nodens alder og mer. src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts 34 @@ -7758,6 +7865,7 @@ See Lightning nodes with the most channels open along with high-level stats like total node capacity, node age, and more. + Se Lightning-noder med flest åpne kanaler sammen med statistikk som total nodekapasitet, nodealder og mer. src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts 38 @@ -7781,7 +7889,8 @@ - See top the Lightning network nodes ranked by liquidity, connectivity, and age. + See the top Lightning network nodes ranked by liquidity, connectivity, and age. + Se de beste Lightning-nettverksnodene rangert etter likviditet, tilkobling og alder. src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts 23 @@ -7789,6 +7898,7 @@ See the capacity of the Lightning network visualized over time in terms of the number of open channels and total bitcoin capacity. + Se kapasiteten til Lightning-nettverket visualisert over tid i form av antall åpne kanaler og total bitcoin-kapasitet. src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts 67 @@ -7796,6 +7906,7 @@ confirmation + bekreftelse src/app/shared/components/confirmations/confirmations.component.html 4 @@ -7805,6 +7916,7 @@ confirmations + bekreftelser src/app/shared/components/confirmations/confirmations.component.html 5 @@ -7814,6 +7926,7 @@ Replaced + Erstattet src/app/shared/components/confirmations/confirmations.component.html 12 @@ -7823,6 +7936,7 @@ Removed + Fjernet src/app/shared/components/confirmations/confirmations.component.html 15 @@ -7842,6 +7956,7 @@ sat/WU + sat/WU src/app/shared/components/fee-rate/fee-rate.component.html 3 @@ -7851,6 +7966,7 @@ My Account + Min konto src/app/shared/components/global-footer/global-footer.component.html 25 @@ -7863,6 +7979,7 @@ Sign In + Logg inn src/app/shared/components/global-footer/global-footer.component.html 26 @@ -7875,6 +7992,7 @@ Explore + Utforsk src/app/shared/components/global-footer/global-footer.component.html 41 @@ -7883,6 +8001,7 @@ Connect to our Nodes + Koble til nodene våre src/app/shared/components/global-footer/global-footer.component.html 46 @@ -7891,6 +8010,7 @@ API Documentation + API-dokumentasjon src/app/shared/components/global-footer/global-footer.component.html 47 @@ -7899,6 +8019,7 @@ Learn + Lær src/app/shared/components/global-footer/global-footer.component.html 50 @@ -7907,6 +8028,7 @@ What is a mempool? + Hva er en mempool? src/app/shared/components/global-footer/global-footer.component.html 51 @@ -7915,6 +8037,7 @@ What is a block explorer? + Hva er en blokkutforsker? src/app/shared/components/global-footer/global-footer.component.html 52 @@ -7923,6 +8046,7 @@ What is a mempool explorer? + Hva er en mempool utforsker? src/app/shared/components/global-footer/global-footer.component.html 53 @@ -7931,6 +8055,7 @@ Why isn't my transaction confirming? + Hvorfor bekreftes ikke transaksjonen min? src/app/shared/components/global-footer/global-footer.component.html 54 @@ -7939,6 +8064,7 @@ More FAQs » + Flere vanlige spørsmål » src/app/shared/components/global-footer/global-footer.component.html 55 @@ -7947,6 +8073,7 @@ Networks + Nettverk src/app/shared/components/global-footer/global-footer.component.html 59 @@ -7955,6 +8082,7 @@ Mainnet Explorer + Mainnet utforsker src/app/shared/components/global-footer/global-footer.component.html 60 @@ -7963,6 +8091,7 @@ Testnet Explorer + Testnet utforsker src/app/shared/components/global-footer/global-footer.component.html 61 @@ -7971,6 +8100,7 @@ Signet Explorer + Signet utforsker src/app/shared/components/global-footer/global-footer.component.html 62 @@ -7979,6 +8109,7 @@ Liquid Testnet Explorer + Liquid testnet utforsker src/app/shared/components/global-footer/global-footer.component.html 63 @@ -7987,6 +8118,7 @@ Liquid Explorer + Liquid utforsker src/app/shared/components/global-footer/global-footer.component.html 64 @@ -7995,6 +8127,7 @@ Bisq Explorer + Bisq utforsker src/app/shared/components/global-footer/global-footer.component.html 65 @@ -8003,6 +8136,7 @@ Tools + Verktøy src/app/shared/components/global-footer/global-footer.component.html 69 @@ -8011,6 +8145,7 @@ Clock (Mined) + Klokke (utvunnet) src/app/shared/components/global-footer/global-footer.component.html 71 @@ -8019,6 +8154,7 @@ Legal + Lovlig src/app/shared/components/global-footer/global-footer.component.html 76 @@ -8047,6 +8183,7 @@ Trademark Policy + Varemerkepolitikk src/app/shared/components/global-footer/global-footer.component.html 79 @@ -8056,6 +8193,7 @@ This is a test network. Coins have no value. + Dette er et testnettverk. Mynter har ingen verdi. src/app/shared/components/testnet-alert/testnet-alert.component.html 3 From 65236c7b08c63498f849a695767fccdecdf4a074 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Sat, 13 Jan 2024 13:55:12 -0800 Subject: [PATCH 21/24] Add seconds to the block view timestamp --- frontend/src/app/components/block/block.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index b34b39c8c..885f8e1ac 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -47,7 +47,7 @@ Timestamp - + From a8442a3467d808d63406a38a90680db9f8e1d70b Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Sat, 13 Jan 2024 18:11:13 -0800 Subject: [PATCH 22/24] Add seconds to block timestamp in the mining pool view and blocks list --- .../src/app/components/blocks-list/blocks-list.component.html | 2 +- frontend/src/app/components/pool/pool.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 85e2ea17f..838c7cb4e 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -46,7 +46,7 @@ - ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} + ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}
{{ block.height }} - ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} + ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm:ss' }} From 0722c221c78729f3954e5d2e2f620c7fa52e4200 Mon Sep 17 00:00:00 2001 From: softsimon Date: Sun, 14 Jan 2024 11:24:56 +0700 Subject: [PATCH 23/24] Minor adjustments --- .../search-form/search-results/search-results.component.html | 4 ++-- .../search-form/search-results/search-results.component.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.html b/frontend/src/app/components/search-form/search-results/search-results.component.html index f83df6b93..5d64b7495 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.html +++ b/frontend/src/app/components/search-form/search-results/search-results.component.html @@ -36,10 +36,10 @@ -
Other Networks Address
+
Other Network Address
diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.ts b/frontend/src/app/components/search-form/search-results/search-results.component.ts index 84c541a70..ade01bf89 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.ts +++ b/frontend/src/app/components/search-form/search-results/search-results.component.ts @@ -45,6 +45,9 @@ export class SearchResultsComponent implements OnChanges { break; case 'Enter': event.preventDefault(); + if (this.resultsFlattened[this.activeIdx]?.isNetworkAvailable === false) { + return; + } if (this.resultsFlattened[this.activeIdx]) { this.selectedResult.emit(this.resultsFlattened[this.activeIdx]); } else { From 00b8f001b0d9f37458f6979fb209c7e39b7cb5ca Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 15 Jan 2024 02:47:29 +0000 Subject: [PATCH 24/24] Fix goggles inscription detection bug --- backend/src/api/common.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 358a98c98..af93b9622 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -263,8 +263,13 @@ export class Common { case 'v0_p2wsh': flags |= TransactionFlags.p2wsh; break; case 'v1_p2tr': { flags |= TransactionFlags.p2tr; - if (vin.witness.length > 2) { - const asm = vin.inner_witnessscript_asm || transactionUtils.convertScriptSigAsm(vin.witness[vin.witness.length - 2]); + // in taproot, if the last witness item begins with 0x50, it's an annex + const hasAnnex = vin.witness?.[vin.witness.length - 1].startsWith('50'); + // script spends have more than one witness item, not counting the annex (if present) + if (vin.witness.length > (hasAnnex ? 2 : 1)) { + // the script itself is the second-to-last witness item, not counting the annex + const asm = vin.inner_witnessscript_asm || transactionUtils.convertScriptSigAsm(vin.witness[vin.witness.length - (hasAnnex ? 3 : 2)]); + // inscriptions smuggle data within an 'OP_0 OP_IF ... OP_ENDIF' envelope if (asm?.includes('OP_0 OP_IF')) { flags |= TransactionFlags.inscription; }