fix parser

This commit is contained in:
2025-07-28 05:55:37 +03:00
parent 2a5271b9b2
commit 454de7eba7

View File

@@ -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 47
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);
} }