From 74b87b60065b617b93cbcec6219a0f2281f26387 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 26 Jul 2023 10:47:59 +0900 Subject: [PATCH 1/3] Support p2pk track-address websocket subscriptions --- backend/src/api/transaction-utils.ts | 9 ++++ backend/src/api/websocket-handler.ts | 76 +++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index 0b10afdfb..b8a9a108a 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -3,6 +3,7 @@ import { IEsploraApi } from './bitcoin/esplora-api.interface'; import { Common } from './common'; import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory'; import * as bitcoinjs from 'bitcoinjs-lib'; +import crypto from 'node:crypto'; class TransactionUtils { constructor() { } @@ -170,6 +171,14 @@ class TransactionUtils { 16 ); } + + public calcScriptHash(script: string): string { + if (!/^[0-9a-fA-F]*$/.test(script) || script.length % 2 !== 0) { + throw new Error('script is not a valid hex string'); + } + const buf = Buffer.from(script, 'hex'); + return crypto.createHash('sha256').update(buf).digest('hex'); + } } export default new TransactionUtils(); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 56c8513cd..3438e0e0c 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -183,15 +183,22 @@ class WebsocketHandler { } if (parsedMessage && parsedMessage['track-address']) { - if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/ + if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[0-9a-fA-F]{130})$/ .test(parsedMessage['track-address'])) { let matchedAddress = parsedMessage['track-address']; if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(parsedMessage['track-address'])) { matchedAddress = matchedAddress.toLowerCase(); } - client['track-address'] = matchedAddress; + if (/^[0-9a-fA-F]{130}$/.test(parsedMessage['track-address'])) { + client['track-address'] = null; + client['track-scripthash'] = transactionUtils.calcScriptHash('41' + matchedAddress + 'ac'); + } else { + client['track-address'] = matchedAddress; + client['track-scripthash'] = null; + } } else { client['track-address'] = null; + client['track-scripthash'] = null; } } @@ -546,6 +553,44 @@ class WebsocketHandler { } } + if (client['track-scripthash']) { + const foundTransactions: TransactionExtended[] = []; + + for (const tx of newTransactions) { + const someVin = tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scripthash']); + if (someVin) { + if (config.MEMPOOL.BACKEND !== 'esplora') { + try { + const fullTx = await transactionUtils.$getMempoolTransactionExtended(tx.txid, true); + foundTransactions.push(fullTx); + } catch (e) { + logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e)); + } + } else { + foundTransactions.push(tx); + } + return; + } + const someVout = tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scripthash']); + if (someVout) { + if (config.MEMPOOL.BACKEND !== 'esplora') { + try { + const fullTx = await transactionUtils.$getMempoolTransactionExtended(tx.txid, true); + foundTransactions.push(fullTx); + } catch (e) { + logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e)); + } + } else { + foundTransactions.push(tx); + } + } + } + + if (foundTransactions.length) { + response['address-transactions'] = JSON.stringify(foundTransactions); + } + } + if (client['track-asset']) { const foundTransactions: TransactionExtended[] = []; @@ -821,6 +866,33 @@ class WebsocketHandler { } } + if (client['track-scripthash']) { + const foundTransactions: TransactionExtended[] = []; + + transactions.forEach((tx) => { + if (tx.vin && tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scripthash'])) { + foundTransactions.push(tx); + return; + } + if (tx.vout && tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scripthash'])) { + foundTransactions.push(tx); + } + }); + + if (foundTransactions.length) { + foundTransactions.forEach((tx) => { + tx.status = { + confirmed: true, + block_height: block.height, + block_hash: block.id, + block_time: block.timestamp, + }; + }); + + response['block-transactions'] = JSON.stringify(foundTransactions); + } + } + if (client['track-asset']) { const foundTransactions: TransactionExtended[] = []; From 5b2470955d480656f2f3e636bc257a9da3f7bf0d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 28 Jul 2023 16:04:03 +0900 Subject: [PATCH 2/3] track p2pk addresses by scriptpubkey not scripthash --- backend/src/api/websocket-handler.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 3438e0e0c..74c4ed832 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -191,14 +191,14 @@ class WebsocketHandler { } if (/^[0-9a-fA-F]{130}$/.test(parsedMessage['track-address'])) { client['track-address'] = null; - client['track-scripthash'] = transactionUtils.calcScriptHash('41' + matchedAddress + 'ac'); + client['track-scriptpubkey'] = '41' + matchedAddress + 'ac'; } else { client['track-address'] = matchedAddress; - client['track-scripthash'] = null; + client['track-scriptpubkey'] = null; } } else { client['track-address'] = null; - client['track-scripthash'] = null; + client['track-scriptpubkey'] = null; } } @@ -553,11 +553,11 @@ class WebsocketHandler { } } - if (client['track-scripthash']) { + if (client['track-scriptpubkey']) { const foundTransactions: TransactionExtended[] = []; for (const tx of newTransactions) { - const someVin = tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scripthash']); + const someVin = tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scriptpubkey']); if (someVin) { if (config.MEMPOOL.BACKEND !== 'esplora') { try { @@ -571,7 +571,7 @@ class WebsocketHandler { } return; } - const someVout = tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scripthash']); + const someVout = tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scriptpubkey']); if (someVout) { if (config.MEMPOOL.BACKEND !== 'esplora') { try { @@ -866,15 +866,15 @@ class WebsocketHandler { } } - if (client['track-scripthash']) { + if (client['track-scriptpubkey']) { const foundTransactions: TransactionExtended[] = []; transactions.forEach((tx) => { - if (tx.vin && tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scripthash'])) { + if (tx.vin && tx.vin.some((vin) => !!vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk' && vin.prevout.scriptpubkey === client['track-scriptpubkey'])) { foundTransactions.push(tx); return; } - if (tx.vout && tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scripthash'])) { + if (tx.vout && tx.vout.some((vout) => vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey === client['track-scriptpubkey'])) { foundTransactions.push(tx); } }); From 63ccecf4107823194787e0aca8d7309f0dfde9df Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 28 Jul 2023 16:14:28 +0900 Subject: [PATCH 3/3] remove unused calcScriptHash function --- backend/src/api/transaction-utils.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index b8a9a108a..0b10afdfb 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -3,7 +3,6 @@ import { IEsploraApi } from './bitcoin/esplora-api.interface'; import { Common } from './common'; import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory'; import * as bitcoinjs from 'bitcoinjs-lib'; -import crypto from 'node:crypto'; class TransactionUtils { constructor() { } @@ -171,14 +170,6 @@ class TransactionUtils { 16 ); } - - public calcScriptHash(script: string): string { - if (!/^[0-9a-fA-F]*$/.test(script) || script.length % 2 !== 0) { - throw new Error('script is not a valid hex string'); - } - const buf = Buffer.from(script, 'hex'); - return crypto.createHash('sha256').update(buf).digest('hex'); - } } export default new TransactionUtils();