From f708e4063ca46a055bc3a9bab49f4d9ae29b9bee Mon Sep 17 00:00:00 2001 From: JGiter Date: Mon, 28 Feb 2022 13:49:54 +0200 Subject: [PATCH] feat: upgrade resolver to v2 --- e2e/utils/setup_contracts.ts | 10 +++- package.json | 3 +- src/errors/UnregisteredResolverError.ts | 5 ++ src/modules/domains/domains.service.ts | 73 +++++++++++++++++-------- src/modules/domains/domains.types.ts | 23 +++++++- 5 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 src/errors/UnregisteredResolverError.ts diff --git a/e2e/utils/setup_contracts.ts b/e2e/utils/setup_contracts.ts index 83d5de5b..30129922 100644 --- a/e2e/utils/setup_contracts.ts +++ b/e2e/utils/setup_contracts.ts @@ -4,6 +4,7 @@ import { Chain } from '@ew-did-registry/did'; import { DomainNotifier__factory } from '../../ethers/factories/DomainNotifier__factory'; import type { DomainNotifier } from '../../ethers/DomainNotifier'; import { RoleDefinitionResolver__factory } from '../../ethers/factories/RoleDefinitionResolver__factory'; +import { RoleDefinitionResolverV2__factory } from '../../ethers/factories/RoleDefinitionResolverV2__factory'; import type { RoleDefinitionResolver } from '../../ethers/RoleDefinitionResolver'; import { ENSRegistry } from '../../ethers/ENSRegistry'; import { ENSRegistry__factory } from '../../ethers/factories/ENSRegistry__factory'; @@ -14,6 +15,7 @@ import { ClaimManager__factory } from '../../ethers/factories/ClaimManager__fact import { ClaimManager } from '../../ethers/ClaimManager'; import { setChainConfig } from '../../src/config/chain.config'; import { labelhash } from '../../src/utils/ensHash'; +import { RoleDefinitionResolverV2 } from '../../ethers/RoleDefinitionResolverV2'; const { JsonRpcProvider } = providers; const { parseEther, namehash } = utils; @@ -27,6 +29,7 @@ const provider = new JsonRpcProvider(rpcUrl); let didRegistry: Contract; let ensRegistry: ENSRegistry; let ensResolver: RoleDefinitionResolver; +let ensResolverV2: RoleDefinitionResolverV2; let domainNotifer: DomainNotifier; let assetsManager: IdentityManager; let claimManager: ClaimManager; @@ -51,6 +54,10 @@ const deployEns = async () => { ensRegistry.address, domainNotifer.address ); + ensResolverV2 = await new RoleDefinitionResolverV2__factory(deployer).deploy( + ensRegistry.address, + domainNotifer.address + ); }; const deployIdentityManager = async (): Promise => { @@ -93,6 +100,7 @@ export const setupENS = async (rootOwner: string) => { rpcUrl, chainName: Chain.VOLTA, ensRegistryAddress: ensRegistry.address, + ensResolverV2Address: ensResolverV2.address, ensResolverAddress: ensResolver.address, didRegistryAddress: didRegistry.address, assetManagerAddress: assetsManager.address, @@ -103,7 +111,7 @@ export const setupENS = async (rootOwner: string) => { namehash(''), labelhash(root), rootOwner, - ensResolver.address, + ensResolverV2.address, BigNumber.from(0) ); await tx.wait(); diff --git a/package.json b/package.json index d61051fa..f0b84d4f 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,13 @@ "build:contracts": "npm run build:typechain:ens && npm run build:typechain:did && npm run build:typechain:roles && npm run build:typechain:staking", "build:typechain:ens": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@ensdomains/ens/build/contracts/ENSRegistry.json\"", "build:typechain:did": "npm run build:typechain:offerableIdentity && npm run build:typechain:identityManager", - "build:typechain:roles": " npm run build:typechain:claimManager && npm run build:typechain:roledefinitionresolver && npm run build:typechain:domainnotifier && npm run build:typechain:publicresolver", + "build:typechain:roles": " npm run build:typechain:claimManager && npm run build:typechain:roledefinitionresolver && npm run build:typechain:domainnotifier && npm run build:typechain:publicresolver && npm run build:typechain:roledefinitionresolverv2", "build:typechain:staking": "npm run build:typechain:staking-pool", "build:typechain:offerableIdentity": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@ew-did-registry/proxyidentity/build/contracts/OfferableIdentity.json\"", "build:typechain:identityManager": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@ew-did-registry/proxyidentity/build/contracts/IdentityManager.json\"", "build:typechain:claimManager": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@energyweb/iam-contracts/dist/contracts/ClaimManager.json\"", "build:typechain:roledefinitionresolver": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@energyweb/iam-contracts/dist/contracts/RoleDefinitionResolver.json\"", + "build:typechain:roledefinitionresolverv2": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@energyweb/iam-contracts/dist/contracts/RoleDefinitionResolverV2.json\"", "build:typechain:domainnotifier": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@energyweb/iam-contracts/dist/contracts/DomainNotifier.json\"", "build:typechain:publicresolver": "typechain --target ethers-v5 --out-dir ethers \"./node_modules/@energyweb/iam-contracts/dist/contracts/PublicResolver.json\"", "build:typechain:staking-pool": "typechain --target ethers-v5 --out-dir ethers-staking \"./node_modules/@energyweb/staking-pool/dist/contracts/StakingPool.sol/StakingPool.json\"", diff --git a/src/errors/UnregisteredResolverError.ts b/src/errors/UnregisteredResolverError.ts new file mode 100644 index 00000000..fd5bab8c --- /dev/null +++ b/src/errors/UnregisteredResolverError.ts @@ -0,0 +1,5 @@ +export class UnregisteredResolverError extends Error { + constructor(domain: string, resolver: string) { + super(`Domain ${domain} is defined on unregistered resolver ${resolver}`); + } +} diff --git a/src/modules/domains/domains.service.ts b/src/modules/domains/domains.service.ts index 7c33dcd4..5623998f 100644 --- a/src/modules/domains/domains.service.ts +++ b/src/modules/domains/domains.service.ts @@ -3,11 +3,13 @@ import { IAppDefinition, IOrganizationDefinition, IRoleDefinition, + IRoleDefinitionV2, EncodedCall, DomainReader, - DomainTransactionFactory, ResolverContractType, DomainHierarchy, + VOLTA_CHAIN_ID, + DomainTransactionFactoryV2, } from '@energyweb/iam-contracts'; import { ENSRegistry } from '../../../ethers/ENSRegistry'; import { ENSRegistry__factory } from '../../../ethers/factories/ENSRegistry__factory'; @@ -27,6 +29,8 @@ import { SignerService } from '../signer/signer.service'; import { NamespaceType, IOrganization } from './domains.types'; import { SearchType } from '../cacheClient/cacheClient.types'; import { validateAddress } from '../../utils/address'; +import { UnregisteredResolverError } from '../../errors/UnregisteredResolverError'; +import { castToV2 } from './domains.types'; const { namehash } = utils; @@ -34,10 +38,12 @@ export class DomainsService { private chainId: number; private _ensRegistry: ENSRegistry; private _domainDefinitionReader: DomainReader; - private _domainDefinitionTransactionFactory: DomainTransactionFactory; + private _domainDefinitionTransactionFactory: DomainTransactionFactoryV2; private _domainHierarchy: DomainHierarchy; private _owner: string; private _ensRegistryAddress: string; + private _ensResolver: string; + private _ensResolverV2Address: string; private _ensResolverAddress: string; private _ensPublicResolverAddress: string; private _ttl = BigNumber.from(0); @@ -61,11 +67,14 @@ export class DomainsService { const provider = this._signerService.provider; const { ensRegistryAddress, + ensResolverV2Address, ensResolverAddress, ensPublicResolverAddress, domainNotifierAddress, } = chainConfigs()[this.chainId]; this._ensRegistryAddress = ensRegistryAddress; + this._ensResolver = ensResolverV2Address; + this._ensResolverV2Address = ensResolverV2Address; this._ensResolverAddress = ensResolverAddress; this._ensPublicResolverAddress = ensPublicResolverAddress; this._ensRegistry = new ENSRegistry__factory( @@ -78,28 +87,37 @@ export class DomainsService { ensRegistryAddress, provider, }); - ensPublicResolverAddress && + + this._domainDefinitionReader.addKnownResolver({ + chainId, + address: ensResolverV2Address, + type: ResolverContractType.RoleDefinitionResolver_v2, + }); + + // until role definitions are migrated to resolver_v2 + if (chainId === VOLTA_CHAIN_ID) { this._domainDefinitionReader.addKnownResolver({ chainId, - address: ensPublicResolverAddress, - type: ResolverContractType.PublicResolver, + address: ensResolverAddress, + type: ResolverContractType.RoleDefinitionResolver_v1, }); - ensResolverAddress && this._domainDefinitionReader.addKnownResolver({ chainId, - address: ensResolverAddress, - type: ResolverContractType.RoleDefinitionResolver_v1, + address: ensPublicResolverAddress, + type: ResolverContractType.PublicResolver, }); - this._domainDefinitionTransactionFactory = new DomainTransactionFactory({ - domainResolverAddress: ensResolverAddress, + } + this._domainDefinitionTransactionFactory = new DomainTransactionFactoryV2({ + domainResolverAddress: ensResolverV2Address, }); this._domainHierarchy = new DomainHierarchy({ domainReader: this._domainDefinitionReader, - ensRegistryAddress: this._ensRegistryAddress, + ensRegistryAddress: ensRegistryAddress, provider, domainNotifierAddress: domainNotifierAddress, publicResolverAddress: ensPublicResolverAddress, }); + this._owner = this._signerService.address; } @@ -115,7 +133,7 @@ export class DomainsService { data, }: { domain: string; - data: IAppDefinition | IOrganizationDefinition | IRoleDefinition; + data: IAppDefinition | IOrganizationDefinition | IRoleDefinitionV2; }) { // Special case of updating legacy PublicResolver definitions if (await this.updateLegacyDefinition(domain, data)) { @@ -290,9 +308,10 @@ export class DomainsService { }: { roleName: string; namespace: string; - data: IRoleDefinition; + data: IRoleDefinition | IRoleDefinitionV2; returnSteps?: boolean; }) { + const dataV2 = castToV2(data); const newDomain = `${roleName}.${namespace}`; const from = await this.getOwner({ namespace }); const steps = [ @@ -307,7 +326,7 @@ export class DomainsService { { tx: this._domainDefinitionTransactionFactory.newRole({ domain: newDomain, - roleDefinition: data, + roleDefinition: dataV2, }), info: 'Set name and definition for role', }, @@ -368,8 +387,6 @@ export class DomainsService { } const apps = await this.getAppsOfOrg(namespace); - // @todo fix /org/namespace/suborgs and transfer suborganizations - // https://energyweb.atlassian.net/browse/ICS-135 if (apps && apps.length > 0) { if (!withSubdomains) { throw new Error( @@ -965,18 +982,22 @@ export class DomainsService { */ async updateLegacyDefinition( domain: string, - data: IAppDefinition | IOrganizationDefinition | IRoleDefinition + data: + | IAppDefinition + | IOrganizationDefinition + | IRoleDefinition + | IRoleDefinitionV2 ): Promise { const node = namehash(domain); const currentResolverAddress = await this._ensRegistry.resolver(node); - const { ensPublicResolverAddress, ensResolverAddress, ensRegistryAddress } = + const { ensResolverV2Address, ensRegistryAddress } = chainConfigs()[this.chainId]; - if (currentResolverAddress === ensPublicResolverAddress) { + if (currentResolverAddress !== ensResolverV2Address) { const updateResolverTransaction: EncodedCall = { to: ensRegistryAddress, data: this._ensRegistry.interface.encodeFunctionData('setResolver', [ node, - ensResolverAddress, + ensResolverV2Address, ]), }; // Need to use newRole/newDomain as need to set reverse domain name @@ -984,7 +1005,7 @@ export class DomainsService { ? this._domainDefinitionTransactionFactory.newRole({ domain, roleDefinition: { - ...data, + ...castToV2(data), version: parseInt(data.version.toString(), 10), }, }) @@ -1015,11 +1036,17 @@ export class DomainsService { continue; } const resolver = await this._ensRegistry.resolver(namehash(role)); - if (resolver === this._ensResolverAddress) { + if ( + [this._ensResolverAddress, this._ensResolverV2Address].includes( + resolver + ) + ) { types[role].add(RegistrationTypes.OnChain); types[role].add(RegistrationTypes.OffChain); } else if (resolver === this._ensPublicResolverAddress) { types[role].add(RegistrationTypes.OffChain); + } else { + throw new UnregisteredResolverError(role, resolver); } } return types; @@ -1045,7 +1072,7 @@ export class DomainsService { namehash(domain), labelhash(nodeName), owner, - this._ensResolverAddress, + this._ensResolver, this._ttl, ]), }; diff --git a/src/modules/domains/domains.types.ts b/src/modules/domains/domains.types.ts index d699cbec..af9d7617 100644 --- a/src/modules/domains/domains.types.ts +++ b/src/modules/domains/domains.types.ts @@ -1,4 +1,10 @@ -import { IAppDefinition, IOrganizationDefinition, IRoleDefinition } from '@energyweb/iam-contracts'; +import { + IAppDefinition, + IOrganizationDefinition, + IRevokerDefinition, + IRoleDefinition, + IRoleDefinitionV2, +} from '@energyweb/iam-contracts'; export enum NamespaceType { Role = 'roles', @@ -38,3 +44,18 @@ export interface IApp { } export const NODE_FIELDS_KEY = 'metadata'; + +export function castToV2( + roleDef: IRoleDefinition | IRoleDefinitionV2 +): IRoleDefinitionV2 { + if (!Object.keys(roleDef).includes('revoker')) { + const revoker: IRevokerDefinition = { + did: roleDef.issuer.did, + revokerType: roleDef.issuer.issuerType, + roleName: roleDef.issuer.roleName, + }; + return { ...roleDef, revoker }; + } else { + return roleDef; + } +}