Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve the upload metadata process #443

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/coordinator/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ zkeys/
proofs/
tally.json
session-keys.json
round-metadata.json
9 changes: 9 additions & 0 deletions packages/coordinator/round-metadata-example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"roundId": "Round-ID",
"description": "This is the test round.",
"startsAt": "2024-11-05T17:00:00.000Z",
"registrationEndsAt": "2024-11-06T10:00:00.000Z",
"votingStartsAt": "2024-11-06T10:00:00.000Z",
"votingEndsAt": "2024-11-06T17:00:00.000Z",
"tallyFile": "[storage url]/[roundId].json"
}
106 changes: 54 additions & 52 deletions packages/coordinator/scripts/uploadRoundMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { parse, isValid } from "date-fns";
import { enGB } from "date-fns/locale";
import dotenv from "dotenv";

import fs from "fs";
import path from "path";
import * as readline from "readline";

Expand All @@ -21,13 +22,26 @@ interface IUploadMetadataProps {
name: string;
}

const METADATA_PATH = path.resolve("./round-metadata.json");

dotenv.config({ path: path.resolve(import.meta.dirname, "../.env") });

/**
* A function to check if the input string is a valid date
* @param formattedDateStr a date string to be checked
* @returns if the date string is valid or not
*/
function isValidDate(formattedDateStr: string) {
const parsed = parse(`${formattedDateStr}Z`, "yyyy/M/d H:m:sX", new Date(), { locale: enGB });
return isValid(parsed);
}

/**
* This is a function for uploading metadata to the blob storage
* @param data an object containing metadata information about the round
* @param name the name of the round
* @returns url of the uploaded metadata
*/
export async function uploadRoundMetadata({ data, name }: IUploadMetadataProps): Promise<string> {
// NOTICE! this is when you use vercel storage, if you're using another tool, please change this part.
const blob = await put(name, JSON.stringify(data), {
Expand All @@ -39,6 +53,10 @@ export async function uploadRoundMetadata({ data, name }: IUploadMetadataProps):
return blob.url;
}

/**
* This function collect round information from the console
* @returns an object containing information of the round
*/
export async function collectMetadata(): Promise<RoundMetadata> {
const rl = readline.createInterface({
input: process.stdin,
Expand Down Expand Up @@ -66,7 +84,7 @@ export async function collectMetadata(): Promise<RoundMetadata> {
const askStartTime = () =>
new Promise<Date>((resolve, reject) => {
rl.question(
"When would you like to start this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ",
"When would you like to START this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in YOUR time) ",
(answer) => {
const valid = isValidDate(answer);

Expand All @@ -82,65 +100,40 @@ export async function collectMetadata(): Promise<RoundMetadata> {
});

const askRegistrationEndTime = () =>
new Promise<Date>((resolve, reject) => {
rl.question(
"When would you like to end the registration for applications? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ",
(answer) => {
const valid = isValidDate(answer);

if (!valid) {
reject(new Error("Please answer in valid format."));
}

// eslint-disable-next-line no-console
console.log(`Your application registration end date for this round is: ${answer}`);
resolve(new Date(answer));
},
);
});
new Promise<number>((resolve, reject) => {
rl.question("Please specify the duration of application registration in seconds: ", (answer) => {
const duration = Number(answer);

const askVotingStartTime = () =>
new Promise<Date>((resolve, reject) => {
rl.question(
"When would you like to start the voting for this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ",
(answer) => {
const valid = isValidDate(answer);

if (!valid) {
reject(new Error("Please answer in valid format."));
}
if (!duration) {
reject(new Error("Please answer in number."));
}

// eslint-disable-next-line no-console
console.log(`Your voting start date for this round is: ${answer}`);
resolve(new Date(answer));
},
);
// eslint-disable-next-line no-console
console.log(`Your application registration duration for this round is: ${answer}`);
resolve(duration);
});
});

const askVotingEndTime = () =>
new Promise<Date>((resolve, reject) => {
rl.question(
"When would you like to end the voting for this round? (Please respond in the format {Year}/{Month}/{Day} {Hour}:{Minute}:{Second} in UTC time) ",
(answer) => {
const valid = isValidDate(answer);
new Promise<number>((resolve, reject) => {
rl.question("Please specify the duration of voting in seconds: ", (answer) => {
const duration = Number(answer);

if (!valid) {
reject(new Error("Please answer in valid format."));
}
if (!duration) {
reject(new Error("Please answer in number."));
}

// eslint-disable-next-line no-console
console.log(`Your voting end date for this round is: ${answer}`);
resolve(new Date(answer));
},
);
// eslint-disable-next-line no-console
console.log(`Your voting duration for this round is: ${answer}`);
resolve(duration);
});
});

const roundId = await askRoundId();
const description = await askDescription();
const startsAt = await askStartTime();
const registrationEndsAt = await askRegistrationEndTime();
const votingStartsAt = await askVotingStartTime();
const votingEndsAt = await askVotingEndTime();
const registrationEndsIn = await askRegistrationEndTime();
const votingEndsIn = await askVotingEndTime();

rl.close();

Expand All @@ -151,15 +144,24 @@ export async function collectMetadata(): Promise<RoundMetadata> {
roundId,
description,
startsAt,
registrationEndsAt,
votingStartsAt,
votingEndsAt,
registrationEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000),
votingStartsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000),
votingEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000 + votingEndsIn * 1000),
tallyFile: `${vercelStoragePrefix}/tally-${roundId}.json`,
};
}

async function main(): Promise<void> {
const metadata = await collectMetadata();
let metadata: RoundMetadata;

// Try to read a metadata file first
if (fs.existsSync(METADATA_PATH)) {
metadata = JSON.parse(fs.readFileSync(METADATA_PATH, "utf-8")) as RoundMetadata;
} else {
// If there's no metadata file provided, collect metadata from console
metadata = await collectMetadata();
}

const url = await uploadRoundMetadata({ data: metadata, name: `${metadata.roundId}.json` });

// eslint-disable-next-line no-console
Expand Down
Loading