Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability for MPW to run explorer-less #427

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
12 changes: 6 additions & 6 deletions scripts/dashboard/WalletBalance.vue
Original file line number Diff line number Diff line change
Expand Up @@ -508,16 +508,16 @@ function restoreWallet() {
</div>
<div style="width: 100%">
{{
transparentSyncing
? syncTStr
: `Syncing ${shieldBlockRemainingSyncing} Blocks...`
shieldSyncing
? `Syncing ${shieldBlockRemainingSyncing} Blocks...`
: syncTStr
}}
<LoadingBar
:show="true"
:percentage="
transparentSyncing
? transparentProgressSyncing
: shieldPercentageSyncing
shieldSyncing
? shieldPercentageSyncing
: transparentProgressSyncing
"
style="
border: 1px solid #932ecd;
Expand Down
121 changes: 91 additions & 30 deletions scripts/network.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cChainParams } from './chain_params.js';
import { createAlert } from './misc.js';

Check warning on line 2 in scripts/network.js

View workflow job for this annotation

GitHub Actions / Run linters

'createAlert' is defined but never used
import {
debugError,
debugLog,
Expand All @@ -11,7 +11,7 @@
import { getEventEmitter } from './event_bus.js';
import { setExplorer, fAutoSwitch, setNode } from './settings.js';
import { cNode } from './settings.js';
import { ALERTS, tr, translation } from './i18n.js';

Check warning on line 14 in scripts/network.js

View workflow job for this annotation

GitHub Actions / Run linters

'ALERTS' is defined but never used
import { Transaction } from './transaction.js';

/**
Expand Down Expand Up @@ -105,9 +105,34 @@
* @returns {Promise<Object>} the block fetched from explorer
*/
async getBlock(blockHeight, skipCoinstake = false) {
JSKitty marked this conversation as resolved.
Show resolved Hide resolved
const block = await this.safeFetchFromExplorer(
`/api/v2/block/${blockHeight}`
);
let block = null;
let fUseNodes = false;
try {
// Attempt via Explorer first
block = await this.safeFetchFromExplorer(
`/api/v2/block/${blockHeight}`
);
} catch {
// Use Nodes as fallback
fUseNodes = true;
// First we fetch the blockhash
let strHash = await this.callRPC(
`/getblockhash?params=${blockHeight}`,
true
);
// Strip quotes from the RPC response
strHash = strHash.replace(/"/g, '');
// Craft a filter to retrieve only raw Tx hex and txid
const strFilter =
'&filter=' +
encodeURI(`. | .tx = [.tx[] | { hex: .hex, txid: .txid}]`);
// Fetch the full block (verbose)
block = await this.callRPC(
`/getblock?params=${strHash},2${strFilter}`
);
// Shift the array to `txs` to match Blockbook format
block.txs = block.tx;
}
const newTxs = [];
// This is bad. We're making so many requests
// This is a quick fix to try to be compliant with the blockbook
Expand All @@ -118,39 +143,56 @@
// In addition, always skip the coinbase transaction and in case the coinstake one
// TODO: once v6.0 and shield stake is activated we might need to change this optimization
for (const tx of block.txs.slice(skipCoinstake ? 2 : 1)) {
const r = await fetch(
`${this.strUrl}/api/v2/tx-specific/${tx.txid}`
);
if (!r.ok) throw new Error('failed');
const newTx = await r.json();
newTxs.push(newTx);
// Pull raw Tx from explorer - unless we used Nodes, then the full tx is already loaded
if (!fUseNodes) {
const r = await fetch(
`${this.strUrl}/api/v2/tx-specific/${tx.txid}`
);
if (!r.ok) throw new Error('failed');
const newTx = await r.json();
newTxs.push(newTx);
} else {
newTxs.push(tx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is very good, we should consider removing the explorer based getBlock

}
}
block.txs = newTxs;
return block;
}

/**
* Fetch the block height of the current explorer
* Fetch the block height of the current explorer or fallback node
* @returns {Promise<number>} - Block height
*/
async getBlockCount() {
const { backend } = await (
await retryWrapper(fetchBlockbook, true, `/api/v2/api`)
).json();
try {
// Attempt via Explorer first
const { backend } = await (
await retryWrapper(fetchBlockbook, true, `/api/v2/api`)
).json();

return backend.blocks;
return backend.blocks;
} catch {
// Use Nodes as a fallback
return parseInt(await this.callRPC('/getblockcount', true));
}
}

/**
* Fetch the latest block hash of the current explorer
* Fetch the latest block hash of the current explorer or fallback node
* @returns {Promise<string>} - Block hash
*/
async getBestBlockHash() {
const { backend } = await (
await retryWrapper(fetchBlockbook, true, `/api/v2/api`)
).json();
try {
// Attempt via Explorer first
const { backend } = await (
await retryWrapper(fetchBlockbook, true, `/api/v2/api`)
).json();

return backend.bestBlockHash;
return backend.bestBlockHash;
} catch {
// Use Nodes as a fallback
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like using nodes as a fallback for light RPC calls like getBestBlockHash , sendTransaction and getBlockCount: the code is more ugly and imo we should just make sure to have working explorers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo we would need at least 2x (4) explorers to consider it a stable network, or we start relying more on Nodes instead, it is the only compromise to get MPW more stable.

return await this.callRPC('/getbestblockhash', true);
}
}

/**
Expand Down Expand Up @@ -297,19 +339,38 @@

async sendTransaction(hex) {
try {
const data = await (
await retryWrapper(fetchBlockbook, true, '/api/v2/sendtx/', {
method: 'post',
body: hex,
})
).json();
// Attempt via Explorer first
let strTXID = '';
try {
const cData = await (
await retryWrapper(
fetchBlockbook,
true,
'/api/v2/sendtx/',
{
method: 'post',
body: hex,
}
)
).json();
// If there's no TXID, we throw any potential Blockbook errors
if (!cData.result || cData.result.length !== 64) throw cData;
strTXID = cData.result;
} catch {
// Use Nodes as a fallback
strTXID = await this.callRPC(
'/sendrawtransaction?params=' + hex,
true
);
strTXID = strTXID.replace(/"/g, '');
}

// Throw and catch if the data is not a TXID
if (!data.result || data.result.length !== 64) throw data;
// Throw and catch if there's no TXID
if (!strTXID || strTXID.length !== 64) throw strTXID;

debugLog(DebugTopics.NET, 'Transaction sent! ' + data.result);
getEventEmitter().emit('transaction-sent', true, data.result);
return data.result;
debugLog(DebugTopics.NET, 'Transaction sent! ' + strTXID);
getEventEmitter().emit('transaction-sent', true, strTXID);
return strTXID;
} catch (e) {
getEventEmitter().emit('transaction-sent', false, e);
return false;
Expand Down
17 changes: 15 additions & 2 deletions scripts/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { confirmPopup, createAlert, isShieldAddress } from './misc.js';
import { cChainParams } from './chain_params.js';
import { COIN } from './chain_params.js';
import { ALERTS, tr, translation } from './i18n.js';

Check warning on line 12 in scripts/wallet.js

View workflow job for this annotation

GitHub Actions / Run linters

'tr' is defined but never used
import { encrypt } from './aes-gcm.js';
import { Database } from './database.js';
import { RECEIVE_TYPES } from './contacts-book.js';
Expand Down Expand Up @@ -710,8 +710,21 @@
async #transparentSync() {
if (!this.isLoaded() || this.#isSynced) return;
const cNet = getNetwork();
await cNet.getLatestTxs(this);
getEventEmitter().emit('transparent-sync-status-update', '', '', true);
let fSynced = false;
try {
await cNet.getLatestTxs(this);
fSynced = true;
} catch {
// If all Explorers are down, we'll just rely on the local TXDB and display a warning
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more serious: getLatestTxs failed, wallet is almost unusable, balance is wrong and any attempt to create a tx will likely fail.

Copy link
Member Author

@JSKitty JSKitty Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shield is fully usable and transactable, as well as Masternode controller and Governance. Meanwhile current MPW will not even open/load if explorers are down (which they are down far too frequently).

The idea of the warning anyway is that as soon as the getLatestTxs API is available again - we want to re-run getLatestTxs and then we can drop the warning (just not sure where to handle this state yet).

}
getEventEmitter().emit(
'transparent-sync-status-update',
fSynced
? ''
: 'Explorers are unreachable, your wallet may not be fully synced!',
fSynced ? 100 : 0,
fSynced
);
}

/**
Expand Down
Loading