forked from panleone/PivxNodeController
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shield.js
117 lines (106 loc) · 3.71 KB
/
shield.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import fs from "fs/promises";
import { makeRpc } from "./rpc.js";
export const shield = {
testnet: [],
mainnet: [],
};
const shieldArrayFile = (isTestnet) =>
isTestnet ? "shield.testnet.json" : "shield.json";
const shieldBinFile = (isTestnet) =>
isTestnet ? "shield.testnet.bin" : "shield.bin";
async function recoverShieldbin(isTestnet) {
// If the shield bin was written, but the index was not
// We would repeat some blocks
// So we truncate the file based on the last index we have
const currentShield = shield[isTestnet ? "testnet" : "mainnet"];
let lastBlock = currentShield.at(-1);
if (!lastBlock) return;
const file = await fs.open(shieldBinFile(isTestnet), "r+");
const buffer = Buffer.alloc(4);
file.read(buffer, 0, 4, lastBlock.i);
const length = buffer.readInt32LE();
await file.close();
await fs.truncate(shieldBinFile(isTestnet), lastBlock.i + length);
}
export async function beginShieldSync(isTestnet) {
shield[isTestnet ? "testnet" : "mainnet"] =
JSON.parse(await fs.readFile(shieldArrayFile(isTestnet))) || [];
const currentShield = shield[isTestnet ? "testnet" : "mainnet"];
const { size } = await fs.stat(shieldBinFile(isTestnet));
await recoverShieldbin(isTestnet);
const file = await fs.open(shieldBinFile(isTestnet), "a");
const stream = file.createWriteStream();
let writtenBytes = 0;
let previousBlock = size;
try {
let block = currentShield.length
? currentShield[currentShield.length - 1].block + 1
: 2700501;
let { status, response } = await makeRpc(isTestnet, "getblockhash", block);
let blockHash = JSON.parse(response);
while (true) {
const { status, response } = await makeRpc(
isTestnet,
"getblock",
blockHash,
2,
);
const { tx, nextblockhash, time, height } = JSON.parse(response);
if (status === 200) {
let isShield = false;
for (const transaction of tx) {
if (transaction.hex.startsWith("03")) {
isShield = true;
const length = Buffer.alloc(4);
length.writeUint32LE(transaction.hex.length / 2);
stream.write(length);
stream.write(Buffer.from(transaction.hex, "hex"));
writtenBytes += transaction.hex.length / 2 + 4;
}
}
if (isShield) {
const bytes = Buffer.alloc(1 + 4 + 4 + 4);
// 5d indicates start of new block
// Other `5d`s are not escaped, this should not
// be relied upon, it's just confirmation that
// the stream is being read correctly
const length = Buffer.alloc(4);
length.writeUint32LE(1 + 4 + 4);
length.copy(bytes, 0, 0, bytes.length);
bytes.writeUint8(0x5d, 4);
bytes.writeInt32LE(height, 5);
bytes.writeInt32LE(time, 9);
writtenBytes += bytes.byteLength;
stream.write(bytes);
currentShield.push({ block, i: previousBlock });
previousBlock = size + writtenBytes;
}
blockHash = nextblockhash;
block += 1;
if (block % 10000 === 0) {
console.error(block);
}
} else {
throw new Error(response);
}
if (!nextblockhash) {
break;
}
}
} catch (e) {
console.error(e);
} finally {
await fs.writeFile(
shieldArrayFile(isTestnet),
JSON.stringify(currentShield),
);
await new Promise((res) => {
stream.close(res);
});
setTimeout(() => beginShieldSync(isTestnet), 1000 * 60); // Sync every minute
}
}
export async function getShieldBinary(isTestnet, startingByte = 0) {
const buffer = await fs.readFile(shieldBinFile(isTestnet));
return Uint8Array.prototype.slice.call(buffer, startingByte);
}