-
Notifications
You must be signed in to change notification settings - Fork 35
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
base: master
Are you sure you want to change the base?
Changes from 8 commits
5e00057
734fe8f
4687cf0
a607539
cfb691d
b7c526b
5f6e35f
5803780
26aa132
6b4faf8
845555c
4711083
18ecfac
11bcdbd
a266dbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,9 +98,34 @@ export class ExplorerNetwork extends Network { | |
* @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 | ||
|
@@ -111,39 +136,56 @@ export class ExplorerNetwork extends Network { | |
// 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -272,19 +314,38 @@ export class ExplorerNetwork extends Network { | |
|
||
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; | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -701,7 +701,29 @@ export class Wallet { | |||||||
// This is just to be sure since blockbook (as we know) | ||||||||
// usually does not return txs of the actual last block. | ||||||||
this.#lastProcessedBlock = blockCount - 5; | ||||||||
await this.#transparentSync(); | ||||||||
// Transparent sync is inherently less stable than Shield since it requires heavy | ||||||||
// explorer indexing, so we'll attempt once asynchronously, and if it fails, set a | ||||||||
// recurring "background" sync interval until it's finally successful. | ||||||||
try { | ||||||||
await this.#transparentSync(); | ||||||||
} catch { | ||||||||
// We'll set a 5s interval sync until it's finally successful, then nuke the 'thread'. | ||||||||
const cThread = new AsyncInterval(async () => { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You cannot use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue with blocking it is Shield then becomes unusable unless Transparent syncs (right now, Shield can be used even if Transparent is unable). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally we would split the state so that we have |
||||||||
try { | ||||||||
await this.#transparentSync(true); | ||||||||
cThread.clearInterval(); | ||||||||
} catch { | ||||||||
// Emit a transparent sync warning | ||||||||
getEventEmitter().emit( | ||||||||
'transparent-sync-status-update', | ||||||||
0, | ||||||||
0, | ||||||||
false, | ||||||||
'Explorers are unreachable, your wallet may not be fully synced!' | ||||||||
); | ||||||||
} | ||||||||
}, 5000); | ||||||||
} | ||||||||
if (this.hasShield()) { | ||||||||
await this.#syncShield(); | ||||||||
} | ||||||||
|
@@ -712,8 +734,12 @@ export class Wallet { | |||||||
getEventEmitter().emit('new-tx'); | ||||||||
}); | ||||||||
|
||||||||
async #transparentSync() { | ||||||||
if (!this.isLoaded() || this.#isSynced) return; | ||||||||
/** | ||||||||
* Synchronise UTXOs via xpub/address from the current explorer. | ||||||||
* @param {boolean} [force=false] - Force transparent sync, regardless of state | ||||||||
*/ | ||||||||
async #transparentSync(force = false) { | ||||||||
if ((!this.isLoaded() || this.#isSynced) && !force) return; | ||||||||
Comment on lines
+740
to
+741
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move the loadedCheck and synced in the sync function, so we can remove the force param.
Suggested change
It's only going to be called by sync anyways |
||||||||
const cNet = getNetwork(); | ||||||||
const addr = this.getKeyToExport(); | ||||||||
let nStartHeight = Math.max( | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you call this error/warning?