forked from panleone/PivxNodeController
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathshield.js
138 lines (127 loc) · 4.35 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import fs from "fs/promises";
import { makeRpc } from "./rpc.js";
import { RwLock } from "./lock.js";
export const shield = {
testnet: [],
mainnet: [],
};
export const shieldLock = new RwLock();
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);
let blockLength = 0;
while (true) {
await file.read(buffer, 0, 4, lastBlock.i + blockLength);
blockLength += 4;
const length = buffer.readInt32LE();
await file.read(buffer, 0, 4, lastBlock.i + blockLength);
const version = buffer.readUint8();
blockLength += length;
if (version === 0x5d) {
// This is a block footer
// After this it's the beginning of a new block
break;
}
if (version !== 3) {
console.error("Warning: Invalid tx", version);
}
}
await file.close();
await fs.truncate(shieldBinFile(isTestnet), lastBlock.i + blockLength);
}
export async function beginShieldSync(isTestnet) {
await shieldLock.write();
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);
await stream.write(length);
await 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;
await 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);
});
shieldLock.releaseWrite();
setTimeout(() => beginShieldSync(isTestnet), 1000 * 60); // Sync every minute
}
}
export async function getShieldBinary(isTestnet, startingByte = 0) {
await shieldLock.read();
const buffer = await fs.readFile(shieldBinFile(isTestnet));
shieldLock.releaseRead();
return Uint8Array.prototype.slice.call(buffer, startingByte);
}