diff --git a/hasura/metadata/databases/default/tables/public_validator.yaml b/hasura/metadata/databases/default/tables/public_validator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..358f72acf3abba6c0de63dae041363b023f24620 --- /dev/null +++ b/hasura/metadata/databases/default/tables/public_validator.yaml @@ -0,0 +1,11 @@ +table: + name: validator + schema: public +object_relationships: [] +array_relationships: [] +select_permissions: + - role: public + permission: + columns: '*' + filter: {} + allow_aggregations: true diff --git a/hasura/metadata/databases/default/tables/tables.yaml b/hasura/metadata/databases/default/tables/tables.yaml index da44ca49c263bf2ef827a16e80aed68baac9021b..82ca3f89e117c136072107f056f71eed56aa90e5 100644 --- a/hasura/metadata/databases/default/tables/tables.yaml +++ b/hasura/metadata/databases/default/tables/tables.yaml @@ -6,6 +6,7 @@ - "!include public_enum_item_type.yaml" - "!include public_enum_counter_level.yaml" - "!include public_account.yaml" +- "!include public_validator.yaml" - "!include public_transfer.yaml" - "!include public_smith.yaml" - "!include public_enum_smith_status.yaml" diff --git a/schema.graphql b/schema.graphql index c659788892064d97a2e0622b89faf16b26d747a0..492415dcda1bf82dc6a9359e21a39fb682169db9 100644 --- a/schema.graphql +++ b/schema.graphql @@ -127,6 +127,13 @@ type Account @entity { isActive: Boolean! } +type Validator @entity { + "Account address is SS58 format" + id: ID! + "Identity index" + index: Int! @index +} + type Transfer @entity { blockNumber: Int! @index timestamp: DateTime! @index @@ -154,6 +161,8 @@ type Smith @entity { forged: Int! "Last forged block" lastForged: Int + "Past associated validatorId" + validatorsId: [String] @derivedFrom(field: "id") } # === this part of the schema is for Duniter pallets === diff --git a/src/data_handler.ts b/src/data_handler.ts index 0f95a5fd8975ededff675dbb05a998652b42e22d..0015707c466690395b961bd4ef8eb2ba24b67bd2 100644 --- a/src/data_handler.ts +++ b/src/data_handler.ts @@ -19,6 +19,7 @@ import { Transfer, UdReeval, UniversalDividend, + Validator, } from "./model"; import { Address, BlockNumber, Ctx, Data, IdtyIndex, NewData } from "./types_custom"; import { hexToString } from "./utils"; @@ -31,6 +32,7 @@ export class DataHandler { accounts: new Map(), identities: new Map(), smiths: new Map(), + validators: new Map(), membershipEvents: [], smithEvents: [], changeOwnerKey: [], @@ -183,6 +185,13 @@ export class DataHandler { idty.lastChangeOn = idtyChange.blockNumber; idty.expireOn = idtyChange.expireOn; this.data.identities.set(idtyChange.index, idty); + + const smith = await this.getSmithByIndexOrFail(ctx, idtyChange.index); + if (smith.smithStatus == SmithStatus.Smith) { + const validator = new Validator({ id: idtyChange.accountId, index: idtyChange.index }) + this.data.validators.set(idtyChange.accountId, validator); + } + this.data.smiths.set(idtyChange.index, smith); } // Process membership added @@ -334,13 +343,12 @@ export class DataHandler { } // Process validators - for (const { block, validator } of newData.validators) { - const account = await this.getAccountByAddressOrFail(ctx, validator); - const identity = await this.getIdtyByAccountOrFail(ctx, account); - const smith = await this.getSmithByIndexOrFail(ctx, identity.index); + for (const { block, validatorId } of newData.validators) { + const validator = await this.getValidatorByAddressOrFail(ctx, validatorId); + const smith = await this.getSmithByIndexOrFail(ctx, validator.index); smith.forged += 1; smith.lastForged = block - this.data.smiths.set(identity.index, smith); + this.data.smiths.set(smith.index, smith); } // Process certifications removals @@ -423,12 +431,12 @@ export class DataHandler { const { idtyIndex, event } = acceptedSmithInvitations; const smith = await this.getSmithByIndexOrFail(ctx, idtyIndex); - smith.smithStatus = SmithStatus.Smith; + smith.smithStatus = SmithStatus.Pending; smith.lastChanged = event.block.height; this.data.smiths.set(idtyIndex, smith); this.data.smithEvents.push( new SmithEvent({ - id: `smith-invited-${idtyIndex}-${event.id}`, + id: `smith-accepted-${idtyIndex}-${event.id}`, smith, eventType: SmithEventType.Accepted, event: await ctx.store.getOrFail(Event, event.id), @@ -459,6 +467,10 @@ export class DataHandler { blockNumber: event.block.height, }) ); + + const identity = await this.getIdtyWithAccountByIndexOrFail(ctx, idtyIndex); + const validator = new Validator({ id: identity.account.id, index: idtyIndex }) + this.data.validators.set(identity.account?.id, validator); } // Process Smith exlusion @@ -604,6 +616,7 @@ export class DataHandler { // identities can have been changed (confirmed, change owner key...) or added (created) await ctx.store.upsert([...this.data.smiths.values()]); await ctx.store.upsert([...this.data.identities.values()]); + await ctx.store.upsert([...this.data.validators.values()]); // membership can have been created, renewed, or removed await ctx.store.upsert([...this.data.membershipEvents.values()]); await ctx.store.upsert([...this.data.smithEvents.values()]); @@ -687,10 +700,10 @@ export class DataHandler { })) } - async getIdtyByAccountOrFail(ctx: Ctx, account: Account): Promise<Identity> { + async getValidatorByAddressOrFail(ctx: Ctx, address: Address): Promise<Validator> { return ( - account.linkedIdentity ?? - ctx.store.findOneByOrFail(Identity, { linkedAccount: account }) + this.data.validators.get(address) ?? + ctx.store.findOneByOrFail(Validator, { id: address }) ); } diff --git a/src/genesis/genesis.ts b/src/genesis/genesis.ts index c836575bed22bdb7725f48d0983431193e32c986..c6f8b7365b97d3b5b6ca077f3a3a99977ed5367d 100644 --- a/src/genesis/genesis.ts +++ b/src/genesis/genesis.ts @@ -1,6 +1,6 @@ import { readFileSync } from "fs"; import path from "path/posix"; -import { Account, Block, Cert, CertEvent, Event, EventType, Smith, PopulationHistory, Identity, MembershipEvent, SmithEvent, SmithEventType, SmithCert, SmithStatus, Transfer } from "../model"; +import { Account, Block, Cert, CertEvent, Event, EventType, Smith, PopulationHistory, Identity, MembershipEvent, SmithEvent, SmithEventType, SmithCert, SmithStatus, Transfer, Validator } from "../model"; import type { Address, BlockV1, Certv1, Ctx, Genesis, Genv1, IdtyIndex, TransactionHistory } from "../types_custom"; import { bytesToString, hexToUint8Array, safePubkeyToAddress, v1_to_v2_height } from "../utils"; import { AccountId32 } from "../types/v800"; @@ -93,6 +93,7 @@ export async function saveGenesis(ctx: Ctx, block: Block) { const accounts: Map<Address, Account> = new Map(); const identities: Map<IdtyIndex, Identity> = new Map(); + const validators: Map<Address, Validator> = new Map(); const smiths: Map<IdtyIndex, Smith> = new Map(); const identitiesMap: Map<AccountId32, IdtyIndex> = new Map(); const certs: Map<string, Cert> = new Map(); @@ -305,6 +306,9 @@ export async function saveGenesis(ctx: Ctx, block: Block) { smiths.set(identity.index, smith); populationHistory.smithCount += 1; + const validator = new Validator({ id: identity.account.id, index: parseInt(idtyIdex) }) + validators.set(validator.id, validator); + smithsEvents.push( new SmithEvent({ id: `genesis-smith_${idtyIdex}`, @@ -329,6 +333,7 @@ export async function saveGenesis(ctx: Ctx, block: Block) { await ctx.store.insert([...smiths.values()]); await ctx.store.insert(smithsEvents); await ctx.store.insert(smithCerts); + await ctx.store.insert([...validators.values()]); ctx.log.info("Genesis saved"); diff --git a/src/main.ts b/src/main.ts index 95d79eeec9a49c985cac6a92655c28b2d67041b5..e3d08f2489238bb0b1f690279851365234419051 100644 --- a/src/main.ts +++ b/src/main.ts @@ -99,7 +99,7 @@ function collectDataFromEvents(ctx: Ctx, newData: NewData) { const validator = block.header.validator; if (validator != null) { const blockNumber = block.header.height; - newData.validators.push({ block: blockNumber, validator: ss58encode(validator) }); + newData.validators.push({ block: blockNumber, validatorId: ss58encode(validator) }); } block.events.forEach((event) => { diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index bf13484a18a2d393bf5bcb13b91a2e45bbfcff4c..abf212bebe0c078b7d82837e07cb594762a39efc 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -7,6 +7,7 @@ export * from "./itemsCounter.model" export * from "./_itemType" export * from "./_counterLevel" export * from "./account.model" +export * from "./validator.model" export * from "./transfer.model" export * from "./smith.model" export * from "./_smithStatus" diff --git a/src/model/generated/smith.model.ts b/src/model/generated/smith.model.ts index 6c41231affeb2662c806e55afbff06c729d0d0cc..e551c903d5e15b937f785217832e12093fad863a 100644 --- a/src/model/generated/smith.model.ts +++ b/src/model/generated/smith.model.ts @@ -66,4 +66,10 @@ export class Smith { */ @Column_("int4", {nullable: true}) lastForged!: number | undefined | null + + /** + * Past associated validatorId + */ + @Column_("text", {array: true, nullable: true}) + validatorsId!: (string | undefined | null)[] | undefined | null } diff --git a/src/model/generated/validator.model.ts b/src/model/generated/validator.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f3673d41da244de336906db199bce5b6dde1f36 --- /dev/null +++ b/src/model/generated/validator.model.ts @@ -0,0 +1,21 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" + +@Entity_() +export class Validator { + constructor(props?: Partial<Validator>) { + Object.assign(this, props) + } + + /** + * Account address is SS58 format + */ + @PrimaryColumn_() + id!: string + + /** + * Identity index + */ + @Index_() + @Column_("int4", {nullable: false}) + index!: number +} diff --git a/src/types_custom.ts b/src/types_custom.ts index aac44a2dbee256546e7a0e2f49899b018cfb2e5b..95320625ef46bcea91c9e26b600e5630efb32833 100644 --- a/src/types_custom.ts +++ b/src/types_custom.ts @@ -14,6 +14,7 @@ import { Transfer, UdReeval, UniversalDividend, + Validator, } from "./model"; import { Event, ProcessorContext } from "./processor"; import { AccountId32, MembershipRemovalReason, RemovalReason, RevocationReason } from "./types/v800"; @@ -154,6 +155,7 @@ export interface Certv1 { export interface Data { accounts: Map<Address, Account>; identities: Map<IdtyIndex, Identity>; + validators: Map<Address, Validator>; smiths: Map<IdtyIndex, Smith>; membershipEvents: MembershipEvent[]; smithEvents: SmithEvent[]; @@ -259,7 +261,7 @@ interface AccountEvent { interface BlockValidator { block: BlockNumber; - validator: Address; + validatorId: Address; } interface SmithPromotedEvent {