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 faster shield sync #430

Merged
merged 30 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e17ad21
Add faster shield sync
Duddino Oct 17, 2024
3de3aec
Change shield progress text to use bytes
Duddino Oct 17, 2024
e6e6c93
Remove `...`
Duddino Oct 17, 2024
e062639
Use beta lib
Duddino Oct 21, 2024
85708d8
Remove console.logs
Duddino Oct 21, 2024
9fe683e
Merge branch 'master' into faster-shield-sync
Duddino Oct 21, 2024
e5aba37
Fix merge error
Duddino Oct 21, 2024
774297c
Remove hard coded url
Duddino Oct 21, 2024
4129386
Update pivx-shield deps
Duddino Oct 21, 2024
aedbf38
Merge remote-tracking branch 'origin/master' into faster-shield-sync
Duddino Oct 31, 2024
9d2d21f
Use new pivx-shield beta
Duddino Oct 31, 2024
8ac57b2
Add txid do txs
Duddino Nov 2, 2024
704a8ca
Auto switch nodes during shield sync
Duddino Nov 2, 2024
2917cd2
Merge remote-tracking branch 'origin/master' into faster-shield-sync
Duddino Nov 2, 2024
a158221
Merge remote-tracking branch 'origin/master' into faster-shield-sync
Duddino Nov 7, 2024
0a4e5f7
Fix
Duddino Nov 7, 2024
df47bcd
Fix for real
Duddino Nov 8, 2024
7b9ded7
Merge remote-tracking branch 'origin/master' into faster-shield-sync
Duddino Nov 12, 2024
b9468b8
fix lint
Duddino Nov 12, 2024
a3e4f3c
Update pivx shield to latest beta
Duddino Nov 12, 2024
34d7422
random typo
Duddino Nov 12, 2024
b2fcd47
prettier
Duddino Nov 12, 2024
b60db33
Merge remote-tracking branch 'origin/master' into faster-shield-sync
Duddino Nov 12, 2024
1046295
Remove leftover comment
Duddino Nov 12, 2024
35bdf67
Only 1 digit after . instead of 2
Duddino Nov 12, 2024
51f88cc
Fix not switching nodes
Duddino Nov 12, 2024
9284d8a
Add base method
Duddino Nov 12, 2024
5882f70
Review
Duddino Nov 12, 2024
f8d45ee
Remove ghost import
Duddino Nov 12, 2024
2bdd937
Readd event
Duddino Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 12 additions & 21 deletions scripts/dashboard/WalletBalance.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,12 @@ const {

// Transparent sync status
const transparentSyncing = ref(false);
const transparentProgressSyncing = ref(0.0);
const percentage = ref(0.0);
const syncTStr = ref('');

// Shield sync status
const shieldSyncing = ref(false);
const shieldPercentageSyncing = ref(0.0);
const shieldBlockRemainingSyncing = ref(0);
const shieldSyncingStr = ref('');

// Shield transaction creation
const isCreatingTx = ref(false);
Expand Down Expand Up @@ -137,20 +136,20 @@ getEventEmitter().on(
]);
const progress = ((totalPages - i) / totalPages) * 100;
syncTStr.value = str;
transparentProgressSyncing.value = progress;
percentage.value = progress;
transparentSyncing.value = !finished;
}
);

