From b4e349c170a83d4e0d153bfec62a97170e534fda Mon Sep 17 00:00:00 2001 From: buck Date: Tue, 5 Mar 2024 16:18:45 -0600 Subject: [PATCH] Mempool client fix (#34) * update mempool client endpoint * Add support for multiple public client options (mempool or blockstream) * fix: avoid unnecessary re-instantiation of client --- .changeset/clean-lamps-develop.md | 5 ++++ .changeset/tricky-stingrays-rest.md | 5 ++++ apps/coordinator/src/actions/clientActions.ts | 4 ++- .../src/components/ClientPicker/index.jsx | 28 +++++++++---------- .../Wallet/WalletDescriptorImporter.tsx | 1 - .../src/components/Wallet/WalletGenerator.jsx | 10 +++++-- .../src/components/Wallet/index.jsx | 12 +++++--- apps/coordinator/src/selectors/wallet.js | 2 +- packages/caravan-clients/src/client.test.ts | 26 +++++++++++------ packages/caravan-clients/src/client.ts | 8 ++---- 10 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 .changeset/clean-lamps-develop.md create mode 100644 .changeset/tricky-stingrays-rest.md diff --git a/.changeset/clean-lamps-develop.md b/.changeset/clean-lamps-develop.md new file mode 100644 index 00000000..e4415f02 --- /dev/null +++ b/.changeset/clean-lamps-develop.md @@ -0,0 +1,5 @@ +--- +"@caravan/coordinator": minor +--- + +Add support for different public client options (mempool or blockstream). diff --git a/.changeset/tricky-stingrays-rest.md b/.changeset/tricky-stingrays-rest.md new file mode 100644 index 00000000..6c8f41de --- /dev/null +++ b/.changeset/tricky-stingrays-rest.md @@ -0,0 +1,5 @@ +--- +"@caravan/clients": patch +--- + +Update mempool client host diff --git a/apps/coordinator/src/actions/clientActions.ts b/apps/coordinator/src/actions/clientActions.ts index 6c2271d0..4cd24136 100644 --- a/apps/coordinator/src/actions/clientActions.ts +++ b/apps/coordinator/src/actions/clientActions.ts @@ -23,6 +23,8 @@ export const getBlockchainClientFromStore = async () => { const { network } = getState().settings; const { client } = getState(); if (!client) return; + if (client.blockchainClient?.type === client.type) + return client.blockchainClient; let clientType: ClientType; switch (client.type) { @@ -42,7 +44,7 @@ export const getBlockchainClientFromStore = async () => { client, type: clientType, network, - throttled: true, + throttled: client.type === ClientType.BLOCKSTREAM, }); dispatch({ type: SET_BLOCKCHAIN_CLIENT, value: blockchainClient }); return blockchainClient; diff --git a/apps/coordinator/src/components/ClientPicker/index.jsx b/apps/coordinator/src/components/ClientPicker/index.jsx index dc0b7da1..3e251937 100644 --- a/apps/coordinator/src/components/ClientPicker/index.jsx +++ b/apps/coordinator/src/components/ClientPicker/index.jsx @@ -10,8 +10,8 @@ import { FormControl, Radio, RadioGroup, - FormHelperText, } from "@mui/material"; +import { ClientType } from "@caravan/clients"; // Components @@ -127,13 +127,22 @@ const ClientPicker = ({ } name="clientType" - value="public" - label={Public} + value={ClientType.MEMPOOL} + label={Mempool.space} onChange={handleTypeChange} - checked={client.type === "public"} + checked={client.type === ClientType.MEMPOOL} + /> + } + name="clientType" + value={ClientType.BLOCKSTREAM} + label={Blockstream.info} + onChange={handleTypeChange} + checked={client.type === ClientType.BLOCKSTREAM} /> - {client.type === "public" && ( - - {"'Public' uses the "} - {blockchainClient?.type} - {" API. Switch to private to use a "} - bitcoind - {" node."} - - )} {client.type === "private" && ( handleUrlChange(event)} diff --git a/apps/coordinator/src/components/Wallet/WalletDescriptorImporter.tsx b/apps/coordinator/src/components/Wallet/WalletDescriptorImporter.tsx index 47ed01d6..86288be5 100644 --- a/apps/coordinator/src/components/Wallet/WalletDescriptorImporter.tsx +++ b/apps/coordinator/src/components/Wallet/WalletDescriptorImporter.tsx @@ -80,7 +80,6 @@ export const WalletDescriptorImporter = () => { try { const config = await getWalletFromDescriptor(descriptor, network); const checksum = await getChecksum(descriptor); - console.log(config); dispatch(updateWalletUuidAction(checksum)); setWalletConfig(config); } catch (e) { diff --git a/apps/coordinator/src/components/Wallet/WalletGenerator.jsx b/apps/coordinator/src/components/Wallet/WalletGenerator.jsx index e90d9a75..2474e5c5 100644 --- a/apps/coordinator/src/components/Wallet/WalletGenerator.jsx +++ b/apps/coordinator/src/components/Wallet/WalletGenerator.jsx @@ -20,7 +20,7 @@ import { Box, } from "@mui/material"; import AccountCircleIcon from "@mui/icons-material/AccountCircle"; - +import { ClientType } from "@caravan/clients"; import ClientPicker from "../ClientPicker"; import ConfirmWallet from "./ConfirmWallet"; import RegisterWallet from "./RegisterWallet"; @@ -460,7 +460,13 @@ class WalletGenerator extends React.Component { variant="contained" color="primary" onClick={this.generate} - disabled={client.type !== "public" && !connectSuccess} + disabled={ + ![ + "public", + ClientType.MEMPOOL, + ClientType.BLOCKSTREAM, + ].includes(client.type) && !connectSuccess + } > Confirm diff --git a/apps/coordinator/src/components/Wallet/index.jsx b/apps/coordinator/src/components/Wallet/index.jsx index d2c6dd97..5800a283 100644 --- a/apps/coordinator/src/components/Wallet/index.jsx +++ b/apps/coordinator/src/components/Wallet/index.jsx @@ -8,6 +8,7 @@ import { validateExtendedPublicKey, } from "@caravan/bitcoin"; import { Box, Button, FormHelperText, Grid } from "@mui/material"; +import { ClientType } from "@caravan/clients"; import { downloadFile } from "../../utils"; import { resetWallet as resetWalletAction, @@ -97,10 +98,13 @@ class CreateWallet extends React.Component { } if (config.client) { - const clientProperties = - config.client.type === "public" - ? ["type"] - : ["type", "url", "username"]; + const clientProperties = [ + "public", + ClientType.MEMPOOL, + ClientType.BLOCKSTREAM, + ].includes(config.client.type) + ? ["type"] + : ["type", "url", "username"]; const validClient = CreateWallet.validateProperties( config, clientProperties, diff --git a/apps/coordinator/src/selectors/wallet.js b/apps/coordinator/src/selectors/wallet.js index 0e50a13e..54c1bd35 100644 --- a/apps/coordinator/src/selectors/wallet.js +++ b/apps/coordinator/src/selectors/wallet.js @@ -28,7 +28,7 @@ const getClientDetails = (state) => { }`; } return `{ - "type": "public" + "type": "${state.client.type}" }`; }; diff --git a/packages/caravan-clients/src/client.test.ts b/packages/caravan-clients/src/client.test.ts index c620a8b5..aa5a32ba 100644 --- a/packages/caravan-clients/src/client.test.ts +++ b/packages/caravan-clients/src/client.test.ts @@ -106,7 +106,7 @@ describe("BlockchainClient", () => { type: ClientType.MEMPOOL, network: Network.MAINNET, }); - expect(mempool.host).toEqual("https://mempool.space/api/v1"); + expect(mempool.host).toEqual("https://unchained.mempool.space/api"); }); it("should set the testnet host for a public client", () => { @@ -119,7 +119,7 @@ describe("BlockchainClient", () => { type: ClientType.MEMPOOL, network: Network.TESTNET, }); - expect(mempool.host).toEqual("https://mempool.space/testnet/api/v1"); + expect(mempool.host).toEqual("https://unchained.mempool.space/testnet/api"); }); it("should set the signet host for a public client", () => { @@ -127,7 +127,7 @@ describe("BlockchainClient", () => { type: ClientType.MEMPOOL, network: Network.SIGNET, }); - expect(mempool.host).toEqual("https://mempool.space/signet/api/v1"); + expect(mempool.host).toEqual("https://unchained.mempool.space/signet/api"); expect(() => { new BlockchainClient({ type: ClientType.BLOCKSTREAM, @@ -482,8 +482,18 @@ describe("BlockchainClient", () => { // Mock the response from the Get method const mockUtxos: UTXO[] = [ - { txid: "txid1", vout: 0, value: 100, status: {confirmed: true, block_time: 21} }, - { txid: "txid2", vout: 1, value: 200, status: {confirmed: true, block_time: 42} }, + { + txid: "txid1", + vout: 0, + value: 100, + status: { confirmed: true, block_time: 21 }, + }, + { + txid: "txid2", + vout: 1, + value: 200, + status: { confirmed: true, block_time: 42 }, + }, ]; const mockGet = jest.fn().mockResolvedValue(mockUtxos); @@ -504,8 +514,8 @@ describe("BlockchainClient", () => { // Verify the returned result expect(result.utxos).toEqual( await Promise.all( - mockUtxos.map((utxo: UTXO) => blockchainClient.formatUtxo(utxo)) - ) + mockUtxos.map((utxo: UTXO) => blockchainClient.formatUtxo(utxo)), + ), ); expect(result.balanceSats).toEqual(new BigNumber(300)); expect(result.addressKnown).toBe(true); @@ -770,7 +780,7 @@ describe("BlockchainClient", () => { ); // Verify the mock axios instance was called with the correct URL - expect(mockGet).toHaveBeenCalledWith("/fees/recommended"); + expect(mockGet).toHaveBeenCalledWith("/v1/fees/recommended"); // Verify the returned fee estimate expect(feeEstimate).toEqual(mockResponse[block]); } diff --git a/packages/caravan-clients/src/client.ts b/packages/caravan-clients/src/client.ts index ada5db83..436a2a7e 100644 --- a/packages/caravan-clients/src/client.ts +++ b/packages/caravan-clients/src/client.ts @@ -122,15 +122,13 @@ export class BlockchainClient extends ClientBase { if (type === ClientType.BLOCKSTREAM) { host = "https://blockstream.info"; } else if (type === ClientType.MEMPOOL) { - host = "https://mempool.space"; + host = "https://unchained.mempool.space"; } if (type !== ClientType.PRIVATE && network !== Network.MAINNET) { host += `/${network}`; } - if (type === ClientType.BLOCKSTREAM) { + if (type !== ClientType.PRIVATE) { host += "/api"; - } else if (type === ClientType.MEMPOOL) { - host += "/api/v1"; } super(throttled, host); this.network = network; @@ -284,7 +282,7 @@ export class BlockchainClient extends ClientBase { fees = await this.Get(`/fee-estimates`); return fees[blocks]; case ClientType.MEMPOOL: - fees = await this.Get("/fees/recommended"); + fees = await this.Get("/v1/fees/recommended"); if (blocks === 1) { return fees.fastestFee; } else if (blocks <= 3) {