git.delta.rocks / unique-network / refs/commits / 751651bbbc76

difftreelog

Merge pull request #852 from UniqueNetwork/feat/improvements-to-identity-setter

Yaroslav Bolyukin2023-01-26parents: #acb95c7 #4b9f806.patch.diff
in: master

3 files changed

modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -32,6 +32,7 @@
     "fix": "eslint --ext .ts,.js src/ --fix",
     "setup": "ts-node ./src/util/globalSetup.ts",
     "setIdentities": "ts-node ./src/util/identitySetter.ts",
+    "checkRelayIdentities": "ts-node ./src/util/relayIdentitiesChecker.ts",
     "test": "yarn setup && mocha --timeout 9999999 -r ts-node/register './src/**/*.*test.ts'",
     "testParallelFull": "yarn testParallel && yarn testSequential",
     "testParallel": "yarn setup && mocha --parallel --timeout 9999999 -r ts-node/register './src/**/*.test.ts'",
modifiedtests/src/util/identitySetter.tsdiffbeforeafterboth
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
2// SPDX-License-Identifier: Apache-2.02// SPDX-License-Identifier: Apache-2.0
3//
4// Pulls identities and sub-identities from a chain and then uses sudo to force upload them into another.
5// Only changed or previously non-existent data are inserted.
3//6//
4// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [sudo key]`7// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [sudo key]`
5// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`8// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`
12const paraUrl = process.argv[3] ?? 'ws://localhost:9944';15const paraUrl = process.argv[3] ?? 'ws://localhost:9944';
13const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';16const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';
1417
15function extractAccountId(key: any): string {18export function extractAccountId(key: any): string {
16 return (key as any).toHuman()[0];19 return (key as any).toHuman()[0];
17}20}
21
22export function extractIdentityInfo(value: any): object {
23 const heart = (value as any).unwrap();
24 const identity = heart.toJSON();
25 identity.judgements = heart.judgements.toHuman();
26 return identity;
27}
1828
19function extractIdentity(key: any, value: any): [string, any] {29export function extractIdentity(key: any, value: any): [string, any] {
20 return [extractAccountId(key), (value as any).unwrap()];30 return [extractAccountId(key), extractIdentityInfo(value)];
21}31}
2232
23async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {33export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {
24 const identities: [string, any][] = [];34 const identities: [string, any][] = [];
25 for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {35 for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {
26 const value = v as any;36 const value = v as any;
33 return identities;43 return identities;
34}44}
3545
46// whether the existing chain data is more important than the coming
47function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {
48 if (!currentChainId) return false;
49 // information has come from local chain, and is automatically superior
50 if (currentChainId == helper.chain.getChainProperties().ss58Format) return true;
51 // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2)
52 return currentChainId < relayChainId;
53}
54
55// construct an object with all data necessary for insertion from storage query results
36function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {56export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {
37 const deposit = subQuery.toJSON()[0];57 const deposit = subQuery.toJSON()[0];
38 const subIdentities = subQuery.toHuman()[1];58 const subIdentities = subQuery.toHuman()[1];
39 subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));59 subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));
40 // supers.find((x: any) => x[0] === subIdentities[0])![1].toHuman();60
41 return [61 return [
42 encodeAddress(identityAccount, ss58), [62 encodeAddress(identityAccount, ss58), [
43 deposit,63 deposit,
44 subIdentities.map((sub: string) => [64 subIdentities.map((sub: string): [string, object] | null => {
45 encodeAddress(sub, ss58),
46 supers.find((sup: any) => sup[0] === sub)![1].toJSON()[1],65 const sup = supers.find((sup: any) => sup[0] === sub);
66 if (!sup) {
67 console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`);
68 return null;
69 }
70 return [encodeAddress(sub, ss58), sup[1].toJSON()[1]];
47 ]),71 }).filter((x: any) => x),
48 ],72 ],
49 ];73 ];
50}74}
5175
52async function getSubs(helper: ChainHelperBase) {76export async function getSubs(helper: ChainHelperBase) {
53 return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);77 return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
54}78}
5579
56async function getSupers(helper: ChainHelperBase) {80export async function getSupers(helper: ChainHelperBase) {
57 return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);81 return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
58}82}
5983
60// The utility for pulling identity and sub-identity data84// The utility for pulling identity and sub-identity data
61const forceInsertIdentities = async (): Promise<void> => {85const forceInsertIdentities = async (): Promise<void> => {
86 let relaySS58Prefix = 0;
62 const identitiesOnRelay: any[] = [];87 const identitiesOnRelay: any[] = [];
63 const subsOnRelay: any[] = [];88 const subsOnRelay: any[] = [];
64 const identitiesToRemove: string[] = [];89 const identitiesToRemove: string[] = [];
65 await usingPlaygrounds(async helper => {90 await usingPlaygrounds(async helper => {
66 try {91 try {
92 relaySS58Prefix = helper.chain.getChainProperties().ss58Format;
67 // iterate over every identity93 // iterate over every identity
68 for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {94 for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {
69 // if any of the judgements resulted in a good confirmed outcome, keep this identity95 // if any of the judgements resulted in a good confirmed outcome, keep this identity
96 let knownGood = false, reasonable = false;
70 if (value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;97 for (const [_id, judgement] of value.judgements) {
98 if (judgement == 'KnownGood') knownGood = true;
99 if (judgement == 'Reasonable') reasonable = true;
100 }
101 if (!(reasonable || knownGood)) continue;
102 // replace the registrator id with the relay chain's ss58 format
103 value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];
71 identitiesOnRelay.push([key, value]);104 identitiesOnRelay.push([key, value]);
72 }105 }
73106
100 const ss58Format = helper.chain.getChainProperties().ss58Format;133 const ss58Format = helper.chain.getChainProperties().ss58Format;
101 const paraIdentities = await getIdentities(helper);134 const paraIdentities = await getIdentities(helper);
102 const identitiesToAdd: any[] = [];135 const identitiesToAdd: any[] = [];
136 const paraAccountsRegistrators: {[name: string]: number} = {};
103137
104 // cross-reference every account for changes138 // cross-reference every account for changes
105 for (const [key, value] of identitiesOnRelay) {139 for (const [key, value] of identitiesOnRelay) {
106 const encodedKey = encodeAddress(key, ss58Format);140 const encodedKey = encodeAddress(key, ss58Format);
107141
142 // only update if the identity info does not exist or is changed
108 const identity = paraIdentities.find(i => i[0] === encodedKey);143 const identity = paraIdentities.find(i => i[0] === encodedKey);
109 if (identity) {144 if (identity) {
110 // only update if the identity info does not exist or is changed145 const registratorId = identity[1].judgements[0][0];
146 paraAccountsRegistrators[encodedKey] = registratorId;
111 if (JSON.stringify(value) === JSON.stringify(identity[1])) {147 if (isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])
148 || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {
112 continue;149 continue;
113 }150 }
114 }151 }
119 // identitiesToRemove.push((key as any).toHuman()[0]);157 // identitiesToRemove.push((key as any).toHuman()[0]);
120 }158 }
121
122 const paraSubs = await getSubs(helper);
123 const supersOfSubs = await getSupers(helper);
124 const subsToUpdate: any[] = [];
125159
126 if (identitiesToRemove.length != 0)160 if (identitiesToRemove.length != 0)
127 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);161 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);
132 + ` and found ${identitiesToRemove.length} identities for potential removal.`166 + ` and found ${identitiesToRemove.length} identities for potential removal.`
133 + ` Now there are ${(await helper.getApi().query.identity.identityOf.keys()).length}.`);167 + ` Now there are ${(await helper.getApi().query.identity.identityOf.keys()).length}.`);
168
169 // fill sub-identities
170 const paraSubs = await getSubs(helper);
171 const supersOfSubs = await getSupers(helper);
172 const subsToUpdate: any[] = [];
134173
135 for (const [key, value] of subsOnRelay) {174 for (const [key, value] of subsOnRelay) {
136 const encodedKey = encodeAddress(key, ss58Format);175 const encodedKey = encodeAddress(key, ss58Format);
137 const sub = paraSubs.find(i => i[0] === encodedKey);176 const sub = paraSubs.find(i => i[0] === encodedKey);
138 if (sub) {177 if (sub) {
139 // only update if the sub-identity info does not exist or is changed178 // only update if the sub-identity info does not exist or is changed
140 if (JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {179 if (isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)
180 || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {
141 continue;181 continue;
142 }182 }
143 }183 } else if (value[1].length == 0)
184 continue;
185
144 subsToUpdate.push([key, value]);186 subsToUpdate.push([key, value]);
145 }187 }
156 }, paraUrl);198 }, paraUrl);
157};199};
158200
201if (process.argv[1] === module.filename)
159forceInsertIdentities().catch(() => process.exit(1));202 forceInsertIdentities().catch(() => process.exit(1));
203
addedtests/src/util/relayIdentitiesChecker.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/util/relayIdentitiesChecker.ts
@@ -0,0 +1,114 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Checks and reports the differences between identities and sub-identities on two chains.
+//
+// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]`
+// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com`
+
+import {encodeAddress} from '@polkadot/keyring';
+import {usingPlaygrounds} from './index';
+import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter';
+
+const relay1Url = process.argv[2] ?? 'ws://localhost:9844';
+const relay2Url = process.argv[3] ?? 'ws://localhost:9844';
+
+async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> {
+  const identities: any[] = [];
+  const subs: any[] = [];
+
+  await usingPlaygrounds(async helper => {
+    try {
+      // iterate over every identity
+      for(const [key, value] of await getIdentities(helper)) {
+        // if any of the judgements resulted in a good confirmed outcome, keep this identity
+        if (value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;
+        identities.push([key, value]);
+      }
+
+      const supersOfSubs = await getSupers(helper);
+
+      // iterate over every sub-identity
+      for(const [key, value] of await getSubs(helper)) {
+        // only get subs of the identities interesting to us
+        if (identities.find((x: any) => x[0] == key) == -1) continue;
+        subs.push(constructSubInfo(key, value, supersOfSubs));
+      }
+    } catch (error) {
+      console.error(error);
+      throw Error(`Error during fetching identities on ${relayUrl}`);
+    }
+  }, relayUrl);
+
+  return [identities, subs];
+}
+
+// The utility for pulling identity and sub-identity data
+const checkRelayIdentities = async (): Promise<void> => {
+  const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url);
+  const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url);
+
+  console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length);
+  console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length);
+  console.log();
+
+  try {
+    const matchingAddresses: string[] = [];
+    const inequalIdentities: {[name: string]: [any, any]} = {};
+
+    for (const [key1, value1] of identitiesOnRelay1) {
+      const address = encodeAddress(key1);
+      const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
+      if (!identity2) continue;
+      matchingAddresses.push(address);
+
+      //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1);
+      const [_key2, value2] = identity2;
+      if (JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue;
+      inequalIdentities[address] = [value1, value2];
+    }
+
+    /*for (const [v1, v2] of Object.values(inequalIdentities)) {
+      console.log(v1.toHuman().info);
+      console.log();
+      console.log(v2.toHuman().info);
+      await new Promise(resolve => setTimeout(resolve, 4000));
+    }*/
+
+    console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`);
+    console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`);
+    console.log();
+
+    const inequalSubIdentities = [];
+    let matchesFound = 0;
+    for (const address of matchingAddresses) {
+      const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1));
+      if (!sub1) continue;
+      const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
+      if (!sub2) continue;
+
+      const [value1, value2] = [sub1[1], sub2[1]];
+      matchesFound++;
+
+      if (JSON.stringify(value1[1]) === JSON.stringify(value2[1])) {
+        continue;
+      }
+      inequalSubIdentities.push([value1, value2]);
+    }
+
+    /*for (const [v1, v2] of inequalSubIdentities) {
+      console.log(v1[1]);
+      console.log();
+      console.log(v2[1]);
+      await new Promise(resolve => setTimeout(resolve, 300));
+    }*/
+    console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`);
+    console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`);
+  } catch (error) {
+    console.error(error);
+    throw Error('Error during comparison');
+  }
+};
+
+if (process.argv[1] === module.filename)
+  checkRelayIdentities().catch(() => process.exit(1));