get poll votes message

This commit is contained in:
2025-08-06 20:39:01 +03:00
parent 2a85e9177a
commit 0dccf56b2f
5 changed files with 251 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ import {
handleLastReference,
handleNamesMessage,
handlePollsMessage,
handlePollVotesMessage,
handlePrimaryNameMessage,
handleProcessTransactionResponseMessage,
handlePublicKeyMessage,
@@ -36,7 +37,9 @@ import {
createGetNamesForSalePayload,
createGetNamesPayload,
createGetOwnerGroupsPayload,
createGetPollPayload,
createGetPollsPayload,
createGetPollVotesPayload,
createGetPrimaryNamePayload,
createGetPublickeyFromAddressPayload,
createGetUnitFeePayload,
@@ -119,6 +122,38 @@ export async function getPolls(
return handlePollsMessage(res);
}
export async function getPoll(pollName: string): Promise<any> {
const client = getRandomClient();
if (!client) throw new Error('No available peers');
const res: Buffer = await client.sendRequest(
MessageType.GET_POLL,
createGetPollPayload(pollName)
);
const data = handlePollsMessage(res);
if (Array.isArray(data) && data.length > 0) {
return data[0];
}
throw new Error('No poll data');
}
export async function getPollVotes(
pollName: string,
onlyCounts: boolean
): Promise<any> {
const client = getRandomClient();
if (!client) throw new Error('No available peers');
const res: Buffer = await client.sendRequest(
MessageType.GET_POLL_VOTES,
createGetPollVotesPayload(pollName, onlyCounts)
);
const data = handlePollVotesMessage(res);
return data;
}
export async function getGroupMembers(
groupId: number,
onlyAdmins: boolean,

View File

@@ -18,7 +18,9 @@ import {
getNames,
getNamesForSale,
getOwnerGroups,
getPoll,
getPolls,
getPollVotes,
getPrimaryName,
getPublickeyFromAddress,
getSearchNames,
@@ -369,6 +371,26 @@ export async function createHttpServer() {
}
});
app.get('/polls/:pollName', async (req, res) => {
try {
const pollName = req.params.pollName;
const pollInfo = await getPoll(pollName);
res.json(pollInfo);
} catch (err: any) {
res.status(500).type('text').send(`Error: ${err.message}`);
}
});
app.get('/polls/votes/:pollName', async (req, res) => {
try {
const pollName = req.params.pollName;
const onlyCounts = req.query.onlyCounts === 'true';
const pollInfo = await getPollVotes(pollName, Boolean(onlyCounts));
res.json(pollInfo);
} catch (err: any) {
res.status(500).type('text').send(`Error: ${err.message}`);
}
});
const server = createServer(app);
const wss = new WebSocketServer({ noServer: true });

View File

@@ -391,6 +391,170 @@ export function handlePollsMessage(buffer) {
return polls;
}
// export function handlePollVotesMessage(buffer) {
// let offset = 0;
// const { value: totalVotes, size: s1 } = readInt(buffer, offset);
// offset += s1;
// const { value: totalWeight, size: s2 } = readInt(buffer, offset);
// offset += s2;
// const { value: countSize, size: s3 } = readInt(buffer, offset);
// offset += s3;
// const voteCounts = [];
// for (let i = 0; i < countSize; i++) {
// const { value: optionName, size: s4 } = readSizedString(buffer, offset);
// offset += s4;
// const { value: voteCount, size: s5 } = readInt(buffer, offset);
// offset += s5;
// voteCounts.push({ optionName, voteCount });
// }
// const { value: weightSize, size: s6 } = readInt(buffer, offset);
// offset += s6;
// const voteWeights = [];
// for (let i = 0; i < weightSize; i++) {
// const { value: optionName, size: s7 } = readSizedString(buffer, offset);
// offset += s7;
// const { value: voteWeight, size: s8 } = readInt(buffer, offset);
// offset += s8;
// voteWeights.push({ optionName, voteWeight });
// }
// const { value: hasVotes, size: s9 } = readInt(buffer, offset);
// offset += s9;
// let votes = null;
// if (hasVotes === 1) {
// const { value: votesCount, size: s10 } = readInt(buffer, offset);
// offset += s10;
// votes = [];
// for (let i = 0; i < votesCount; i++) {
// const voterPublicKeyBytes = buffer.subarray(offset, offset + 32);
// const voterPublicKey = bs58.encode(voterPublicKeyBytes);
// offset += 32;
// const { value: optionIndex, size: s11 } = readInt(buffer, offset);
// offset += s11;
// votes.push({ voterPublicKey, optionIndex });
// }
// }
// return {
// totalVotes,
// totalWeight,
// voteCounts,
// voteWeights,
// votes,
// };
// }
// Debugger version of handlePollVotesMessage
export function handlePollVotesMessage(buffer) {
const dataView = new DataView(
buffer.buffer,
buffer.byteOffset,
buffer.byteLength
);
let offset = 0;
const readInt = () => {
const value = dataView.getInt32(offset, false); // big-endian
offset += 4;
return value;
};
const readSizedString = () => {
const length = readInt();
if (length === 0) return '';
const strBytes = buffer.subarray(offset, offset + length);
offset += length;
return new TextDecoder().decode(strBytes);
};
const readBytes = (length) => {
const bytes = buffer.subarray(offset, offset + length);
offset += length;
return bytes;
};
const log = (...args) => console.log('[Offset:', offset, ']', ...args);
// hasVotes flag
const hasVotes = readInt();
log('hasVotes:', hasVotes);
let votes = null;
if (hasVotes === 1) {
const votesCount = readInt();
log('votesCount:', votesCount);
votes = [];
for (let i = 0; i < votesCount; i++) {
const pollName = readSizedString();
log(`vote[${i}] pollName:`, pollName);
const voterPublicKeyBytes = readBytes(32);
const voterPublicKey = bs58.encode(voterPublicKeyBytes);
log(`vote[${i}] voterPublicKey:`, voterPublicKey);
const optionIndex = readInt();
log(`vote[${i}] optionIndex:`, optionIndex);
votes.push({ pollName, voterPublicKey, optionIndex });
}
}
const totalVotes = readInt();
log('totalVotes:', totalVotes);
const totalWeight = readInt();
log('totalWeight:', totalWeight);
const voteCountsSize = readInt();
log('voteCounts size:', voteCountsSize);
const voteCounts = [];
for (let i = 0; i < voteCountsSize; i++) {
const optionName = readSizedString();
const voteCount = readInt();
log(`voteCount[${i}] optionName:`, optionName, 'count:', voteCount);
voteCounts.push({ optionName, voteCount });
}
const voteWeightsSize = readInt();
log('voteWeights size:', voteWeightsSize);
const voteWeights = [];
for (let i = 0; i < voteWeightsSize; i++) {
const optionName = readSizedString();
const voteWeight = readInt();
log(`voteWeight[${i}] optionName:`, optionName, 'weight:', voteWeight);
voteWeights.push({ optionName, voteWeight });
}
log('Final offset:', offset, '/', buffer.length);
return {
hasVotes: hasVotes === 1,
votes,
totalVotes,
totalWeight,
voteCounts,
voteWeights,
};
}
export function handleUnitFee(payload: Buffer): bigint {
if (payload.length !== 8) {
throw new Error(

View File

@@ -52,4 +52,7 @@ export enum MessageType {
POLLS = 326,
GET_POLLS = 327,
GET_POLL = 328,
POLL_VOTES = 329,
GET_POLL_VOTES = 330,
}

View File

@@ -191,6 +191,33 @@ export function createGetPollsPayload(
return Buffer.concat([limitBuffer, offsetBuffer, reverseBuffer]);
}
export function createGetPollPayload(pollName: string): Buffer {
const pollNameBuffer = Buffer.from(pollName, 'utf-8');
const pollNameLength = Buffer.alloc(4);
pollNameLength.writeInt32BE(pollNameBuffer.length);
const payloadParts = [pollNameLength, pollNameBuffer];
return Buffer.concat(payloadParts);
}
export function createGetPollVotesPayload(pollName, onlyCounts) {
const pollNameBuffer = Buffer.from(pollName, 'utf-8');
const pollNameLengthBuffer = Buffer.alloc(4);
pollNameLengthBuffer.writeInt32BE(pollNameBuffer.length);
const onlyCountsBuffer = Buffer.alloc(4);
onlyCountsBuffer.writeInt32BE(onlyCounts ? 1 : 0);
const buffer = Buffer.concat([
pollNameLengthBuffer,
pollNameBuffer,
onlyCountsBuffer,
]);
return buffer;
}
export function createGetGroupMembersPayload(
groupId: number,
onlyAdmins: boolean,