chia-network-scanner
Version:
Scans the Chia network for active full nodes
185 lines • 7.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtocolMessageTypes = exports.decodeString = exports.encodeString = exports.decodeMessage = exports.encodeMessage = void 0;
const peer_1 = require("./peer");
const log_1 = require("./log");
const ProtocolMessageTypes = {
handshake: 1,
handshake_ack: 2,
request_peers: 43,
respond_peers: 44
};
exports.ProtocolMessageTypes = ProtocolMessageTypes;
const encodeHandshake = (handshake) => {
const { network_id, protocol_version, software_version, server_port, node_type } = handshake;
const serverPortBuffer = Buffer.alloc(2);
serverPortBuffer.writeUInt16BE(server_port);
const message = Buffer.concat([
encodeString(network_id),
encodeString(protocol_version),
encodeString(software_version),
serverPortBuffer,
Buffer.from([node_type]),
// todo: add capabilities here, a new thing! https://github.com/Chia-Network/chia-blockchain/blob/main/src/protocols/shared_protocol.py#L30
// for now just wacking some dummy ones on and seeing what happens
Buffer.from('0000000100010000000131', 'hex')
]);
const messageLengthBuffer = Buffer.alloc(4);
messageLengthBuffer.writeUInt32BE(message.length);
return Buffer.concat([
Buffer.from([ProtocolMessageTypes.handshake]),
Buffer.from([0]),
messageLengthBuffer,
// message id apparently optional - maybe sometinh here is missing..? - at least need to be able to decode it right...?
message,
]);
};
const encodeRespondPeers = (respondPeers) => {
const { peer_list } = respondPeers;
const peerListSizeBuffer = Buffer.alloc(4);
peerListSizeBuffer.writeUIntBE(peer_list.length, 0, 4);
const peers = peer_list.reduce((accum, { hostname, port, timestamp }) => {
const portBuffer = Buffer.alloc(2);
portBuffer.writeUInt16BE(port);
const timestampBuffer = Buffer.alloc(8);
timestampBuffer.writeBigUInt64BE(BigInt(timestamp));
return Buffer.concat([
accum,
encodeString(hostname),
portBuffer,
timestampBuffer
]);
}, Buffer.alloc(0));
const message = Buffer.concat([
peerListSizeBuffer,
peers
]);
const messageLengthBuffer = Buffer.alloc(4);
messageLengthBuffer.writeUInt32BE(message.length);
return Buffer.concat([
Buffer.from([ProtocolMessageTypes.respond_peers]),
Buffer.from([0]),
messageLengthBuffer,
message,
]);
};
const encodeMessage = (messageType, data) => {
// Messages that only have a type and don't contain any data
if (messageType === ProtocolMessageTypes.handshake_ack || messageType === ProtocolMessageTypes.request_peers) {
const messageLengthBuffer = Buffer.alloc(4);
messageLengthBuffer.writeUInt32BE(0);
const message = Buffer.concat([
Buffer.from([messageType]),
Buffer.from([0]),
messageLengthBuffer,
]);
log_1.log.info(`Encoded ${messageType} ${message.toString('hex')}`);
return message;
}
if (messageType === ProtocolMessageTypes.handshake) {
const message = encodeHandshake(data);
log_1.log.info(`Encoded handshake ${message.toString('hex')}`);
return message;
}
if (messageType === ProtocolMessageTypes.respond_peers) {
const message = encodeRespondPeers(data);
log_1.log.info(`Encoded respond peers ${message.toString('hex')}`);
return message;
}
throw new Error(`Could not encode message of type ${messageType}`);
};
exports.encodeMessage = encodeMessage;
const decodeHandshake = (message) => {
let currentPos = 0;
const network_id = decodeString(message.slice(currentPos));
currentPos += 4 + network_id.length;
const protocol_version = decodeString(message.slice(currentPos));
currentPos += 4 + protocol_version.length;
const software_version = decodeString(message.slice(currentPos));
currentPos += 4 + software_version.length;
const server_port = message.readUInt16BE(currentPos);
currentPos += 2;
const node_type = message[currentPos];
// Todo: there is also capabilities but it's not needed for now so not implementing it on decode
return {
network_id,
protocol_version,
software_version,
server_port,
node_type
};
};
const decodeRespondPeers = (message) => {
let currentPos = 0;
const peer_list = [];
const peer_list_size = message.readUIntBE(currentPos, 4);
currentPos += 4;
for (let i = 0; i < peer_list_size; i++) {
const hostname = decodeString(message.slice(currentPos));
currentPos += 4 + hostname.length;
const port = message.readUInt16BE(currentPos);
currentPos += 2;
const timestamp = Number(message.readBigUInt64BE(currentPos));
currentPos += 8;
peer_list.push(new peer_1.Peer({
hostname,
port,
timestamp
}));
}
return { peer_list };
};
const decodeMessage = (data) => {
let currentPos = 0;
const messageType = data[currentPos];
currentPos++;
const isIdPresent = data[currentPos];
currentPos++;
if (isIdPresent) {
// Id is present so we need to read another 16 bytes for now
// Not actually reading it for now as not planning on using it...
// could maybe use it later on....
currentPos += 2;
}
const messageLength = data.readUInt32BE(currentPos);
currentPos += 4;
const message = data.slice(currentPos, currentPos + messageLength);
if (messageType === ProtocolMessageTypes.handshake_ack || messageType === ProtocolMessageTypes.request_peers) {
return {};
}
if (messageType === ProtocolMessageTypes.handshake) {
return decodeHandshake(message);
}
if (messageType === ProtocolMessageTypes.respond_peers) {
return decodeRespondPeers(message);
}
log_1.log.warn(`Could not decode message of type ${messageType}`);
return null;
};
exports.decodeMessage = decodeMessage;
// https://github.com/Chia-Network/chia-blockchain/blob/main/src/util/streamable.py#L206
const decodeString = (buffer) => {
const str_size_bytes = buffer.readUIntBE(0, 4);
if (str_size_bytes === 0) {
throw new Error('EOF while decoding string size');
}
const stringBuffer = buffer.slice(4, 4 + str_size_bytes);
const result = stringBuffer.toString('utf-8');
if (!result) {
throw new Error('EOF while decoding string');
}
return result;
};
exports.decodeString = decodeString;
// https://github.com/Chia-Network/chia-blockchain/blob/main/src/util/streamable.py#L206
const encodeString = (str) => {
const str_size_bytes = str.length;
const strBuffer = Buffer.alloc(4 + str_size_bytes);
// Encode length of string in first 4 bytes
strBuffer.writeIntBE(str_size_bytes, 0, 4);
// Encode the actual string
strBuffer.write(str, 4, 'utf-8');
return strBuffer;
};
exports.encodeString = encodeString;
//# sourceMappingURL=encoder.js.map