difftreelog
util(identity-setter): compose pulled identities into preimages
in: master
1 file changed
tests/src/util/identitySetter.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.03//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.6//7// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [sudo key]`8// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`910import {encodeAddress} from '@polkadot/keyring';11import {usingPlaygrounds, Pallets} from './index';12import {ChainHelperBase} from './playgrounds/unique';1314const relayUrl = process.argv[2] ?? 'ws://localhost:9844';15const paraUrl = process.argv[3] ?? 'ws://localhost:9944';16const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';1718export function extractAccountId(key: any): string {19 return (key as any).toHuman()[0];20}2122export 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}2829export function extractIdentity(key: any, value: any): [string, any] {30 return [extractAccountId(key), extractIdentityInfo(value)];31}3233export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {34 const identities: [string, any][] = [];35 for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {36 const value = v as any;37 if (value.isNone) {38 if (noneCasePredicate) noneCasePredicate(key, value);39 continue;40 }41 identities.push(extractIdentity(key, value));42 }43 return identities;44}4546// whether the existing chain data is more important than the coming47function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {48 if (!currentChainId) return false;49 // information has come from local chain, and is automatically superior50 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}5455// construct an object with all data necessary for insertion from storage query results56export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {57 const deposit = subQuery.toJSON()[0];58 const subIdentities = subQuery.toHuman()[1];59 subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));6061 return [62 encodeAddress(identityAccount, ss58), [63 deposit,64 subIdentities.map((sub: string): [string, object] | null => {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]];71 }).filter((x: any) => x),72 ],73 ];74}7576export async function getSubs(helper: ChainHelperBase) {77 return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);78}7980export async function getSupers(helper: ChainHelperBase) {81 return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);82}8384// The utility for pulling identity and sub-identity data85const forceInsertIdentities = async (): Promise<void> => {86 let relaySS58Prefix = 0;87 const identitiesOnRelay: any[] = [];88 const subsOnRelay: any[] = [];89 const identitiesToRemove: string[] = [];90 await usingPlaygrounds(async helper => {91 try {92 relaySS58Prefix = helper.chain.getChainProperties().ss58Format;93 // iterate over every identity94 for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {95 // if any of the judgements resulted in a good confirmed outcome, keep this identity96 let knownGood = false, reasonable = false;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 format103 value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];104 identitiesOnRelay.push([key, value]);105 }106107 const sublessIdentities = [...identitiesOnRelay];108 const supersOfSubs = await getSupers(helper);109110 // iterate over every sub-identity111 for(const [key, value] of await getSubs(helper)) {112 // only get subs of the identities interesting to us113 const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key);114 if (identityIndex == -1) continue;115 sublessIdentities.splice(identityIndex, 1);116 subsOnRelay.push(constructSubInfo(key, value, supersOfSubs));117 }118119 // mark the rest of sub-identities for deletion with empty arrays120 /*for(const [account, _identity] of sublessIdentities) {121 subsOnRelay.push([account, ['0', []]]);122 }*/123 } catch (error) {124 console.error(error);125 throw Error('Error during fetching identities');126 }127 }, relayUrl);128129 await usingPlaygrounds(async (helper, privateKey) => {130 if (helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');131 try {132 const superuser = await privateKey(key);133 const ss58Format = helper.chain.getChainProperties().ss58Format;134 const paraIdentities = await getIdentities(helper);135 const identitiesToAdd: any[] = [];136 const paraAccountsRegistrators: {[name: string]: number} = {};137138 // cross-reference every account for changes139 for (const [key, value] of identitiesOnRelay) {140 const encodedKey = encodeAddress(key, ss58Format);141142 // only update if the identity info does not exist or is changed143 const identity = paraIdentities.find(i => i[0] === encodedKey);144 if (identity) {145 const registratorId = identity[1].judgements[0][0];146 paraAccountsRegistrators[encodedKey] = registratorId;147 if (isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])148 || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {149 continue;150 }151 }152153 identitiesToAdd.push([key, value]);154 // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:155 // 1) it was deleted on the relay;156 // 2) it is our own identity, we don't want to delete it.157 // identitiesToRemove.push((key as any).toHuman()[0]);158 }159160 if (identitiesToRemove.length != 0)161 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);162 if (identitiesToAdd.length != 0)163 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identitiesToAdd]);164165 console.log(`Tried to upload ${identitiesToAdd.length} identities`166 + ` and found ${identitiesToRemove.length} identities for potential removal.`167 + ` Now there are ${(awaithelper.getApi().query.identity.identityOf.keys()).length}.`);168169 // fill sub-identities170 const paraSubs = await getSubs(helper);171 const supersOfSubs = await getSupers(helper);172 const subsToUpdate: any[] = [];173174 for (const [key, value] of subsOnRelay) {175 const encodedKey = encodeAddress(key, ss58Format);176 const sub = paraSubs.find(i => i[0] === encodedKey);177 if (sub) {178 // only update if the sub-identity info does not exist or is changed179 if (isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)180 || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {181 continue;182 }183 } else if (value[1].length == 0)184 continue;185186 subsToUpdate.push([key, value]);187 }188189 if (subsToUpdate.length != 0)190 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsToUpdate]);191192 console.log(`Also tried to update ${subsToUpdate.length} identities with their sub-identities.`193 + ` Now there are ${(awaithelper.getApi().query.identity.subsOf.keys()).length} identities with subs.`);194 } catch (error) {195 console.error(error);196 throw Error('Error during setting identities');197 }198 }, paraUrl);199};200201if (process.argv[1] === module.filename)202 forceInsertIdentities().catch(() => process.exit(1));1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.03//4// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another.5// Only changed or previously non-existent data are inserted.6//7// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key]8// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`910import {encodeAddress} from '@polkadot/keyring';11import {IKeyringPair} from '@polkadot/types/types';12import {usingPlaygrounds, Pallets} from './index';13import {ChainHelperBase} from './playgrounds/unique';1415const relayUrl = process.argv[2] ?? 'ws://localhost:9844';16const paraUrl = process.argv[3] ?? 'ws://localhost:9944';17const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';1819export function extractAccountId(key: any): string {20 return (key as any).toHuman()[0];21}2223export function extractIdentityInfo(value: any): object {24 const heart = (value as any).unwrap();25 const identity = heart.toJSON();26 identity.judgements = heart.judgements.toHuman();27 return identity;28}2930export function extractIdentity(key: any, value: any): [string, any] {31 return [extractAccountId(key), extractIdentityInfo(value)];32}3334export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {35 const identities: [string, any][] = [];36 for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {37 const value = v as any;38 if (value.isNone) {39 if (noneCasePredicate) noneCasePredicate(key, value);40 continue;41 }42 identities.push(extractIdentity(key, value));43 }44 return identities;45}4647// whether the existing chain data is more important than the coming48function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {49 if (!currentChainId) return false;50 // information has come from local chain, and is automatically superior51 if (currentChainId == helper.chain.getChainProperties().ss58Format) return true;52 // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2)53 return currentChainId < relayChainId;54}5556// construct an object with all data necessary for insertion from storage query results57export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {58 const deposit = subQuery.toJSON()[0];59 const subIdentities = subQuery.toHuman()[1];60 subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));6162 return [63 encodeAddress(identityAccount, ss58), [64 deposit,65 subIdentities.map((sub: string): [string, object] | null => {66 const sup = supers.find((sup: any) => sup[0] === sub);67 if (!sup) {68 console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`);69 return null;70 }71 return [encodeAddress(sub, ss58), sup[1].toJSON()[1]];72 }).filter((x: any) => x),73 ],74 ];75}7677export async function getSubs(helper: ChainHelperBase) {78 return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);79}8081export async function getSupers(helper: ChainHelperBase) {82 return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);83}8485async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) {86 try {87 await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]);88 } catch(e: any) {89 if (e.message.includes('AlreadyNoted')) {90 console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.');91 } else {92 console.error(e);93 }94 }95}9697// The utility for pulling identity and sub-identity data98const forceInsertIdentities = async (): Promise<void> => {99 let relaySS58Prefix = 0;100 const identitiesOnRelay: any[] = [];101 const subsOnRelay: any[] = [];102 const identitiesToRemove: string[] = [];103 await usingPlaygrounds(async helper => {104 try {105 relaySS58Prefix = helper.chain.getChainProperties().ss58Format;106 // iterate over every identity107 for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {108 // if any of the judgements resulted in a good confirmed outcome, keep this identity109 let knownGood = false, reasonable = false;110 for (const [_id, judgement] of value.judgements) {111 if (judgement == 'KnownGood') knownGood = true;112 if (judgement == 'Reasonable') reasonable = true;113 }114 if (!(reasonable || knownGood)) continue;115 // replace the registrator id with the relay chain's ss58 format116 value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];117 identitiesOnRelay.push([key, value]);118 }119120 const sublessIdentities = [...identitiesOnRelay];121 const supersOfSubs = await getSupers(helper);122123 // iterate over every sub-identity124 for(const [key, value] of await getSubs(helper)) {125 // only get subs of the identities interesting to us126 const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key);127 if (identityIndex == -1) continue;128 sublessIdentities.splice(identityIndex, 1);129 subsOnRelay.push(constructSubInfo(key, value, supersOfSubs));130 }131132 // mark the rest of sub-identities for deletion with empty arrays133 /*for(const [account, _identity] of sublessIdentities) {134 subsOnRelay.push([account, ['0', []]]);135 }*/136 } catch (error) {137 console.error(error);138 throw Error('Error during fetching identities');139 }140 }, relayUrl);141142 await usingPlaygrounds(async (helper, privateKey) => {143 if (helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');144 if (helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.');145146 try {147 const preimageMaker = await privateKey(key);148 const ss58Format = helper.chain.getChainProperties().ss58Format;149 const paraIdentities = await getIdentities(helper);150 const identitiesToAdd: any[] = [];151 const paraAccountsRegistrators: {[name: string]: number} = {};152153 // cross-reference every account for changes154 for (const [key, value] of identitiesOnRelay) {155 const encodedKey = encodeAddress(key, ss58Format);156157 // only update if the identity info does not exist or is changed158 const identity = paraIdentities.find(i => i[0] === encodedKey);159 if (identity) {160 const registratorId = identity[1].judgements[0][0];161 paraAccountsRegistrators[encodedKey] = registratorId;162 if (isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])163 || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {164 continue;165 }166 }167168 identitiesToAdd.push([key, value]);169 // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:170 // 1) it was deleted on the relay;171 // 2) it is our own identity, we don't want to delete it.172 // identitiesToRemove.push((key as any).toHuman()[0]);173 }174175 console.log(identitiesToAdd[0][1]);176177 if (identitiesToRemove.length != 0)178 await uploadPreimage(179 helper,180 preimageMaker,181 helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(),182 );183 if (identitiesToAdd.length != 0)184 await uploadPreimage(185 helper,186 preimageMaker,187 helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(),188 );189190 console.log(`Tried to push ${identitiesToAdd.length} identities`191 + ` and found ${identitiesToRemove.length} identities for potential removal.`192 + ` Currently there are ${(awaithelper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`);193194 // fill sub-identities195 const paraSubs = await getSubs(helper);196 const supersOfSubs = await getSupers(helper);197 const subsToUpdate: any[] = [];198199 for (const [key, value] of subsOnRelay) {200 const encodedKey = encodeAddress(key, ss58Format);201 const sub = paraSubs.find(i => i[0] === encodedKey);202 if (sub) {203 // only update if the sub-identity info does not exist or is changed204 if (isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)205 || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {206 continue;207 }208 } else if (value[1].length == 0)209 continue;210211 subsToUpdate.push([key, value]);212 }213214 if (subsToUpdate.length != 0)215 await uploadPreimage(216 helper,217 preimageMaker,218 helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(),219 );220221 console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.`222 + ` Currently there are ${(awaithelper.getApi().query.identity.subsOf.keys()).length} identities with subs.`);223 } catch (error) {224 console.error(error);225 throw Error('Error during setting identities');226 }227 }, paraUrl);228};229230if (process.argv[1] === module.filename)231 forceInsertIdentities().catch(() => process.exit(1));