getEventEmitter().on(
'shield-sync-status-update',
(blocks, totalBlocks, finished) => {
shieldPercentageSyncing.value = Math.round(
(blocks / totalBlocks) * 100
);
shieldBlockRemainingSyncing.value = (
totalBlocks - blocks
).toLocaleString('en-GB');
(bytes, totalBytes, finished) => {
percentage.value = Math.round((100 * bytes) / totalBytes);
const mb = bytes / 1_000_000;
const totalMb = totalBytes / 1_000_000;
shieldSyncingStr.value = `Syncing Shield (${mb.toFixed(
1
)}MB/${totalMb.toFixed(1)}MB)`;
shieldSyncing.value = !finished;
}
);
Expand Down Expand Up @@ -510,18 +509,10 @@ function restoreWallet() {
<i class="fas fa-spinner spinningLoading"></i>
</div>
<div style="width: 100%">
{{
transparentSyncing
? syncTStr
: `Syncing ${shieldBlockRemainingSyncing} Blocks...`
}}
{{ transparentSyncing ? syncTStr : shieldSyncingStr }}
<LoadingBar
:show="true"
:percentage="
transparentSyncing
? transparentProgressSyncing
: shieldPercentageSyncing
"
:percentage="percentage"
style="
border: 1px solid #932ecd;
border-radius: 4px;
Expand Down
14 changes: 13 additions & 1 deletion scripts/network/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
throw new Error('getProposals must be implemented');
}

async getShieldData(_initialBlock = 0) {
throw new Error('getShieldData must be implemented');
}

async getProposalVote(_proposalName, _collateralTxId, _outidx) {
throw new Error('getProposalVote must be implemented');
}
Expand All @@ -117,9 +121,9 @@
}

async submitProposal({
name,

Check warning on line 124 in scripts/network/network.js

View workflow job for this annotation

GitHub Actions / Run linters

'name' is defined but never used. Allowed unused args must match /^_/u
url,

Check warning on line 125 in scripts/network/network.js

View workflow job for this annotation

GitHub Actions / Run linters

'url' is defined but never used. Allowed unused args must match /^_/u
nPayments,

Check warning on line 126 in scripts/network/network.js

View workflow job for this annotation

GitHub Actions / Run linters

'nPayments' is defined but never used. Allowed unused args must match /^_/u
start,
address,
monthlyPayment,
Expand Down Expand Up @@ -200,7 +204,7 @@
// Use Nodes as a fallback
let strTXID = await this.#callRPC(
'/sendrawtransaction?params=' + hex,
true
'text'
);
strTXID = strTXID.replace(/"/g, '');
return { result: strTXID };
Expand Down Expand Up @@ -316,6 +320,14 @@
true
);
}

async getShieldData(startBlock) {
const res = await this.#fetchNode(
`/getshielddata?startBlock=${startBlock}`
);
if (!res.ok) throw new Error('Invalid response');
return res;
}
}

/**
Expand Down
4 changes: 4 additions & 0 deletions scripts/network/network_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ class NetworkManager {
);
}

async getShieldData(initialBlock = 0) {
return await this.#retryWrapper('getShieldData', true, initialBlock);
}

/**
* Submit a proposal
* @param {Object} options
Expand Down
15 changes: 13 additions & 2 deletions scripts/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,26 @@ export class Transaction {
return this.version >= SAPLING_TX_VERSION;
}

/**
* @returns {string} txid of the transaction
*/
get txid() {
if (!this.__original.#txid) {
this.__original.#txid = bytesToHex(
dSHA256(hexToBytes(this.serialize())).reverse()
this.__original.#txid = Transaction.getTxidFromHex(
this.serialize()
);
}
return this.__original.#txid;
}

/**
* @param {string} hex - Hex encoded transaction
* @returns {string} txid
*/
static getTxidFromHex(hex) {
return bytesToHex(dSHA256(hexToBytes(hex)).reverse());
}

