mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-11-02 14:07:04 +00:00
fix parser
This commit is contained in:
@@ -33,7 +33,6 @@ function sbrk(size: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processWaitingQueue() {
|
function processWaitingQueue() {
|
||||||
console.log('Processing waiting queue...');
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < waitingQueue.length) {
|
while (i < waitingQueue.length) {
|
||||||
const request = waitingQueue[i];
|
const request = waitingQueue[i];
|
||||||
@@ -98,25 +97,35 @@ function resetMemory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseMessage(buffer: Buffer) {
|
function parseMessage(buffer: Buffer) {
|
||||||
if (buffer.length < 17) return null;
|
const magic = buffer.subarray(0, 4).toString('ascii');
|
||||||
if (buffer.subarray(0, 4).toString('ascii') !== 'QORT') return null;
|
if (magic !== 'QORT') return null;
|
||||||
|
|
||||||
const messageType = buffer.readUInt32BE(4);
|
const type = buffer.readUInt32BE(4); // bytes 4–7
|
||||||
const payloadLength = buffer.readUInt32BE(9);
|
const hasId = buffer.readUInt8(8); // byte 8
|
||||||
const checksum = buffer.subarray(13, 17);
|
|
||||||
const payload = buffer.subarray(17, 17 + payloadLength);
|
|
||||||
|
|
||||||
const expectedChecksum = crypto
|
let offset = 9;
|
||||||
.createHash('sha256')
|
let id = -1;
|
||||||
.update(payload)
|
if (hasId) {
|
||||||
.digest()
|
id = buffer.readUInt32BE(offset);
|
||||||
.subarray(0, 4);
|
offset += 4;
|
||||||
if (!checksum.equals(expectedChecksum)) return null;
|
}
|
||||||
|
|
||||||
|
const payloadLength = buffer.readUInt32BE(offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
const checksum = buffer.subarray(offset, offset + 4);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
const payload = buffer.subarray(offset, offset + payloadLength);
|
||||||
|
offset += payloadLength;
|
||||||
|
|
||||||
|
const totalLength = offset;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messageType,
|
messageType: type,
|
||||||
|
id,
|
||||||
payload,
|
payload,
|
||||||
totalLength: 17 + payloadLength,
|
totalLength,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,19 +151,37 @@ function createChallengePayload(
|
|||||||
return Buffer.concat([Buffer.from(publicKey), Buffer.from(challenge)]);
|
return Buffer.concat([Buffer.from(publicKey), Buffer.from(challenge)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeFramedMessage(type: number, payload: Buffer): Buffer {
|
function encodeFramedMessage(
|
||||||
const header = Buffer.from('QORT');
|
type: number,
|
||||||
|
payload: Buffer,
|
||||||
|
id: number
|
||||||
|
): Buffer {
|
||||||
|
const header = Buffer.from('QORT', 'ascii');
|
||||||
const typeBuf = Buffer.alloc(4);
|
const typeBuf = Buffer.alloc(4);
|
||||||
typeBuf.writeUInt32BE(type);
|
typeBuf.writeUInt32BE(type);
|
||||||
const hasId = Buffer.from([0]);
|
|
||||||
|
const hasId = Buffer.from([1]);
|
||||||
|
const idBuf = Buffer.alloc(4);
|
||||||
|
idBuf.writeUInt32BE(id);
|
||||||
|
|
||||||
const length = Buffer.alloc(4);
|
const length = Buffer.alloc(4);
|
||||||
length.writeUInt32BE(payload.length);
|
length.writeUInt32BE(payload.length);
|
||||||
|
|
||||||
const checksum = crypto
|
const checksum = crypto
|
||||||
.createHash('sha256')
|
.createHash('sha256')
|
||||||
.update(payload)
|
.update(payload)
|
||||||
.digest()
|
.digest()
|
||||||
.subarray(0, 4);
|
.subarray(0, 4);
|
||||||
return Buffer.concat([header, typeBuf, hasId, length, checksum, payload]);
|
|
||||||
|
return Buffer.concat([
|
||||||
|
header,
|
||||||
|
typeBuf,
|
||||||
|
hasId,
|
||||||
|
idBuf,
|
||||||
|
length,
|
||||||
|
checksum,
|
||||||
|
payload,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ed25519ToX25519Private(edSeed: Uint8Array): Uint8Array {
|
function ed25519ToX25519Private(edSeed: Uint8Array): Uint8Array {
|
||||||
@@ -183,9 +210,15 @@ export class LiteNodeClient {
|
|||||||
private theirXPublicKey: Uint8Array | null = null;
|
private theirXPublicKey: Uint8Array | null = null;
|
||||||
private theirChallenge: Uint8Array | null = null;
|
private theirChallenge: Uint8Array | null = null;
|
||||||
private ourChallenge = crypto.randomBytes(32);
|
private ourChallenge = crypto.randomBytes(32);
|
||||||
|
private pingInterval: NodeJS.Timeout | null = null;
|
||||||
|
private pendingPingIds = new Set<number>();
|
||||||
|
|
||||||
private alreadyResponded = false;
|
private alreadyResponded = false;
|
||||||
|
|
||||||
|
private messageQueue: Buffer[] = [];
|
||||||
|
private isSending: boolean = false;
|
||||||
|
private nextMessageId: number = 1;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: string,
|
private host: string,
|
||||||
private port: number = 12392
|
private port: number = 12392
|
||||||
@@ -296,14 +329,37 @@ export class LiteNodeClient {
|
|||||||
|
|
||||||
private async handleResponse(payload: Buffer) {
|
private async handleResponse(payload: Buffer) {
|
||||||
this.startPinging();
|
this.startPinging();
|
||||||
const nonce = payload.readUInt32BE(0);
|
|
||||||
const responseHash = payload.subarray(4, 36);
|
|
||||||
console.log('📩 Received RESPONSE from core:');
|
|
||||||
console.log(' Nonce:', nonce);
|
|
||||||
console.log(' Hash:', responseHash.toString('hex'));
|
|
||||||
resetMemory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lastHandledPingIds = new Set<number>();
|
||||||
|
|
||||||
|
private handlePing(id: number) {
|
||||||
|
if (this.pendingPingIds.has(id)) {
|
||||||
|
// This is a reply to our ping — success.
|
||||||
|
this.pendingPingIds.delete(id);
|
||||||
|
console.log('✅ Received PING reply for ID', id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.lastHandledPingIds.has(id)) {
|
||||||
|
return; // Already replied to this ping
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = encodeFramedMessage(
|
||||||
|
MessageType.PING,
|
||||||
|
Buffer.from([0x00]), // Required non-empty payload
|
||||||
|
id
|
||||||
|
);
|
||||||
|
this.messageQueue.push(reply);
|
||||||
|
this.flushMessageQueue();
|
||||||
|
|
||||||
|
this.lastHandledPingIds.add(id);
|
||||||
|
console.log('🔁 Replied to PING with ID', id);
|
||||||
|
|
||||||
|
// Optionally clean up old IDs to avoid memory leak
|
||||||
|
if (this.lastHandledPingIds.size > 1000) {
|
||||||
|
this.lastHandledPingIds.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
async connect(): Promise<void> {
|
async connect(): Promise<void> {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
|
||||||
@@ -312,19 +368,20 @@ export class LiteNodeClient {
|
|||||||
{ host: this.host, port: this.port },
|
{ host: this.host, port: this.port },
|
||||||
() => {
|
() => {
|
||||||
console.log(`✅ Connected to ${this.host}:${this.port}`);
|
console.log(`✅ Connected to ${this.host}:${this.port}`);
|
||||||
const helloPayload = createHelloPayload();
|
this.sendMessage(MessageType.HELLO, createHelloPayload());
|
||||||
this.sendMessage(MessageType.HELLO, helloPayload);
|
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.socket.on('data', (data: Buffer) => {
|
this.socket.on('data', (data: Buffer) => {
|
||||||
|
// console.log('📦 Raw data:', data.toString('hex'));
|
||||||
this.buffer = Buffer.concat([this.buffer, data]);
|
this.buffer = Buffer.concat([this.buffer, data]);
|
||||||
while (this.buffer.length >= 17) {
|
while (true) {
|
||||||
const parsed = parseMessage(this.buffer);
|
const parsed = parseMessage(this.buffer);
|
||||||
if (!parsed) break;
|
if (!parsed) break;
|
||||||
|
|
||||||
const { messageType, payload, totalLength } = parsed;
|
const { messageType, payload, totalLength, id } = parsed;
|
||||||
this.buffer = this.buffer.subarray(totalLength);
|
this.buffer = this.buffer.subarray(totalLength);
|
||||||
|
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
@@ -340,6 +397,9 @@ export class LiteNodeClient {
|
|||||||
case MessageType.RESPONSE:
|
case MessageType.RESPONSE:
|
||||||
this.handleResponse(payload);
|
this.handleResponse(payload);
|
||||||
break;
|
break;
|
||||||
|
case MessageType.PING:
|
||||||
|
this.handlePing(id);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`⚠️ Unhandled message type: ${messageType}`);
|
console.warn(`⚠️ Unhandled message type: ${messageType}`);
|
||||||
}
|
}
|
||||||
@@ -356,20 +416,58 @@ export class LiteNodeClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(type: MessageType, payload: Buffer) {
|
private flushMessageQueue() {
|
||||||
if (!this.socket) throw new Error('Socket not connected');
|
if (
|
||||||
const framed = encodeFramedMessage(type, payload);
|
this.isSending ||
|
||||||
this.socket.write(framed);
|
!this.socket ||
|
||||||
|
this.socket.destroyed ||
|
||||||
|
!this.socket.writable
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (this.messageQueue.length > 0) {
|
||||||
|
const message = this.messageQueue[0];
|
||||||
|
const flushed = this.socket.write(message);
|
||||||
|
if (!flushed) {
|
||||||
|
this.isSending = true;
|
||||||
|
this.socket.once('drain', () => {
|
||||||
|
this.isSending = false;
|
||||||
|
this.flushMessageQueue();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.messageQueue.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendMessage(type: MessageType, payload: Buffer, id?: number) {
|
||||||
|
const messageId = id ?? this.nextMessageId++;
|
||||||
|
const framed = encodeFramedMessage(type, payload, messageId);
|
||||||
|
console.log('🔐 Response hash:', framed.toString('hex'));
|
||||||
|
this.messageQueue.push(framed);
|
||||||
|
this.flushMessageQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
startPinging(intervalMs: number = 5000) {
|
startPinging(intervalMs: number = 5000) {
|
||||||
setInterval(() => {
|
if (this.pingInterval) clearInterval(this.pingInterval);
|
||||||
try {
|
|
||||||
this.sendMessage(MessageType.PING, Buffer.alloc(0));
|
this.pingInterval = setInterval(() => {
|
||||||
console.log('📡 Sent PING');
|
if (!this.socket || this.socket.destroyed) {
|
||||||
} catch (err) {
|
console.warn('⚠️ Skipping PING: socket not connected');
|
||||||
console.error('❌ Failed to send PING:', err);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const id = this.nextMessageId++;
|
||||||
|
this.pendingPingIds.add(id);
|
||||||
|
const pingMessage = encodeFramedMessage(
|
||||||
|
MessageType.PING,
|
||||||
|
Buffer.from([0x00]),
|
||||||
|
id
|
||||||
|
);
|
||||||
|
this.messageQueue.push(pingMessage);
|
||||||
|
this.flushMessageQueue();
|
||||||
|
|
||||||
|
console.log('📡 Sent PING with ID', id);
|
||||||
}, intervalMs);
|
}, intervalMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user