Skip to content

Commit

Permalink
fixed active epoch binary array from db
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed May 24, 2024
1 parent 2eea828 commit 1b00744
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 57 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,12 @@ Read more about the process of computing the score in the [nimiq-vts](./packages
#### Database

As well as storing the [Validator Details](#validator-details), we also store the data produced by the fetcher in a sqlite database. This data is then used in the score calculator to compute the score. You can see the file [schema.ts](./server/database/schema.ts).

## Development

Once it is cloned and installed the dependencies, you must run:

```bash
pnpm db:generate
pnpm dev # or pnpm dev:remote to use the remote database
```
6 changes: 0 additions & 6 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ export default defineNuxtConfig({
database: true,
},

$development: {
hub: {
remote: true,
},
},

runtimeConfig: {
rpcUrl: '', // Set in .env
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"dev:remote": "nuxt dev --remote",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
Expand Down
3 changes: 3 additions & 0 deletions packages/nimiq-vts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,8 @@ export interface Range {
blockNumberToIndex(blockNumber: number): number,

blocksPerEpoch: number,

// The amount of epochs in the range
epochCount: number
}

59 changes: 27 additions & 32 deletions packages/nimiq-vts/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,31 @@ interface GetRangeOptions {
* Given the amount of milliseconds we want to consider, it returns an object with the epoch range we will consider.
*/
export async function getRange(client: Client, options?: GetRangeOptions): Promise<Range> {
const { data: policy, error: errorPolicy } = await client.policy.getPolicyConstants()
if (errorPolicy || !policy) throw new Error(errorPolicy.message || 'No policy constants')

const { blockSeparationTime, blocksPerEpoch, genesisBlockNumber } = policy as PolicyConstants & { blockSeparationTime: number, genesisBlockNumber: number }
const durationMs = options?.durationMs || 1000 * 60 * 60 * 24 * 30 * 9
const epochsCount = Math.ceil(durationMs / (blockSeparationTime * blocksPerEpoch))

let toEpochIndex = options?.toEpochIndex
if (!toEpochIndex) {
const { data: currentEpoch, error: errorCurrentEpoch } = await client.blockchain.getEpochNumber()
if (errorCurrentEpoch || !currentEpoch) throw new Error(errorCurrentEpoch?.message || 'No current epoch')
toEpochIndex = currentEpoch - 1
}
// Don't go back more than the block after the genesis block
const fromEpochIndex = Math.max(1, toEpochIndex - epochsCount)

// Convert indexes to election blocks
const fromEpoch = genesisBlockNumber + blocksPerEpoch * fromEpochIndex
const toEpoch = genesisBlockNumber + blocksPerEpoch * toEpochIndex

// Validate the epoch range
if (fromEpoch < 0 || toEpoch < 0 || fromEpoch > toEpoch) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]`)
if (fromEpoch === 0) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]. The range should start from epoch 1`)

const { data: head, error: headError } = await client.blockchain.getBlockNumber()
if (headError || !head) throw new Error(headError?.message || 'No block number')
if (toEpoch >= head) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]. The current head is ${head}`)

const blockNumberToIndex = (blockNumber: number) => Math.floor((blockNumber - genesisBlockNumber) / blocksPerEpoch) - fromEpochIndex

const range: Range = { fromEpoch, toEpoch, blocksPerEpoch, blockNumberToIndex }
return range
const { data: policy, error: errorPolicy } = await client.policy.getPolicyConstants();
if (errorPolicy || !policy) throw new Error(errorPolicy?.message || 'No policy constants');

const { blockSeparationTime, blocksPerEpoch, genesisBlockNumber } = policy as PolicyConstants & { blockSeparationTime: number, genesisBlockNumber: number };
const durationMs = options?.durationMs || 1000 * 60 * 60 * 24 * 30 * 9;
const epochsCount = Math.ceil(durationMs / (blockSeparationTime * blocksPerEpoch));

const { data: currentEpoch, error: errorCurrentEpoch } = await client.blockchain.getEpochNumber();
if (errorCurrentEpoch || !currentEpoch) throw new Error(errorCurrentEpoch?.message || 'No current epoch');

const toEpochIndex = options?.toEpochIndex ?? currentEpoch - 1;
const fromEpochIndex = Math.max(1, toEpochIndex - epochsCount);

const fromEpoch = genesisBlockNumber + blocksPerEpoch * fromEpochIndex;
const toEpoch = genesisBlockNumber + blocksPerEpoch * toEpochIndex;

if (fromEpoch < 0 || toEpoch < 0 || fromEpoch > toEpoch) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]`);
if (fromEpoch === 0) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]. The range should start from epoch 1`);

const { data: head, error: headError } = await client.blockchain.getBlockNumber();
if (headError || !head) throw new Error(headError?.message || 'No block number');
if (toEpoch >= head) throw new Error(`Invalid epoch range: [${fromEpoch}, ${toEpoch}]. The current head is ${head}`);

const blockNumberToIndex = (blockNumber: number) => Math.floor((blockNumber - genesisBlockNumber) / blocksPerEpoch) - fromEpochIndex;
const epochCount = toEpochIndex - fromEpochIndex;

return { fromEpoch, toEpoch, blocksPerEpoch, blockNumberToIndex, epochCount };
}
38 changes: 20 additions & 18 deletions server/database/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ export async function storeValidator(address: string, rest: Omit<NewValidator, '
* If there are missing validators or epochs, it will throw an error.
*/
export async function getValidatorParams(validators: { address: string, balance: number }[], range: Range) {
const addresses = validators.map(v => v.address)
const missingValidators = await getMissingValidators(addresses)
if (missingValidators.length > 0) throw new Error(`Missing validators in database: ${missingValidators.join(', ')}. Run the fetch task first.`)
const addresses = validators.map(v => v.address);

const missingEpochs = await getMissingEpochs(range)
if (missingEpochs.length > 0) throw new Error(`Missing epochs in database: ${missingEpochs.join(', ')}. Run the fetch task first.`)
const missingValidators = await getMissingValidators(addresses);
if (missingValidators.length > 0) throw new Error(`Missing validators in database: ${missingValidators.join(', ')}. Run the fetch task first.`);

const missingEpochs = await getMissingEpochs(range);
if (missingEpochs.length > 0) throw new Error(`Missing epochs in database: ${missingEpochs.join(', ')}. Run the fetch task first.`);

const activities = await useDrizzle()
.select({
Expand All @@ -68,22 +69,23 @@ export async function getValidatorParams(validators: { address: string, balance:
lte(tables.activity.epochBlockNumber, range.toEpoch),
inArray(tables.validators.address, addresses)
))
.execute()
.execute();

// Create an array of all epochs in the range
const allEpochs = Array.from({ length: range.toEpoch - range.fromEpoch + 1 }, (_, i) => range.fromEpoch + i)
const epochCount = Math.ceil((range.toEpoch - range.fromEpoch) / range.blocksPerEpoch);

// Reduce the activities to the desired format
const validatorParams: ValidatorParams = {}
for (const {address, balance} of validators) {
const validatorActivities = activities.filter(a => a.address === address)
const validatorId = validatorActivities[0].validatorId
const activeEpochStates = allEpochs.map(() => 0)
validatorActivities.forEach(activity => activeEpochStates[range.blockNumberToIndex(activity.epoch)] = 1)
validatorParams[address] = { validatorId, balance, activeEpochStates }
const validatorParams: ValidatorParams = {};
for (const { address, balance } of validators) {
const validatorActivities = activities.filter(a => a.address === address);
const validatorId = validatorActivities[0]!.validatorId;
const activeEpochStates = Array(epochCount).fill(0);
validatorActivities.forEach(activity => {
const index = range.blockNumberToIndex(activity.epoch);
if (index >= 0 && index < epochCount) activeEpochStates[index] = 1;
});
validatorParams[address] = { validatorId, balance, activeEpochStates };
}
return validatorParams


return validatorParams;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion server/tasks/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default defineTask({

consola.info(`Computing score for: ${Object.keys(activity).join(', ')}`)
const scores = Object.values(activity).map(({ activeEpochStates, validatorId, balance }) => {
const score = computeScore({ liveness: { activeEpochStates, ...range }, size: { balance, totalBalance } })
const score = computeScore({ liveness: { activeEpochStates }, size: { balance, totalBalance } })
return { validatorId, ...score } satisfies NewScore
})
await storeScores(scores)
Expand Down

0 comments on commit 1b00744

Please sign in to comment.