get hasShieldData() {
return this.bindingSig !== '';
}
Expand Down
125 changes: 86 additions & 39 deletions scripts/wallet.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { validateMnemonic } from 'bip39';
import { decrypt } from './aes-gcm.js';
import { parseWIF } from './encoding.js';
import { bytesToNum, mergeUint8Arrays, parseWIF } from './encoding.js';
import { beforeUnloadListener, blockCount } from './global.js';
import { getNetwork } from './network/network_manager.js';
import { MAX_ACCOUNT_GAP, SHIELD_BATCH_SYNC_SIZE } from './chain_params.js';
Expand Down Expand Up @@ -772,48 +772,94 @@ export class Wallet {
if (!this.#shield || this.#isSynced) {
return;
}
const cNet = getNetwork();

try {
const blockHeights = (await cNet.getShieldBlockList()).filter(
(b) => b > this.#shield.getLastSyncedBlock()
const network = getNetwork();
const req = await network.getShieldData(
wallet.#shield.getLastSyncedBlock() + 1
);
const batchSize = SHIELD_BATCH_SYNC_SIZE;
let handled = 0;
let downloaded = 0;
const blocks = [];
let syncing = false;
await startBatch(
async (i) => {
let block;
block = await cNet.getBlock(blockHeights[i]);
downloaded++;
blocks[i] = block;
// We need to process blocks monotically
// When we get a block, start from the first unhandled
// One and handle as many as possible
for (let j = handled; blocks[j]; j = handled) {
if (syncing) break;
syncing = true;
handled++;
await this.#shield.handleBlock(blocks[j]);
// Backup every 500 handled blocks
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];
syncing = false;
if (!req.ok) throw new Error("Couldn't sync shield");
const reader = req.body.getReader();

/** @type{string[]} Array of txs in the current block */
let txs = [];
let processedBytes = 0;
const length = req.headers.get('Content-Length');
/** @type {Uint8Array} Array of bytes that we are processing **/
const processing = new Uint8Array(length);
getEventEmitter().emit(
'shield-sync-status-update',
0,
length,
false
);
let i = 0;
let max = 0;
while (true) {
/**
* @type {{done: boolean, value: Uint8Array?}}
*/
const { done, value } = await reader.read();
/**
* Array of blocks ready to pass to the shield library
* @type {{txs: string[]; height: number; time: number}[]}
*/
const blocksArray = [];

if (value) {
// Append received bytes in the processing array
processing.set(value, max);
max += value.length;
processedBytes += value.length;
// Loop until we have less than 4 bytes (length)
while (max - i >= 4) {
const length = Number(
bytesToNum(processing.subarray(i, i + 4))
);
// If we have less bytes than the length break and wait for the next
// batch of bytes
if (max - i < length) break;

i += 4;
const bytes = processing.subarray(i, length + i);
i += length;
// 0x5d rapresents the block
if (bytes[0] === 0x5d) {
const height = Number(
bytesToNum(bytes.slice(1, 5))
);
const time = Number(bytesToNum(bytes.slice(5, 9)));

blocksArray.push({ txs, height, time });
txs = [];
} else if (bytes[0] === 0x03) {
// 0x03 is the tx version. We should only get v3 transactions
const hex = bytesToHex(bytes);
txs.push({
hex,
txid: Transaction.getTxidFromHex(hex),
});
} else {
// This is neither a block or a tx.
throw new Error('Failed to parse shield binary');
}
}
}

// Process the current batch of blocks before starting to parse the next one
if (blocksArray.length) {
await this.#shield.handleBlocks(blocksArray);
}
// Emit status update
getEventEmitter().emit(
'shield-sync-status-update',
processedBytes,
length,
false
);
if (done) break;
}

getEventEmitter().emit(
'shield-sync-status-update',
downloaded - 1,
blockHeights.length,
false
);
},
blockHeights.length,
batchSize
);
getEventEmitter().emit('shield-sync-status-update', 0, 0, true);
} catch (e) {
debugError(DebugTopics.WALLET, e);
Copy link
Member

@panleone panleone Nov 12, 2024

Choose a reason for hiding this comment

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

for future PR: we have to change this flow

errors finish in the catch block and after that wallet is just marked as synced even if it failed

Expand Down Expand Up @@ -965,6 +1011,7 @@ export class Wallet {
}
const loadRes = await PIVXShield.load(cAccount.shieldData);
this.#shield = loadRes.pivxShield;
getEventEmitter().emit('shield-loaded-from-disk');
// Load operation was not successful!
// Provided data are not compatible with the latest PIVX shield version.
// Resetting the shield object is required
Expand Down
Loading