diff --git a/drand.ts b/drand.ts index 3472e41..03a0c53 100644 --- a/drand.ts +++ b/drand.ts @@ -35,3 +35,10 @@ export function timeOfRound(round: number): number { export function publishedSince(round: number): number { return Date.now() - timeOfRound(round); } + +/** + * Time between now and publishing in milliseconds + */ +export function publishedIn(round: number): number { + return -publishedSince(round); +} diff --git a/drand_contract.ts b/drand_contract.ts index 93a9deb..4bbc02b 100644 --- a/drand_contract.ts +++ b/drand_contract.ts @@ -43,6 +43,7 @@ export async function queryIsIncentivized( const { incentivized } = await client.queryContractSmart(contractAddress, { is_incentivized: { rounds, sender: botAddress }, }); + // console.log(`#${rounds[0]} incentivized query returned at ${publishedSince(rounds[0])}ms`) assert(Array.isArray(incentivized)); return incentivized; } diff --git a/loop.ts b/loop.ts index b0a965f..45e129c 100644 --- a/loop.ts +++ b/loop.ts @@ -10,7 +10,7 @@ import { SignerData, SigningCosmWasmClient, } from "./deps.ts"; -import { makeAddBeaconMessage, queryIsIncentivized } from "./drand_contract.ts"; +import { makeAddBeaconMessage } from "./drand_contract.ts"; import { ibcPacketsSent } from "./ibc.ts"; interface Capture { @@ -23,6 +23,7 @@ interface Capture { gasPrice: string; userAgent: string; getNextSignData: () => SignerData; + incentivizedRounds: Map>; } export async function loop( @@ -36,29 +37,32 @@ export async function loop( gasPrice, userAgent, getNextSignData, + incentivizedRounds, }: Capture, beacon: RandomnessBeacon, ): Promise { console.log(`➘ #${beacon.round} received after ${publishedSince(beacon.round)}ms`); - const isIncentivized = await queryIsIncentivized( - client, - drandAddress, - [beacon.round], - botAddress, - ); - + // We don't have evidence that this round is incentivized. This is no guarantee it did not + // get incentivized in the meantime, but we prefer to skip than risk the gas. + const isIncentivized = await incentivizedRounds.get(beacon.round); if (!isIncentivized) { console.log(`Skipping.`); return false; } + // Use this log to ensure awaiting the isIncentivized query does not slow us down. + console.log(`♪ #${beacon.round} ready for signing after ${publishedSince(beacon.round)}ms`); + const broadcastTime = Date.now() / 1000; const msg = makeAddBeaconMessage(botAddress, drandAddress, beacon); const fee = calculateFee(gasLimitAddBeacon, gasPrice); const memo = `Add round: ${beacon.round} (${userAgent})`; const signData = getNextSignData(); // Do this the manual way to save one query const signed = await client.sign(botAddress, [msg], fee, memo, signData); + + // console.log(`♫ #${beacon.round} signed after ${publishedSince(beacon.round)}ms`); + const tx = Uint8Array.from(TxRaw.encode(signed).finish()); const p1 = client.broadcastTx(tx); diff --git a/main.ts b/main.ts index 396f70f..119154d 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,4 @@ -import { drandOptions, drandUrls } from "./drand.ts"; +import { drandOptions, drandUrls, publishedIn } from "./drand.ts"; import { group } from "./group.ts"; import { assert, @@ -16,7 +16,7 @@ import { } from "./deps.ts"; import { BeaconCache } from "./cache.ts"; import { loop } from "./loop.ts"; -import { queryIsAllowListed } from "./drand_contract.ts"; +import { queryIsAllowListed, queryIsIncentivized } from "./drand_contract.ts"; // Constants const gasLimitRegister = 200_000; @@ -123,6 +123,8 @@ if (import.meta.main) { // Initialize local sign data await resetSignData(); + const incentivizedRounds = new Map>(); + const fastestNodeClient = new FastestNodeClient(drandUrls, drandOptions); fastestNodeClient.start(); const cache = new BeaconCache(fastestNodeClient, 200 /* 10 min of beacons */); @@ -130,6 +132,20 @@ if (import.meta.main) { for await (const beacon of watch(fastestNodeClient, abortController)) { cache.add(beacon.round, beacon.signature); + setTimeout(() => { + // This is called 100ms after publishing time (might be some ms later) + // From here we have ~300ms until the beacon comes in which should be + // enough for the query to finish. In case the query is not yet done, + // we can wait for the promise to be resolved. + // console.log(`Now : ${new Date().toISOString()}\nPublish time: ${new Date(timeOfRound(round)).toISOString()}`); + const round = beacon.round + 1; + const promise = queryIsIncentivized(client, config.contract, [round], botAddress).then( + (incentivized) => !!incentivized[0], + (_err) => false, + ); + incentivizedRounds.set(round, promise); + }, publishedIn(beacon.round + 1) + 100); + const didSubmit = await loop({ client, broadcaster2, @@ -140,6 +156,7 @@ if (import.meta.main) { botAddress, drandAddress: config.contract, userAgent, + incentivizedRounds, }, beacon); if (didSubmit) {