Page MenuHomePhabricator

D14325.diff
No OneTemporary

D14325.diff

diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json
--- a/cashtab/package-lock.json
+++ b/cashtab/package-lock.json
@@ -41,7 +41,7 @@
"css-minimizer-webpack-plugin": "^3.2.0",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
- "ecashaddrjs": "^1.3.0",
+ "ecashaddrjs": "^1.5.2",
"ecies-lite": "^1.0.7",
"eslint": "^8.3.0",
"eslint-config-react-app": "^7.0.0",
@@ -7693,9 +7693,9 @@
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
},
"node_modules/ecashaddrjs": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/ecashaddrjs/-/ecashaddrjs-1.3.0.tgz",
- "integrity": "sha512-J0HN4g6GD/AhMUahCu0KD/Lon0lbSKjXjjeRZ0r9HbD2qZUldcLuuykqA/sgpKGf1z4g0dYsSD1oXaaIYHShAA==",
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/ecashaddrjs/-/ecashaddrjs-1.5.2.tgz",
+ "integrity": "sha512-zzpDxdVR6SmIym/s1OUo5gEzkH86ztHKbdzuFGet8vUpV0nIejOcW8JiTiq74PHAcIKVb9bQ7ZU7F1Ryw6F6xA==",
"dependencies": {
"big-integer": "1.6.36",
"bs58check": "^3.0.1"
@@ -25438,9 +25438,9 @@
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
},
"ecashaddrjs": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/ecashaddrjs/-/ecashaddrjs-1.3.0.tgz",
- "integrity": "sha512-J0HN4g6GD/AhMUahCu0KD/Lon0lbSKjXjjeRZ0r9HbD2qZUldcLuuykqA/sgpKGf1z4g0dYsSD1oXaaIYHShAA==",
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/ecashaddrjs/-/ecashaddrjs-1.5.2.tgz",
+ "integrity": "sha512-zzpDxdVR6SmIym/s1OUo5gEzkH86ztHKbdzuFGet8vUpV0nIejOcW8JiTiq74PHAcIKVb9bQ7ZU7F1Ryw6F6xA==",
"requires": {
"big-integer": "1.6.36",
"bs58check": "^3.0.1"
diff --git a/cashtab/package.json b/cashtab/package.json
--- a/cashtab/package.json
+++ b/cashtab/package.json
@@ -36,7 +36,7 @@
"css-minimizer-webpack-plugin": "^3.2.0",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
- "ecashaddrjs": "^1.3.0",
+ "ecashaddrjs": "^1.5.2",
"ecies-lite": "^1.0.7",
"eslint": "^8.3.0",
"eslint-config-react-app": "^7.0.0",
diff --git a/cashtab/src/components/Common/Notifications.js b/cashtab/src/components/Common/Notifications.js
--- a/cashtab/src/components/Common/Notifications.js
+++ b/cashtab/src/components/Common/Notifications.js
@@ -232,12 +232,17 @@
});
};
-const generalNotification = (data, msgStr) => {
+const generalNotification = (
+ data,
+ msgStr,
+ duration = currency.notificationDurationShort,
+) => {
const notificationStyle = getDeviceNotificationStyle();
notification.success({
message: msgStr,
description: data,
style: notificationStyle,
+ duration: duration,
});
};
diff --git a/cashtab/src/utils/__mocks__/chronikMock.js b/cashtab/src/utils/__mocks__/chronikMock.js
--- a/cashtab/src/utils/__mocks__/chronikMock.js
+++ b/cashtab/src/utils/__mocks__/chronikMock.js
@@ -14,10 +14,16 @@
self._url = `https://${url}`;
self._wsUrl = `wss://${url}`;
+ // API call mock return objects
+ // Can be set with self.setMock
+ self.mockedResponses = {
+ // empty for now, will include other API mocks as needed
+ };
+
// Flags to check if methods have been called
self.wsSubscribeCalled = false;
self.wsWaitForOpenCalled = false;
-
+ self.manuallyClosed = false;
// public chronik methods
// todo see latest failing unit test
self.ws = function (wsObj) {
@@ -45,6 +51,9 @@
}
};
+ self.wsClose = function () {
+ self.manuallyClosed = true;
+ };
// Allow user to set expected chronik call response
self.setMock = function (call, options) {
// e.g. ('block', {input: '', output: ''})
diff --git a/cashtab/src/utils/__tests__/chronik.test.js b/cashtab/src/utils/__tests__/chronik.test.js
--- a/cashtab/src/utils/__tests__/chronik.test.js
+++ b/cashtab/src/utils/__tests__/chronik.test.js
@@ -8,6 +8,7 @@
sortAndTrimChronikTxHistory,
parseChronikTx,
getMintAddress,
+ txConfListener,
} from 'utils/chronik';
import {
mockChronikUtxos,
@@ -68,6 +69,37 @@
} from '../__mocks__/chronikMintTxs';
import { ChronikClient } from 'chronik-client';
import { when } from 'jest-when';
+import { MockChronikClient } from 'utils/__mocks__/chronikMock';
+
+it(`txConfListener successfully subscribes to the given address' confirmation event`, async () => {
+ // Initialize mock chronik
+ const address = 'ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8';
+ const txid =
+ 'f7d71433af9a4e0081ea60349becf2a60efed8890df7c3e8e079b3427f51d5ea';
+ const mockedChronik = new MockChronikClient();
+
+ // Create websocket subscription
+ await txConfListener(mockedChronik, address, txid, '');
+
+ // Mock the 'Confirmed' ws event
+ const wsMsg = {
+ type: 'Confirmed',
+ txid: txid,
+ };
+ // Tell mockedChronik what response we expect
+ mockedChronik.setMock('ws', {
+ output: wsMsg,
+ });
+ mockedChronik.wsClose();
+
+ // Verify subscription functions were called
+ expect(mockedChronik.wsWaitForOpenCalled).toStrictEqual(true);
+ expect(mockedChronik.wsSubscribeCalled).toStrictEqual(true);
+ expect(mockedChronik.manuallyClosed).toStrictEqual(true);
+ // Verify ws confirmation event on the given txid
+ expect(mockedChronik.mockedResponses.ws.type).toStrictEqual('Confirmed');
+ expect(mockedChronik.mockedResponses.ws.txid).toStrictEqual(txid);
+});
it(`getTokenStats successfully returns a token stats object`, async () => {
// Initialize chronik
diff --git a/cashtab/src/utils/__tests__/transactions.test.js b/cashtab/src/utils/__tests__/transactions.test.js
--- a/cashtab/src/utils/__tests__/transactions.test.js
+++ b/cashtab/src/utils/__tests__/transactions.test.js
@@ -109,10 +109,8 @@
it('Broadcasts a v0 alias registration tx for an 8-byte alias to a p2pkh address', async () => {
const mockedChronik = new MockChronikClient();
-
const mockTxid =
'1272c4a9bf5829c9dba1efb252e753ed20e3cdd49b6e75a778befc7a87eaf7d0';
-
mockedChronik.broadcastTx = jest
.fn()
.mockResolvedValue({ txid: mockTxid });
@@ -134,13 +132,21 @@
551,
),
).toStrictEqual(expectedResult);
+
+ // Mock the 'Confirmed' ws event to exit the ws reconnections
+ const wsMsg = {
+ type: 'Confirmed',
+ txid: mockTxid,
+ };
+ // Tell mockedChronik what response we expect
+ mockedChronik.setMock('ws', {
+ output: wsMsg,
+ });
});
it('Broadcasts a v0 alias registration tx for a 21-byte alias to a p2pkh address', async () => {
const mockedChronik = new MockChronikClient();
-
const mockTxid =
'912582a1dc11b568f14f8ebae15cbb0ce53bdb973e137e7dc7c9b261327e6cab';
-
mockedChronik.broadcastTx = jest
.fn()
.mockResolvedValue({ txid: mockTxid });
@@ -161,13 +167,21 @@
551,
),
).toStrictEqual(expectedResult);
+
+ // Mock the 'Confirmed' ws event to exit the ws reconnections
+ const wsMsg = {
+ type: 'Confirmed',
+ txid: mockTxid,
+ };
+ // Tell mockedChronik what response we expect
+ mockedChronik.setMock('ws', {
+ output: wsMsg,
+ });
});
it('Broadcasts a v0 alias registration tx for a 16-byte alias to a p2pkh address', async () => {
const mockedChronik = new MockChronikClient();
-
const mockTxid =
'8783d7064ce22e8390c9fa94ef9a4d5bb0184e401ef5a9fbf60b68294e275c80';
-
mockedChronik.broadcastTx = jest
.fn()
.mockResolvedValue({ txid: mockTxid });
@@ -188,6 +202,16 @@
551,
),
).toStrictEqual(expectedResult);
+
+ // Mock the 'Confirmed' ws event to exit the ws reconnections
+ const wsMsg = {
+ type: 'Confirmed',
+ txid: mockTxid,
+ };
+ // Tell mockedChronik what response we expect
+ mockedChronik.setMock('ws', {
+ output: wsMsg,
+ });
});
it(`Throws error if called trying to send one base unit ${currency.ticker} more than available in utxo set`, async () => {
diff --git a/cashtab/src/utils/chronik.js b/cashtab/src/utils/chronik.js
--- a/cashtab/src/utils/chronik.js
+++ b/cashtab/src/utils/chronik.js
@@ -13,6 +13,54 @@
import wif from 'wif';
import { opReturn as opreturnConfig } from 'config/opreturn';
import { chronik as chronikConfig } from 'config/chronik';
+import { generalNotification } from 'components/Common/Notifications';
+import ecashaddr from 'ecashaddrjs';
+
+/**
+ * Subscribes to a given hash160 key and listens for confirmation on the txid
+ *
+ * @param {string} chronik the chronik-client instance
+ * @param {string} address the eCash address of the active wallet
+ * @param {string} txid the txid of the alias registration tx
+ * @param {string} message the message to be rendered upon detection of confirmation on the tx
+ * @throws {error} err chronik websocket subscription errors
+ */
+export const txConfListener = async (chronik, address, txid, message) => {
+ const { type, hash } = ecashaddr.decode(address, true);
+ try {
+ const ws = chronik.ws({
+ onMessage: msg => {
+ if (msg.type === 'Confirmed' && msg.txid === txid) {
+ console.log(
+ `txConfListener: ${txid} has received one confirmation`,
+ );
+ // Sets a perm-notification until user closes it
+ generalNotification(message, 'Confirmed', 0);
+ // Confirmation received, unsubscribe and close websocket
+ ws.unsubscribe(type, hash);
+ ws.close();
+ }
+ },
+ onReconnect: e => {
+ // Fired before a reconnect attempt is made:
+ console.log(
+ 'txConfListener: Reconnecting websocket, disconnection cause: ',
+ e,
+ );
+ },
+ });
+
+ // Wait for WS to be connected:
+ await ws.waitForOpen();
+
+ // Subscribe to scripts
+ ws.subscribe(type, hash);
+ } catch (err) {
+ console.log(
+ 'txConfListener: Error in chronik websocket subscription: ' + err,
+ );
+ }
+};
export const getTxHistoryPage = async (chronik, hash160, page = 0) => {
let txHistoryPage;
diff --git a/cashtab/src/utils/transactions.js b/cashtab/src/utils/transactions.js
--- a/cashtab/src/utils/transactions.js
+++ b/cashtab/src/utils/transactions.js
@@ -18,6 +18,7 @@
import ecies from 'ecies-lite';
import * as utxolib from '@bitgo/utxo-lib';
import { explorer } from 'config/explorer';
+import { txConfListener } from 'utils/chronik';
const SEND_XEC_ERRORS = {
INSUFFICIENT_FUNDS: 0,
@@ -409,6 +410,16 @@
}
const explorerLink = `${explorer.blockExplorerUrl}/tx/${broadcastResponse.txid}`;
+
+ // Open a dedicated websocket to listen for 1 conf on this alias registration tx
+ // Note: not an async call as this can listen in the background and does not need to complete first
+ txConfListener(
+ chronik,
+ wallet.Path1899.cashAddress,
+ broadcastResponse.txid,
+ `Alias registration for ${aliasName} has received 1 confirmation, please check the Alias page`,
+ );
+
// return the explorer link for the broadcasted tx
return { explorerLink, txid: broadcastResponse.txid, rawTxHex };
} catch (err) {

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 10:55 (12 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573339
Default Alt Text
D14325.diff (12 KB)

Event Timeline