From d85eea53658d6fed8b5fdba86b744e2a33db9344 Mon Sep 17 00:00:00 2001 From: Duddino Date: Mon, 4 Nov 2024 12:59:15 +0100 Subject: [PATCH 1/2] Add Eslint eqeqeq rule (#453) * Add eqeqeq eslint rule * Fix eqeqeq eslint rule * Replace `not ===` with `!==` * Run prettier --- .eslintrc.json | 3 +- scripts/contacts-book.js | 2 +- scripts/database.js | 2 +- scripts/encoding.js | 10 ++-- scripts/flipdown.js | 4 +- scripts/global.js | 10 ++-- scripts/masternode.js | 2 +- scripts/mempool.js | 2 +- scripts/network/__mocks__/network_manager.js | 2 +- scripts/network/network_manager.js | 4 +- scripts/script.js | 52 ++++++++++---------- scripts/transaction.js | 8 +-- scripts/transaction_builder.js | 4 +- scripts/utils.js | 2 +- scripts/wallet.js | 6 +-- 15 files changed, 57 insertions(+), 56 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index a72a877a6..deadf5c85 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,6 +14,7 @@ "no-empty": ["error", { "allowEmptyCatch": true }], "no-constant-condition": "off", "@typescript-eslint/no-empty-function": "off", - "no-console": "error" + "no-console": "error", + "eqeqeq": ["error", "smart"] } } diff --git a/scripts/contacts-book.js b/scripts/contacts-book.js index 2548a893d..0fcbf7dba 100644 --- a/scripts/contacts-book.js +++ b/scripts/contacts-book.js @@ -131,7 +131,7 @@ export async function renderContacts(account, fPrompt = false) { // Render an editable Contacts Table strHTML += `
`; diff --git a/scripts/database.js b/scripts/database.js index bb3c0af5d..393583131 100644 --- a/scripts/database.js +++ b/scripts/database.js @@ -433,7 +433,7 @@ export class Database { ' to ' + Database.version ); - if (oldVersion == 0) { + if (oldVersion === 0) { db.createObjectStore('masternodes'); db.createObjectStore('accounts'); db.createObjectStore('settings'); diff --git a/scripts/encoding.js b/scripts/encoding.js index 44afdaee0..ce5266074 100644 --- a/scripts/encoding.js +++ b/scripts/encoding.js @@ -19,7 +19,7 @@ import { bech32 } from 'bech32'; * @returns {Array} The compressed public key bytes */ export function compressPublicKey(pubKeyBytes) { - if (pubKeyBytes.length != 65) + if (pubKeyBytes.length !== 65) throw new Error('Attempting to compress an invalid uncompressed key'); const x = pubKeyBytes.slice(1, 33); const y = pubKeyBytes.slice(33); @@ -94,7 +94,7 @@ export function deriveAddress({ pkBytes, publicKey, output = 'ENCODED' }) { pubKeyBytes = compressPublicKey(pubKeyBytes); } - if (pubKeyBytes.length != 33) { + if (pubKeyBytes.length !== 33) { throw new Error('Invalid public key'); } @@ -244,9 +244,9 @@ export function parseWIF(strWIF, skipVerification = false) { * @returns {number[]} - Encoded numbers */ export function numToBytes(num, bytes = 8) { - if (bytes == 0) { + if (bytes === 0) { return []; - } else if (num == -1n) { + } else if (num === -1n) { return hexToBytes('ffffffffffffffff'); } else { return [Number(num % 256n)].concat(numToBytes(num / 256n, bytes - 1)); @@ -270,7 +270,7 @@ export function numToByteArray(num) { * @returns {BigInt} converted number from bytes */ export function bytesToNum(bytes) { - if (bytes.length == 0) return 0n; + if (bytes.length === 0) return 0n; else return BigInt(bytes[0]) + 256n * bytesToNum(bytes.slice(1)); } diff --git a/scripts/flipdown.js b/scripts/flipdown.js index 05f39dd3a..bb23e1f62 100644 --- a/scripts/flipdown.js +++ b/scripts/flipdown.js @@ -361,7 +361,7 @@ export class FlipDown { function rotorTopFlip() { this.rotorTop.forEach((el, i) => { - if (el.textContent != this.clockValuesAsString[i]) { + if (el.textContent !== this.clockValuesAsString[i]) { el.textContent = this.clockValuesAsString[i]; } }); @@ -369,7 +369,7 @@ export class FlipDown { function rotorLeafRearFlip() { this.rotorLeafRear.forEach((el, i) => { - if (el.textContent != this.clockValuesAsString[i]) { + if (el.textContent !== this.clockValuesAsString[i]) { el.textContent = this.clockValuesAsString[i]; el.parentElement.classList.add('flipped'); var flip = setInterval( diff --git a/scripts/global.js b/scripts/global.js index 6c129c34d..b5f7fb605 100644 --- a/scripts/global.js +++ b/scripts/global.js @@ -478,7 +478,7 @@ export async function govVote(hash, voteCode) { (await confirmPopup({ title: ALERTS.CONFIRM_POPUP_VOTE, html: ALERTS.CONFIRM_POPUP_VOTE_HTML, - })) == true + })) === true ) { const database = await Database.getInstance(); const cMasternode = await database.getMasternode(); @@ -769,7 +769,7 @@ export async function updateGovernanceTab() { fRenderingGovernance = true; // Setup the Superblock countdown (if not already done), no need to block thread with await, either. - if (!isTestnetLastState == cChainParams.current.isTestnet) { + if (isTestnetLastState !== cChainParams.current.isTestnet) { // Reset flipdown governanceFlipdown = null; doms.domFlipdown.innerHTML = ''; @@ -1004,7 +1004,7 @@ async function renderProposals(arrProposals, fContested) { } // Add border radius to last row - if (arrProposals.length - 1 == i) { + if (arrProposals.length - 1 === i) { domStatus.classList.add('bblr-7p'); } @@ -1254,7 +1254,7 @@ async function renderProposals(arrProposals, fContested) { domYesBtn.onclick = () => govVote(cProposal.Hash, 1); // Add border radius to last row - if (arrProposals.length - 1 == i) { + if (arrProposals.length - 1 === i) { domVoteBtns.classList.add('bbrr-7p'); } @@ -1753,7 +1753,7 @@ export function switchSettings(page) { Object.values(SETTINGS).forEach(({ section, btn }) => { // Set the slider to the proper location - if (page == 'display') { + if (page === 'display') { doms.domDisplayDecimalsSlider.oninput = function () { doms.domDisplayDecimalsSliderDisplay.innerHTML = this.value; //let val = ((((doms.domDisplayDecimalsSlider.offsetWidth - 24) / 9) ) * parseInt(this.value)); diff --git a/scripts/masternode.js b/scripts/masternode.js index 26df6ab39..eefd6e942 100644 --- a/scripts/masternode.js +++ b/scripts/masternode.js @@ -522,7 +522,7 @@ export default class Masternode { true ); - if (/^"[a-f0-9]"$/ && res.length == 64 + 2) { + if (/^"[a-f0-9]"$/ && res.length === 64 + 2) { return { ok: true, hash: res }; } else if ( res.includes('is unconfirmed') || diff --git a/scripts/mempool.js b/scripts/mempool.js index a2ca7deff..d344f6f90 100644 --- a/scripts/mempool.js +++ b/scripts/mempool.js @@ -287,7 +287,7 @@ class CachableBalance { value = -1; isValid() { - return this.value != -1; + return this.value !== -1; } invalidate() { this.value = -1; diff --git a/scripts/network/__mocks__/network_manager.js b/scripts/network/__mocks__/network_manager.js index b86095a45..5e99a2782 100644 --- a/scripts/network/__mocks__/network_manager.js +++ b/scripts/network/__mocks__/network_manager.js @@ -122,7 +122,7 @@ class TestBlock { */ addTransaction(txHex, blockHeight) { // Sanity check - if (blockHeight != this.blockHeight) { + if (blockHeight !== this.blockHeight) { throw new Error('Transaction and block have a different height!'); } this.txs.push(new TestTransaction(txHex, blockHeight)); diff --git a/scripts/network/network_manager.js b/scripts/network/network_manager.js index 359d4677f..6a2a37f6b 100644 --- a/scripts/network/network_manager.js +++ b/scripts/network/network_manager.js @@ -70,7 +70,7 @@ class NetworkManager { let attemptNet = isRPC ? this.#currentNode : this.#currentExplorer; let i = this.#networks.findIndex((net) => attemptNet === net); - if (i == -1) { + if (i === -1) { debugWarn(DebugTopics.NET, 'Cannot find index in networks array'); i = 0; } @@ -90,7 +90,7 @@ class NetworkManager { attemptNet.strUrl + ' failed on ' + funcName ); // If allowed, switch instances - if (!fAutoSwitch || attempts == nMaxTries) { + if (!fAutoSwitch || attempts === nMaxTries) { throw error; } attemptNet = this.#networks[(i + attempts) % nMaxTries]; diff --git a/scripts/script.js b/scripts/script.js index d7a3cabed..9fbb04ff3 100644 --- a/scripts/script.js +++ b/scripts/script.js @@ -181,11 +181,11 @@ export function getScriptForBurn(data) { export function isP2PKH(dataBytes) { return ( dataBytes.length >= 25 && - dataBytes[0] == OP['DUP'] && - dataBytes[1] == OP['HASH160'] && - dataBytes[2] == 0x14 && - dataBytes[23] == OP['EQUALVERIFY'] && - dataBytes[24] == OP['CHECKSIG'] + dataBytes[0] === OP['DUP'] && + dataBytes[1] === OP['HASH160'] && + dataBytes[2] === 0x14 && + dataBytes[23] === OP['EQUALVERIFY'] && + dataBytes[24] === OP['CHECKSIG'] ); } @@ -197,18 +197,18 @@ export function isP2PKH(dataBytes) { export function isP2CS(dataBytes) { return ( dataBytes.length >= 51 && - dataBytes[0] == OP['DUP'] && - dataBytes[1] == OP['HASH160'] && - dataBytes[2] == OP['ROT'] && - dataBytes[3] == OP['IF'] && - (dataBytes[4] == OP['CHECKCOLDSTAKEVERIFY'] || - dataBytes[4] == OP['CHECKCOLDSTAKEVERIFY_LOF']) && - dataBytes[5] == 0x14 && - dataBytes[26] == OP['ELSE'] && - dataBytes[27] == 0x14 && - dataBytes[48] == OP['ENDIF'] && - dataBytes[49] == OP['EQUALVERIFY'] && - dataBytes[50] == OP['CHECKSIG'] + dataBytes[0] === OP['DUP'] && + dataBytes[1] === OP['HASH160'] && + dataBytes[2] === OP['ROT'] && + dataBytes[3] === OP['IF'] && + (dataBytes[4] === OP['CHECKCOLDSTAKEVERIFY'] || + dataBytes[4] === OP['CHECKCOLDSTAKEVERIFY_LOF']) && + dataBytes[5] === 0x14 && + dataBytes[26] === OP['ELSE'] && + dataBytes[27] === 0x14 && + dataBytes[48] === OP['ENDIF'] && + dataBytes[49] === OP['EQUALVERIFY'] && + dataBytes[50] === OP['CHECKSIG'] ); } @@ -219,12 +219,12 @@ export function isP2CS(dataBytes) { export function isP2EXC(dataBytes) { return ( dataBytes.length >= 26 && - dataBytes[0] == OP['EXCHANGEADDR'] && - dataBytes[1] == OP['DUP'] && - dataBytes[2] == OP['HASH160'] && - dataBytes[3] == 0x14 && - dataBytes[24] == OP['EQUALVERIFY'] && - dataBytes[25] == OP['CHECKSIG'] + dataBytes[0] === OP['EXCHANGEADDR'] && + dataBytes[1] === OP['DUP'] && + dataBytes[2] === OP['HASH160'] && + dataBytes[3] === 0x14 && + dataBytes[24] === OP['EQUALVERIFY'] && + dataBytes[25] === OP['CHECKSIG'] ); } @@ -234,9 +234,9 @@ export function isP2EXC(dataBytes) { */ export function isProposalFee(dataBytes) { return ( - dataBytes.length == 34 && - dataBytes[0] == OP['RETURN'] && - dataBytes[1] == 32 + dataBytes.length === 34 && + dataBytes[0] === OP['RETURN'] && + dataBytes[1] === 32 ); } /** diff --git a/scripts/transaction.js b/scripts/transaction.js index 93f2ded66..771f15dab 100644 --- a/scripts/transaction.js +++ b/scripts/transaction.js @@ -56,7 +56,7 @@ export class CTxOut { } isEmpty() { - return this.value == 0 && (this.script === 'f8' || this.script === ''); + return this.value === 0 && (this.script === 'f8' || this.script === ''); } serialize() { @@ -156,7 +156,7 @@ export class Transaction { } isConfirmed() { - return this.blockHeight != -1; + return this.blockHeight !== -1; } isCoinStake() { @@ -166,7 +166,7 @@ export class Transaction { isCoinBase() { // txid is full of 0s for coinbase inputs return ( - this.vin.length == 1 && !!this.vin[0].outpoint.txid.match(/^0*$/) + this.vin.length === 1 && !!this.vin[0].outpoint.txid.match(/^0*$/) ); } @@ -429,7 +429,7 @@ export class Transaction { const copy = structuredClone(this.__original); // Black out all inputs for (let i = 0; i < copy.vin.length; i++) { - if (i != index) copy.vin[i].scriptSig = ''; + if (i !== index) copy.vin[i].scriptSig = ''; } return bytesToHex( dSHA256([ diff --git a/scripts/transaction_builder.js b/scripts/transaction_builder.js index 4c726e82c..79209c05e 100644 --- a/scripts/transaction_builder.js +++ b/scripts/transaction_builder.js @@ -124,7 +124,7 @@ export class TransactionBuilder { const front = bytes.slice(0, bytes.length - 4); const back = bytes.slice(bytes.length - 4); const checksum = dSHA256(front).slice(0, 4); - if (checksum + '' == back + '') { + if (checksum + '' === back + '') { return Array.from(front.slice(isExchangeAddress(address) ? 3 : 1)); } throw new Error('Invalid address'); @@ -271,7 +271,7 @@ export class TransactionBuilder { const tx = this.#transaction; let first = true; const outputs = tx.vout.length; - if (!outputs || outputs == 0) { + if (!outputs || outputs === 0) { throw new Error('tx has no outputs!'); } for (let vout of tx.vout) { diff --git a/scripts/utils.js b/scripts/utils.js index fc0fbad9d..dcaba054e 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -94,7 +94,7 @@ export async function startBatch( batchSize, retryTime = 10000 ) { - if (length == 0) { + if (length === 0) { return; } return new Promise((res) => { diff --git a/scripts/wallet.js b/scripts/wallet.js index 68413f801..fc2509761 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -531,7 +531,7 @@ export class Wallet { } else if (isP2CS(dataBytes)) { const addresses = []; for (let i = 0; i < 2; i++) { - const iStart = i == 0 ? OWNER_START_INDEX : COLD_START_INDEX; + const iStart = i === 0 ? OWNER_START_INDEX : COLD_START_INDEX; addresses.push( this.getAddressFromHashCache( bytesToHex(dataBytes.slice(iStart, iStart + 20)), @@ -774,7 +774,7 @@ export class Wallet { handled++; await this.#shield.handleBlock(blocks[j]); // Backup every 500 handled blocks - if (handled % 500 == 0) await this.saveShieldOnDisk(); + if (handled % 500 === 0) await this.saveShieldOnDisk(); // Delete so we don't have to hold all blocks in memory // until we finish syncing delete blocks[j]; @@ -940,7 +940,7 @@ export class Wallet { const cDB = await Database.getInstance(); const cAccount = await cDB.getAccount(); // If the account has not been created yet or there is no shield data return - if (!cAccount || cAccount.shieldData == '') { + if (!cAccount || cAccount.shieldData === '') { return; } this.#shield = await PIVXShield.load(cAccount.shieldData); From 47501965bd2de865edf657dcbca69c55dcc66d6b Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Mon, 4 Nov 2024 18:41:19 +0100 Subject: [PATCH 2/2] Faster wallet synchronization (#443) * Download all blocks up to the actual chain tip * Finish initial sync without waiting for the next block * update broken tests * Remove outdated comment * invalidate the balance cache after getLatestBlock call --- scripts/wallet.js | 9 +++++---- tests/integration/wallet/sync.spec.js | 11 ++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/scripts/wallet.js b/scripts/wallet.js index fc2509761..755f6beaa 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -710,6 +710,9 @@ export class Wallet { await this.#syncShield(); } this.#isSynced = true; + // At this point download the last missing blocks in the range (blockCount -5, blockCount] + await this.getLatestBlocks(blockCount); + // Update both activities post sync getEventEmitter().enableEvent('balance-update'); getEventEmitter().emit('balance-update'); @@ -836,9 +839,9 @@ export class Wallet { subscribeToNetworkEvents() { getEventEmitter().on('new-block', async (block) => { if (this.#isSynced) { + await this.getLatestBlocks(block); // Invalidate the balance cache to keep immature balance updated this.#mempool.invalidateBalanceCache(); - await this.getLatestBlocks(block); getEventEmitter().emit('new-tx'); } }); @@ -851,11 +854,9 @@ export class Wallet { async (blockCount) => { const cNet = getNetwork(); let block; - // Don't ask for the exact last block that arrived, - // since it takes around 1 minute for blockbook to make it API available for ( let blockHeight = this.#lastProcessedBlock + 1; - blockHeight < blockCount; + blockHeight <= blockCount; blockHeight++ ) { try { diff --git a/tests/integration/wallet/sync.spec.js b/tests/integration/wallet/sync.spec.js index 158820453..f8b646769 100644 --- a/tests/integration/wallet/sync.spec.js +++ b/tests/integration/wallet/sync.spec.js @@ -76,11 +76,7 @@ describe('Wallet sync tests', () => { ); // Mint the block with the transaction await mineAndSync(); - // getLatestBlocks sync up until chain tip - 1 block, - // so at this point walletHD doesn't still know about the UTXO he received - expect(walletHD.balance).toBe(1 * 10 ** 8); - // mine an empty block and verify that the tx arrived - await mineAndSync(); + // getLatestBlocks sync up until the actual chain tip expect(walletHD.balance).toBe((1 + 0.05) * 10 ** 8); // Sends funds back to the legacy wallet and verify that he also correctly receives funds @@ -102,7 +98,7 @@ describe('Wallet sync tests', () => { let nAddress = 0; // So according to BIP32 standard // wallets must be aware of addresses up to nAddress + MAX_ACCOUNT_GAP - for (let i = 0; i < 5; i++) { + for (let i = 1; i <= 5; i++) { nAddress += 20; let newAddress = walletHD.getAddressFromPath( path.slice(0, -1) + String(nAddress) @@ -114,10 +110,7 @@ describe('Wallet sync tests', () => { 0.01 * 10 ** 8 ); await mineAndSync(); - // Validate the balance of the HD wallet pre-tx-confirm expect(walletHD.balance).toBe((1 + 0.01 * i) * 10 ** 8); - // Mine a block with the Tx - await mineAndSync(); } }); it('recognizes immature balance', async () => {