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

Add prediction endpoints #46

Merged
merged 2 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
69 changes: 54 additions & 15 deletions api/src/db/blocksProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { AkashBlock as Block, AkashMessage as Message } from "@shared/dbSchemas/akash";
import { Transaction, Validator } from "@shared/dbSchemas/base";
import { averageBlockTime } from "@src/utils/constants";
import { add } from "date-fns";
import { addSeconds, differenceInSeconds } from "date-fns";

export async function getBlocks(limit: number) {
const _limit = Math.min(limit, 100);
Expand Down Expand Up @@ -32,14 +31,6 @@ export async function getBlocks(limit: number) {
}

export async function getBlock(height: number) {
// const latestBlock = await Block.findOne({
// order: [["height", "DESC"]]
// });

// if (height > latestBlock.height) {
// return getFutureBlockEstimate(height, latestBlock);
// }

const block = await Block.findOne({
where: {
height: height
Expand Down Expand Up @@ -87,9 +78,57 @@ export async function getBlock(height: number) {
};
}

async function getFutureBlockEstimate(height: number, latestBlock: Block) {
return {
height: height,
expectedDate: add(latestBlock.datetime, { seconds: (height - latestBlock.height) * averageBlockTime })
};
/**
* Calculate the estimated block time
* @param latestBlock Block to calculate the average from
* @param blockCount Block interval for calculating the average
* @returns Average block time in seconds
*/
async function calculateAverageBlockTime(latestBlock: Block, blockCount: number) {
if (blockCount <= 1) throw new Error("blockCount must be greater than 1");

const earlierBlock = await Block.findOne({
where: {
height: Math.max(latestBlock.height - blockCount, 1)
}
});

const realBlockCount = latestBlock.height - earlierBlock.height;

return differenceInSeconds(latestBlock.datetime, earlierBlock.datetime) / realBlockCount;
}

/**
* Get the predicted height at a given date
* @param date Date to predict the height of
* @param blockWindow Block interval for calculating the average
* @returns Predicted height at the given date
*/
export async function getPredictedDateHeight(date: Date, blockWindow: number) {
const latestBlock = await Block.findOne({ order: [["height", "DESC"]] });

if (date <= latestBlock.datetime) throw new Error("Date must be in the future");

const averageBlockTime = await calculateAverageBlockTime(latestBlock, blockWindow);

const dateDiff = differenceInSeconds(date, latestBlock.datetime);

return Math.floor(latestBlock.height + dateDiff / averageBlockTime);
}

/**
* Get the predicted date at a given height
* @param height Height to predict the date of
* @param blockWindow Block window for calculating the average
* @returns Predicted date at the given height
*/
export async function getPredictedBlockDate(height: number, blockWindow: number) {
const latestBlock = await Block.findOne({ order: [["height", "DESC"]] });

if (height <= latestBlock.height) throw new Error("Height must be in the future");

const averageBlockTime = await calculateAverageBlockTime(latestBlock, blockWindow);

const heightDiff = height - latestBlock.height;
return addSeconds(latestBlock.datetime, heightDiff * averageBlockTime);
}
57 changes: 55 additions & 2 deletions api/src/routers/apiRouter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import express from "express";
import { getBlock, getBlocks } from "@src/db/blocksProvider";
import { getBlock, getBlocks, getPredictedBlockDate, getPredictedDateHeight } from "@src/db/blocksProvider";
import { getTemplateGallery } from "@src/providers/templateReposProvider";
import { getTransaction, getTransactionByAddress, getTransactions } from "@src/db/transactionsProvider";
import {
Expand Down Expand Up @@ -64,6 +64,59 @@ apiRouter.get(
})
);

apiRouter.get(
"/predicted-block-date/:height/:blockWindow?",
asyncHandler(async (req, res) => {
const height = parseInt(req.params.height);
const blockWindow = req.params.blockWindow ? parseInt(req.params.blockWindow) : 10_000;

if (isNaN(height)) {
res.status(400).send("Invalid height.");
return;
}

if (isNaN(blockWindow)) {
res.status(400).send("Invalid block window.");
return;
}

const date = await getPredictedBlockDate(height, blockWindow);

res.send({
height: height,
date: date,
blockWindow: blockWindow
});
})
);

apiRouter.get(
"/predicted-date-height/:timestamp/:blockWindow?",
asyncHandler(async (req, res) => {
const timestamp = parseInt(req.params.timestamp);
const blockWindow = req.params.height ? parseInt(req.params.blockWindow) : 10_000;

if (isNaN(timestamp)) {
res.status(400).send("Invalid timestamp.");
return;
}

if (isNaN(blockWindow)) {
res.status(400).send("Invalid block window.");
return;
}

const date = new Date(timestamp * 1000);
const height = await getPredictedDateHeight(date, blockWindow);

res.send({
height: height,
date: date,
blockWindow: blockWindow
});
})
);

apiRouter.get(
"/transactions",
asyncHandler(async (req, res) => {
Expand Down Expand Up @@ -264,7 +317,7 @@ apiRouter.get(
const chainStats = {
height: latestBlocks[0].height,
transactionCount: latestBlocks[0].totalTransactionCount,
...(await chainStatsQuery)
...chainStatsQuery
};

res.send({
Expand Down