From e661667dcb4610c7057b9b59542408b7ecabf6e2 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Tue, 5 Dec 2023 13:10:10 +0100
Subject: [PATCH] fix identity creation on non existing account
ctx type alias
fix account creation bug
rename prepared data
add never fail comments
---
README-2.md | 19 +++++++++++++-
src/main.ts | 75 ++++++++++++++++++++++++++++++++++-------------------
2 files changed, 67 insertions(+), 27 deletions(-)
diff --git a/README-2.md b/README-2.md
index 97fea1f..fbf3c87 100644
--- a/README-2.md
+++ b/README-2.md
@@ -1,4 +1,10 @@
-# Processus de mise-à-jour
+## Installation d'outils
+
+```sh
+pnpm install --global @subsquid/cli@latest
+```
+
+## Processus de mise-à-jour du runtime
Pour mettre à jour les métadonnées du runtime
@@ -7,9 +13,20 @@ pnpm install --global @subsquid/substrate-metadata-explorer
squid-substrate-metadata-explorer --rpc ws://127.0.0.1:9944 --out gdev-metadata.jsonl
```
+## Processus de mise-à-jour du réseau
+
Pour mettre à jour les assets (nouveau réseau)
```sh
# copier le lien depuis https://git.duniter.org/nodes/rust/duniter-v2s/-/releases/
wget https://nodes.pages.duniter.org/-/rust/duniter-v2s/-/jobs/120948/artifacts/release/gdev.json -O ./assets/gdev.json
+```
+
+## Publication d'image docker
+
+Ce que je fais pour publier l'image
+
+```sh
+docker build . -t h30x/duniter-squid
+docker image push h30x/duniter-squid
```
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index b5c5add..f40fd0b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -50,7 +50,7 @@ processor.run(new TypeormDatabaseWithCache(), async (ctx) => {
getNewData(ctx, ndata);
// transform new data to objects
- let prepared: PreparedData = {
+ let data: Data = {
accounts: new Map(),
identities: new Map(),
changeOwnerKey: [],
@@ -60,10 +60,10 @@ processor.run(new TypeormDatabaseWithCache(), async (ctx) => {
certRenewal: [],
certRemoval: [],
};
- await prepareData(ctx, ndata, prepared);
+ await prepareData(ctx, ndata, data);
// store data
- await storeData(ctx, prepared);
+ await storeData(ctx, data);
});
// =====================================================================================================
@@ -72,6 +72,7 @@ processor.run(new TypeormDatabaseWithCache(), async (ctx) => {
export type Address = string;
export type IdtyIndex = number;
type BlockNumber = number;
+type Ctx = ProcessorContext<StoreWithCache>;
// a way to group data returned from events
// this contains partial data to be turned into types
@@ -117,7 +118,7 @@ interface CertRemovalEvent {
interface IdtyCreatedEvent {
id: string;
index: IdtyIndex;
- account: Address;
+ accountId: Address;
}
interface IdtyConfirmedEvent {
id: string;
@@ -127,12 +128,12 @@ interface IdtyConfirmedEvent {
interface IdtyChangedOwnerKeyEvent {
id: string;
index: IdtyIndex;
- account: Address;
+ accountId: Address;
blockNumber: BlockNumber;
}
// a way to group data prepared for database insertion
-interface PreparedData {
+interface Data {
accounts: Map<Address, Account>;
identities: Map<IdtyIndex, Identity>;
changeOwnerKey: ChangeOwnerKey[];
@@ -146,7 +147,7 @@ interface PreparedData {
// ================================================================================
/// fill data with data collected from events
-function getNewData(ctx: ProcessorContext<StoreWithCache>, ndata: NewData) {
+function getNewData(ctx: Ctx, ndata: NewData) {
const silence_events = [
events_t.system.extrinsicSuccess.name,
events_t.system.killedAccount.name,
@@ -213,7 +214,7 @@ function getNewData(ctx: ProcessorContext<StoreWithCache>, ndata: NewData) {
ndata.identitiesCreated.push({
id: event.id,
index: newI.idtyIndex,
- account: ss58.codec(42).encode(newI.ownerKey),
+ accountId: ss58.codec(42).encode(newI.ownerKey),
});
break;
@@ -243,7 +244,7 @@ function getNewData(ctx: ProcessorContext<StoreWithCache>, ndata: NewData) {
ndata.idtyChangedOwnerKey.push({
id: event.id,
index: chok.idtyIndex,
- account: ss58.codec(42).encode(chok.newOwnerKey),
+ accountId: ss58.codec(42).encode(chok.newOwnerKey),
blockNumber: block.header.height,
});
break;
@@ -315,7 +316,7 @@ function getNewData(ctx: ProcessorContext<StoreWithCache>, ndata: NewData) {
// =============================================================================================================
/// prepare data for injection into database
-async function prepareData(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function prepareData(ctx: Ctx, newData: NewData, data: Data) {
await createAccounts(ctx, newData, data);
await createIdentities(ctx, newData, data);
await confirmIdentities(ctx, newData, data);
@@ -329,7 +330,7 @@ async function prepareData(ctx: ProcessorContext<StoreWithCache>, newData: NewDa
// =============================================
/// store prepared data into database
-async function storeData(ctx: ProcessorContext<StoreWithCache>, data: PreparedData) {
+async function storeData(ctx: Ctx, data: Data) {
// UPSERT = update or insert if not existing
// account can have already existed, been killed, and recreated
await ctx.store.upsert([...data.accounts.values()]);
@@ -348,7 +349,7 @@ async function storeData(ctx: ProcessorContext<StoreWithCache>, data: PreparedDa
// =============================================================================================================
-async function createAccounts(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function createAccounts(ctx: Ctx, newData: NewData, data: Data) {
// system will tell when accounts are created (but this should be added above)
for (let id of newData.accounts) {
let newA = new Account({ id });
@@ -357,9 +358,20 @@ async function createAccounts(ctx: ProcessorContext<StoreWithCache>, newData: Ne
}
}
-async function createIdentities(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function getOrCreateAccount(ctx: Ctx, data: Data, id: Address): Promise<Account> {
+ let account = data.accounts.get(id) ?? await ctx.store.get(Account, id);
+ if (account == null) {
+ // we need to create it
+ account = new Account({id});
+ data.accounts.set(id, account);
+ }
+ return account;
+}
+
+async function createIdentities(ctx: Ctx, newData: NewData, data: Data) {
for (let i of newData.identitiesCreated) {
- let account = await ctx.store.getOrFail(Account, i.account);
+ // an identity can be created for a non existing account without System.NewAccount event
+ let account = await getOrCreateAccount(ctx, data, i.accountId);
let newI = new Identity({
id: i.id,
index: i.index,
@@ -370,19 +382,22 @@ async function createIdentities(ctx: ProcessorContext<StoreWithCache>, newData:
}
}
-async function confirmIdentities(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function confirmIdentities(ctx: Ctx, newData: NewData, data: Data) {
for (let i of newData.identitiesConfirmed) {
- let idty = await ctx.store.findOneByOrFail(Identity, { index: i.index });
+ // should never fail because identities can not be confirmed without have been created
+ let idty = data.identities.get(i.index) ?? await ctx.store.findOneByOrFail(Identity, { index: i.index });
idty.name = i.name;
data.identities.set(i.index, idty);
}
}
-async function changeIdtyOwnerKey(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function changeIdtyOwnerKey(ctx: Ctx, newData: NewData, data: Data) {
for (let icok of newData.idtyChangedOwnerKey) {
+ // should never fail because change owner key is only available for existing identites
let idty = await ctx.store.findOneOrFail(Identity, { where: { index: icok.index }, relations: { account: true } });
+ // should never fail because old_account must pay fees to call change owner key
let old_account = data.accounts.get(idty.account.id) ?? (await ctx.store.getOrFail(Account, idty.account.id));
- let new_account = data.accounts.get(icok.account) ?? (await ctx.store.getOrFail(Account, icok.account));
+ let new_account = await getOrCreateAccount(ctx, data, icok.accountId);
ctx.log.info(`changed owner key of idty ${idty.index} from ${old_account.id} to ${new_account.id}`);
// actually change the account of the identity
idty.account = new_account;
@@ -400,10 +415,12 @@ async function changeIdtyOwnerKey(ctx: ProcessorContext<StoreWithCache>, newData
}
}
-async function createTransfers(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function createTransfers(ctx: Ctx, newData: NewData, data: Data) {
for (let t of newData.transfers) {
let { id, blockNumber, timestamp, amount } = t;
+ // should never fail because source of transfer must be an existing account
let from = data.accounts.get(t.from) ?? (await ctx.store.getOrFail(Account, t.from));
+ // shoud never fail because destination of transfer must be existing account or raise System.NewAccount
let to = data.accounts.get(t.to) ?? (await ctx.store.getOrFail(Account, t.to));
data.transfers.push(
new Transfer({
@@ -418,15 +435,16 @@ async function createTransfers(ctx: ProcessorContext<StoreWithCache>, newData: N
}
}
-async function createCerts(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function createCerts(ctx: Ctx, newData: NewData, data: Data) {
for (let c of newData.certCreation) {
let { id, issuerId, receiverId, createdOn, expireOn } = c;
let cert = await ctx.store.findOne(Cert, {
relations: { issuer: true, receiver: true },
where: { issuer: { index: issuerId }, receiver: { index: receiverId } },
});
+ // first creation of the cert
if (cert == null) {
- // this is the first creation of the cert
+ // should never fail because cert issuer and receiver must be existing or created identities
let issuer = data.identities.get(issuerId) ?? (await ctx.store.findOneByOrFail(Identity, { index: issuerId }));
let receiver = data.identities.get(receiverId) ?? (await ctx.store.findOneByOrFail(Identity, { index: receiverId }));
cert = new Cert({
@@ -437,9 +455,10 @@ async function createCerts(ctx: ProcessorContext<StoreWithCache>, newData: NewDa
createdOn,
expireOn,
});
- } else {
- // the cert has already existed, expired, and is created again
- // we update it
+ }
+ // the cert has already existed, expired, and is created again
+ // we update it accordingly
+ else {
cert.active = true;
cert.createdOn = createdOn;
cert.expireOn = expireOn;
@@ -456,9 +475,11 @@ async function createCerts(ctx: ProcessorContext<StoreWithCache>, newData: NewDa
}
}
-async function createCertRenewals(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function createCertRenewals(ctx: Ctx, newData: NewData, data: Data) {
for (let c of newData.certRenewal) {
let { id, issuerId, receiverId, blockNumber, expireOn } = c;
+ // should never fail because cert renewal can only happen on existing cert
+ // and can not be renewed at the same block as created (delay)
let cert = await ctx.store.findOneOrFail(Cert, {
relations: { issuer: true, receiver: true },
where: { issuer: { index: issuerId }, receiver: { index: receiverId } },
@@ -476,9 +497,11 @@ async function createCertRenewals(ctx: ProcessorContext<StoreWithCache>, newData
}
}
-async function createCertRemovals(ctx: ProcessorContext<StoreWithCache>, newData: NewData, data: PreparedData) {
+async function createCertRemovals(ctx: Ctx, newData: NewData, data: Data) {
for (let c of newData.certRemoval) {
let { id, issuerId, receiverId, blockNumber } = c;
+ // should never fail because cert removal can only happen on existing cert
+ // and cert should not be removed at their creation block
let cert = await ctx.store.findOneOrFail(Cert, {
relations: { issuer: true, receiver: true },
where: { issuer: { index: issuerId }, receiver: { index: receiverId } },
--
GitLab