Update regexes in regex.utils.ts
This commit is contained in:
parent
6cea6ceb7e
commit
bd34d71d8b
@ -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<string>();
|
||||
click$ = new Subject<string>();
|
||||
@ -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; }
|
||||
|
@ -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') {
|
||||
|
@ -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)`);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user