From 7d2de056fdbb36655808cc5f5522714ff4eb1c99 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 10:51:41 +0200 Subject: [PATCH 1/6] Log entry point on start --- main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/main.ts b/main.ts index 4d2a355..1346d29 100644 --- a/main.ts +++ b/main.ts @@ -89,6 +89,7 @@ if (import.meta.main) { const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: config.prefix }); const [firstAccount] = await wallet.getAccounts(); + console.log(`Connecting to ${rpcEndpoint} ...`); const cometClient = await connectComet(rpcEndpoint); const client = await SigningCosmWasmClient.createWithSigner(cometClient, wallet, { gasPrice: GasPrice.fromString(config.gasPrice), From 627bbcccb5587c44ec9193bc5089f05478f04314 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 10:52:10 +0200 Subject: [PATCH 2/6] Show number of jobs --- main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.ts b/main.ts index 1346d29..0bb4218 100644 --- a/main.ts +++ b/main.ts @@ -214,7 +214,7 @@ if (import.meta.main) { const past = rounds.filter((r) => r <= n); const future = rounds.filter((r) => r > n); console.log( - `Past: %o, Future: %o`, + `Past (${past.length}): %o, Future (${future.length}): %o`, past, future, ); From 67219d4340d2209d9d8ca6aba127bb46c6af7c0c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 11:03:16 +0200 Subject: [PATCH 3/6] Avoid parallel processing --- submitter.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/submitter.ts b/submitter.ts index 46e8c4b..6a97537 100644 --- a/submitter.ts +++ b/submitter.ts @@ -64,7 +64,13 @@ export class Submitter { /** Handle jobs for which the round should be public */ public async handlePastRoundsWithJobs(rounds: number[]): Promise { - await Promise.all(rounds.map((round) => this.handlePastRoundWithJobs(round))); + // Do not process those jobs in parallel in order to avoid sequence mismatches. + + // We process from new to old for no good reason + const sorted = rounds.sort().reverse(); + for (const round of sorted) { + await this.handlePastRoundWithJobs(round); + } } private async handlePastRoundWithJobs(round: number): Promise { @@ -106,6 +112,12 @@ export class Submitter { } } + /** + * Takes a beacon, submits it through a transaction and wait for inclusion in a block. + * + * Do not call this multiple times in parallel in order to avoid race conditions and + * account sequence mismatches. + */ private async submit(beacon: Pick) { if (this.submitted.has(beacon.round)) return; this.submitted.add(beacon.round); From a70976e38cadfe12293233d24f05064ac0f0f647 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 12:21:59 +0200 Subject: [PATCH 4/6] Better organize jobsChecker --- jobs.ts | 2 +- main.ts | 51 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/jobs.ts b/jobs.ts index b86cd95..110a912 100644 --- a/jobs.ts +++ b/jobs.ts @@ -26,7 +26,7 @@ function formatDuration(durationInMs: number): string { return `${inSeconds.toFixed(1)}s`; } -export class JobsObserver { +export class JobsChecker { private readonly noisClient: CosmWasmClient; private readonly gateway: string; diff --git a/main.ts b/main.ts index 0bb4218..e549715 100644 --- a/main.ts +++ b/main.ts @@ -24,7 +24,7 @@ import { sleep, watch, } from "./deps.ts"; -import { JobsObserver } from "./jobs.ts"; +import { JobsChecker } from "./jobs.ts"; import { Submitter } from "./submitter.ts"; import { queryIsAllowlisted, queryIsIncentivized } from "./drand_contract.ts"; import { Config } from "./config.ts"; @@ -59,6 +59,11 @@ function getNextSignData(): SignerData { return out; } +// If this is set to false, the bot will only observe drand and check for each +// round **once** if it was incentivised. Everything that is markt as incentivised +// too late or is unprocessed for whatever reason will not be submitted. +const cleanupOldJobs = true; + if (import.meta.main) { const { default: config }: { default: Config } = await import("./config.json", { assert: { type: "json" }, @@ -149,7 +154,10 @@ if (import.meta.main) { })(), ]); - const jobs = new JobsObserver(client, gatewayAddress); + let jobsChecker: JobsChecker | undefined; + if (cleanupOldJobs) { + jobsChecker = new JobsChecker(client, gatewayAddress); + } // Initialize local sign data await resetSignData(); @@ -209,22 +217,29 @@ if (import.meta.main) { const didSubmit = await submitter.handlePublishedBeacon(beacon); - const processJobs = (rounds: number[]): void => { - if (!rounds.length) return; - const past = rounds.filter((r) => r <= n); - const future = rounds.filter((r) => r > n); - console.log( - `Past (${past.length}): %o, Future (${future.length}): %o`, - past, - future, - ); - submitter.handlePastRoundsWithJobs(past); - }; - - // Check jobs every 1.5s, shifted 1200ms from the drand receiving - const shift = 1200; - setTimeout(() => jobs.check().then(processJobs, (err) => console.error(err)), shift); - setTimeout(() => jobs.check().then(processJobs, (err) => console.error(err)), shift + 1500); + if (cleanupOldJobs) { + const processJobs = (rounds: number[]): void => { + if (!rounds.length) return; + const past = rounds.filter((r) => r <= n); + const future = rounds.filter((r) => r > n); + console.log( + `Past (${past.length}): %o, Future (${future.length}): %o`, + past, + future, + ); + submitter.handlePastRoundsWithJobs(past); + }; + + const check = () => { + assert(jobsChecker); + jobsChecker.check().then(processJobs, (err) => console.error(err)); + }; + + // Check jobs every 1.5s, shifted 1200ms from the drand receiving + const shift = 1200; + setTimeout(check, shift); + setTimeout(check, shift + 1500); + } if (didSubmit) { // Some seconds after the submission when things are idle, check and log From fb2b1c498598b1b2b9ba862907e0f6ac47cab313 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 12:30:00 +0200 Subject: [PATCH 5/6] Reduce query limit and reverse order --- jobs.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jobs.ts b/jobs.ts index 110a912..2b4388a 100644 --- a/jobs.ts +++ b/jobs.ts @@ -42,7 +42,11 @@ export class JobsChecker { * Checks gateway for pending jobs and returns the rounds of those jobs as a list */ public async check(): Promise { - const query = { jobs_desc: { offset: null, limit: 50 } }; + const queryLimit = 4; + + // Use jobs_asc because with jobs_desc all entries in the result might be in the (far) future, + // leading to cases where the unprocesses jobs in the past are not processed anymore. + const query = { jobs_asc: { offset: null, limit: queryLimit } }; const { jobs }: JobsResponse = await this.noisClient.queryContractSmart(this.gateway, query); if (jobs.length === 0) return []; // Nothing to do for us @@ -51,7 +55,7 @@ export class JobsChecker { const due = timeOfRound(round) - Date.now(); return `#${round} (due ${formatDuration(due)})`; }); - console.log(`Jobs pending for rounds: %c${roundInfos.join(", ")}`, "color: orange"); + console.log(`Top ${queryLimit} pending jobs: %c${roundInfos.join(", ")}`, "color: orange"); return rounds; } } From 4586dc927fcd6e7b2b6778adcd772719a3009ba5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sun, 14 Apr 2024 12:36:30 +0200 Subject: [PATCH 6/6] Improve doc comment for cleanupOldJobs --- main.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/main.ts b/main.ts index e549715..21db27d 100644 --- a/main.ts +++ b/main.ts @@ -59,9 +59,13 @@ function getNextSignData(): SignerData { return out; } -// If this is set to false, the bot will only observe drand and check for each -// round **once** if it was incentivised. Everything that is markt as incentivised -// too late or is unprocessed for whatever reason will not be submitted. +/** + * If this is set to false, the bot will only watch the drand chain and check for + * each round **once** if it is incentivised. Everything that is marked as incentivised + * too late or is still unprocessed for whatever reason will not be submitted. + * + * It's not recommended to change this value for anything else than development. + */ const cleanupOldJobs = true; if (import.meta.main) {