UNPKG

@authereum/resolution

Version:
657 lines (603 loc) 22.9 kB
import Resolution from './index'; import { ResolutionErrorCode } from './errors/resolutionError'; import { NullAddress } from './types'; import { CryptoDomainWithAdaBchAddresses, CryptoDomainWithEmail, CryptoDomainWithEmptyResolver, CryptoDomainWithoutResolver, CryptoDomainWithIpfsRecords, CryptoDomainWithTwitterVerification, mockAsyncMethods, expectSpyToBeCalled, expectResolutionErrorCode, protocolLink, } from './tests/helpers'; import FetchProvider from './FetchProvider'; import { FetchError } from 'node-fetch'; import { NamingServiceName } from './publicTypes'; import Cns from './Cns'; import standardKeys from './utils/standardKeys'; let resolution: Resolution; let cns: Cns; beforeEach(async () => { jest.restoreAllMocks(); resolution = new Resolution({ blockchain: { cns: { url: protocolLink() }, }, }); cns = resolution.cns!; }); describe('CNS', () => { it('should define the default cns contract', () => { expect(resolution.cns).toBeDefined(); expect(resolution.cns!.network).toBe(1); expect(resolution.cns!.url).toBe(protocolLink()); }); it('checks the record by key', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xa1cac442be6673c49f8e74ffc7c4fd746f3cbd0d', records: { 'ipfs.html.value': 'QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv', } }, }); const ipfsHash = await resolution.record( CryptoDomainWithIpfsRecords, 'ipfs.html.value', ); expectSpyToBeCalled(eyes); expect(ipfsHash).toBe('QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv'); }); it('should return verified twitter handle', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xb66DcE2DA6afAAa98F2013446dBCB0f4B0ab2842', owner: '0x6ec0deed30605bcd19342f3c30201db263291589', records: { [standardKeys.validation_twitter_username]: '0xcd2655d9557e5535313b47107fa8f943eb1fec4da6f348668062e66233dde21b413784c4060340f48da364311c6e2549416a6a23dc6fbb48885382802826b8111b', [standardKeys.twitter_username]: 'derainberk' } } }); const twitterHandle = await resolution.cns!.twitter( CryptoDomainWithTwitterVerification, ); expectSpyToBeCalled(spies); expect(twitterHandle).toBe('derainberk'); }); it('should return NoRecord Resolution error', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xa1cac442be6673c49f8e74ffc7c4fd746f3cbd0d', records: {}, }, }); await expectResolutionErrorCode( resolution.record(CryptoDomainWithEmptyResolver, 'No.such.record'), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(spies); }, 20000); it('should return a valid resolver address', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3' }, }); const resolverAddress = await resolution.resolver( CryptoDomainWithEmptyResolver, ); expectSpyToBeCalled(spies); expect(resolverAddress).toBe('0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3'); }); it('should not find a resolver address', async () => { const spies = mockAsyncMethods(cns, { get: { owner: '0x0000000000000000000000000000000000000000', resolver: undefined } }); await expectResolutionErrorCode( resolution.resolver('unknown-unknown-938388383.crypto'), ResolutionErrorCode.UnregisteredDomain, ); expectSpyToBeCalled(spies); }); it('should throw ResolutionError.UnspecifiedResolver', async () => { const spies = mockAsyncMethods(cns, { get: { owner: 'someowneraddress', resolver: NullAddress }, }); await expectResolutionErrorCode( resolution.resolver(CryptoDomainWithoutResolver), ResolutionErrorCode.UnspecifiedResolver, ); expectSpyToBeCalled(spies); }); describe('.Crypto', () => { it(`checks the BCH address on ${CryptoDomainWithAdaBchAddresses}`, async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xa1cac442be6673c49f8e74ffc7c4fd746f3cbd0d', records: { [standardKeys.BCH]: 'qzx048ez005q4yhphqu2pylpfc3hy88zzu4lu6q9j8', } }, }); const addr = await resolution.addr( CryptoDomainWithAdaBchAddresses, 'BCH', ); expectSpyToBeCalled(eyes); expect(addr).toBe('qzx048ez005q4yhphqu2pylpfc3hy88zzu4lu6q9j8'); }); it(`checks the ADA address on ${CryptoDomainWithAdaBchAddresses}`, async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xa1cac442be6673c49f8e74ffc7c4fd746f3cbd0d', records: { [standardKeys.ADA]: 'DdzFFzCqrhssjmxkChyAHE9MdHJkEc4zsZe7jgum6RtGzKLkUanN1kPZ1ipVPBLwVq2TWrhmPsAvArcr47Pp1VNKmZTh6jv8ctAFVCkj', } }, }); const addr = await resolution.addr( CryptoDomainWithAdaBchAddresses, 'ADA', ); expectSpyToBeCalled(eyes); expect(addr).toBe( 'DdzFFzCqrhssjmxkChyAHE9MdHJkEc4zsZe7jgum6RtGzKLkUanN1kPZ1ipVPBLwVq2TWrhmPsAvArcr47Pp1VNKmZTh6jv8ctAFVCkj', ); }); describe('.Metadata', () => { it('should resolve with ipfs stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: { [standardKeys.html]: 'QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv', } }, }); const ipfsHash = await resolution.ipfsHash(CryptoDomainWithIpfsRecords); expectSpyToBeCalled(spies); expect(ipfsHash).toBe('QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv'); }); it('should resolve with email stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: { [standardKeys.email]: 'paul@unstoppabledomains.com' } }, }); const email = await resolution.email(CryptoDomainWithEmail); expectSpyToBeCalled(spies); expect(email).toBe('paul@unstoppabledomains.com'); }); it('should resolve with httpUrl stored on cns', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: { [standardKeys.redirect_domain]: 'https://unstoppabledomains.com/', } }, }); const httpUrl = await resolution.httpUrl(CryptoDomainWithIpfsRecords); expectSpyToBeCalled(eyes); expect(httpUrl).toBe('https://unstoppabledomains.com/'); }); it('should resolve with the gundb chatId stored on cns', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xb66DcE2DA6afAAa98F2013446dBCB0f4B0ab2842', records: { [standardKeys.gundb_username]:'0x8912623832e174f2eb1f59cc3b587444d619376ad5bf10070e937e0dc22b9ffb2e3ae059e6ebf729f87746b2f71e5d88ec99c1fb3c7c49b8617e2520d474c48e1c', } }, }); const chatId = await resolution.chatId('brad.crypto'); expectSpyToBeCalled(eyes); expect(chatId).toBe( '0x8912623832e174f2eb1f59cc3b587444d619376ad5bf10070e937e0dc22b9ffb2e3ae059e6ebf729f87746b2f71e5d88ec99c1fb3c7c49b8617e2520d474c48e1c', ); }); it('should throw UnspecifiedResolver for chatId', async () => { mockAsyncMethods(cns, { get: { owner: '0xBD5F5ec7ed5f19b53726344540296C02584A5237', }, }); await expectResolutionErrorCode( resolution.chatId(CryptoDomainWithoutResolver), ResolutionErrorCode.UnspecifiedResolver, ); }); it('should resolve with the gundb public key stored on cns', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xb66DcE2DA6afAAa98F2013446dBCB0f4B0ab2842', records: { [standardKeys.gundb_public_key]: 'pqeBHabDQdCHhbdivgNEc74QO-x8CPGXq4PKWgfIzhY.7WJR5cZFuSyh1bFwx0GWzjmrim0T5Y6Bp0SSK0im3nI', } }, }); const publicKey = await resolution.chatPk('brad.crypto'); expectSpyToBeCalled(eyes); expect(publicKey).toBe( 'pqeBHabDQdCHhbdivgNEc74QO-x8CPGXq4PKWgfIzhY.7WJR5cZFuSyh1bFwx0GWzjmrim0T5Y6Bp0SSK0im3nI', ); }); it('should error out for gundb public key stored on cns', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); await expectResolutionErrorCode( resolution.chatPk(CryptoDomainWithEmptyResolver), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(eyes); }); it('should error out for gundb chatId stored on cns', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); await expectResolutionErrorCode( resolution.chatId(CryptoDomainWithEmptyResolver), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(eyes); }); }); }); describe('.Crypto ProxyReader', () => { beforeEach(async () => { resolution = new Resolution({ blockchain: { cns: { url: protocolLink() } }, }); cns = resolution.cns!; }); it('should return record by key', async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0xa1cac442be6673c49f8e74ffc7c4fd746f3cbd0d', records: { [standardKeys.html]: 'QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv', } }, }); const ipfsHash = await resolution.record( CryptoDomainWithIpfsRecords, 'ipfs.html.value', ); expectSpyToBeCalled(eyes); expect(ipfsHash).toBe('QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv'); }); it('should return NoRecord Resolution error when value not found', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); await expectResolutionErrorCode( resolution.record(CryptoDomainWithEmptyResolver, 'No.such.record'), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(spies); }); it('should return a valid resolver address', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); const resolverAddress = await resolution.resolver( CryptoDomainWithEmptyResolver, ); expectSpyToBeCalled(spies); expect(resolverAddress).toBe( '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', ); }); it('should return UnregisteredDomain error when owner address not found', async () => { const spies = mockAsyncMethods(cns, { get: { owner: NullAddress }, }); await expectResolutionErrorCode( resolution.resolver('unknown-unknown-938388383.crypto'), ResolutionErrorCode.UnregisteredDomain, ); expectSpyToBeCalled(spies); }); it('should return UnspecifiedResolver error when resolver address not found', async () => { const spies = mockAsyncMethods(cns, { get: { owner: '0xBD5F5ec7ed5f19b53726344540296C02584A5237' }, }); await expectResolutionErrorCode( resolution.resolver(CryptoDomainWithoutResolver), ResolutionErrorCode.UnspecifiedResolver, ); expectSpyToBeCalled(spies); }); it('should work without any configs', async () => { resolution = new Resolution(); const eyes = mockAsyncMethods(resolution.cns!, { get: { resolver: '0xb66DcE2DA6afAAa98F2013446dBCB0f4B0ab2842', records: { [standardKeys.ETH]: '0x8aaD44321A86b170879d7A244c1e8d360c99DdA8', } }, }); const address = await resolution.addr('brad.crypto', 'eth'); expectSpyToBeCalled(eyes); expect(address).toBe('0x8aaD44321A86b170879d7A244c1e8d360c99DdA8'); }); it(`checks the BCH address on ${CryptoDomainWithAdaBchAddresses}`, async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: { [standardKeys.BCH]: 'qzx048ez005q4yhphqu2pylpfc3hy88zzu4lu6q9j8', } }, }); const addr = await resolution.addr( CryptoDomainWithAdaBchAddresses, 'BCH', ); expectSpyToBeCalled(eyes); expect(addr).toBe('qzx048ez005q4yhphqu2pylpfc3hy88zzu4lu6q9j8'); }); it(`checks the ADA address on ${CryptoDomainWithAdaBchAddresses}`, async () => { const eyes = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: { [standardKeys.ADA]: 'DdzFFzCqrhssjmxkChyAHE9MdHJkEc4zsZe7jgum6RtGzKLkUanN1kPZ1ipVPBLwVq2TWrhmPsAvArcr47Pp1VNKmZTh6jv8ctAFVCkj', } }, }); const addr = await resolution.addr( CryptoDomainWithAdaBchAddresses, 'ADA', ); expectSpyToBeCalled(eyes); expect(addr).toBe( 'DdzFFzCqrhssjmxkChyAHE9MdHJkEc4zsZe7jgum6RtGzKLkUanN1kPZ1ipVPBLwVq2TWrhmPsAvArcr47Pp1VNKmZTh6jv8ctAFVCkj', ); }); describe('.Metadata', () => { it('should resolve with ipfs stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: { [standardKeys.html]: 'QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv', } }, }); const ipfsHash = await resolution.ipfsHash(CryptoDomainWithIpfsRecords); expectSpyToBeCalled(spies); expect(ipfsHash).toBe('QmVJ26hBrwwNAPVmLavEFXDUunNDXeFSeMPmHuPxKe6dJv'); }); it('should resolve with email stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: {[standardKeys.email]: 'paul@unstoppabledomains.com'}, }, }); const email = await resolution.email(CryptoDomainWithEmail); expectSpyToBeCalled(spies); expect(email).toBe('paul@unstoppabledomains.com'); }); it('should resolve with httpUrl stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: {[standardKeys.redirect_domain]: 'https://unstoppabledomains.com/',} }, }); const httpUrl = await resolution.httpUrl(CryptoDomainWithIpfsRecords); expectSpyToBeCalled(spies); expect(httpUrl).toBe('https://unstoppabledomains.com/'); }); it('should resolve with the gundb chatId stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xA1cAc442Be6673C49f8E74FFC7c4fD746f3cBD0D', records: { [standardKeys.gundb_username]: '0x8912623832e174f2eb1f59cc3b587444d619376ad5bf10070e937e0dc22b9ffb2e3ae059e6ebf729f87746b2f71e5d88ec99c1fb3c7c49b8617e2520d474c48e1c', } }, }); const chatId = await resolution.chatId('brad.crypto'); expectSpyToBeCalled(spies); expect(chatId).toBe( '0x8912623832e174f2eb1f59cc3b587444d619376ad5bf10070e937e0dc22b9ffb2e3ae059e6ebf729f87746b2f71e5d88ec99c1fb3c7c49b8617e2520d474c48e1c', ); }); it('should throw UnspecifiedResolver for chatId', async () => { mockAsyncMethods(cns, { get: { owner: '0xBD5F5ec7ed5f19b53726344540296C02584A5237', records: {}, resolver: NullAddress }, }); await expectResolutionErrorCode( resolution.chatId(CryptoDomainWithoutResolver), ResolutionErrorCode.UnspecifiedResolver, ); }); it('should resolve with the gundb public key stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0xb66DcE2DA6afAAa98F2013446dBCB0f4B0ab2842', records: { [standardKeys.gundb_public_key]: 'pqeBHabDQdCHhbdivgNEc74QO-x8CPGXq4PKWgfIzhY.7WJR5cZFuSyh1bFwx0GWzjmrim0T5Y6Bp0SSK0im3nI', } }, }); const publicKey = await resolution.chatPk('brad.crypto'); expectSpyToBeCalled(spies); expect(publicKey).toBe( 'pqeBHabDQdCHhbdivgNEc74QO-x8CPGXq4PKWgfIzhY.7WJR5cZFuSyh1bFwx0GWzjmrim0T5Y6Bp0SSK0im3nI', ); }); it('should error out for gundb public key stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); await expectResolutionErrorCode( resolution.chatPk(CryptoDomainWithEmptyResolver), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(spies); }); it('should error out for gundb chatId stored on cns', async () => { const spies = mockAsyncMethods(cns, { get: { resolver: '0x878bC2f3f717766ab69C0A5f9A6144931E61AEd3', records: {}, }, }); await expectResolutionErrorCode( resolution.chatId(CryptoDomainWithEmptyResolver), ResolutionErrorCode.RecordNotFound, ); expectSpyToBeCalled(spies); }); }); }); describe('.Hashing', () => { describe('.Namehash', () => { it('supports root node', async () => { expect(resolution.isSupportedDomain('crypto')).toEqual(true); expect(resolution.namehash('crypto')).toEqual( '0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f', ); }); it('starts with -', async () => { expect(resolution.isSupportedDomain('-hello.crypto')).toEqual(true); expect(resolution.namehash('-hello.crypto')).toBe( '0xc4ad028bcae9b201104e15f872d3e85b182939b06829f75a128275177f2ff9b2', ); }); it('ends with -', async () => { expect(resolution.isSupportedDomain('hello-.crypto')).toEqual(true); expect(resolution.namehash('hello-.crypto')).toBe( '0x82eaa6ef14e438940bfd7747e0e4c4fec42af20cee28ddd0a7d79f52b1c59b72', ); }); it('starts and ends with -', async () => { expect(resolution.isSupportedDomain('-hello-.crypto')).toEqual(true); expect(resolution.namehash('-hello-.crypto')).toBe( '0x90cc1963ff09ce95ee2dbb3830df4f2115da9756e087a50283b3e65f6ffe2a4e', ); }); it('should throw UnregisteredDomain', async () => { const eyes = mockAsyncMethods(cns, { get: {owner: NullAddress}, }); await expectResolutionErrorCode( resolution.record('unregistered.crypto', 'crypto.ETH.address'), ResolutionErrorCode.UnregisteredDomain, ); expectSpyToBeCalled(eyes); }); }); describe('#childhash', () => { it('checks root crypto domain', () => { const rootHash = '0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f'; expect(resolution.namehash('crypto')).toBe(rootHash); expect( resolution.childhash( '0000000000000000000000000000000000000000000000000000000000000000', 'crypto', NamingServiceName.CNS, ), ).toBe(rootHash); }); it('checks the childhash functionality', () => { const namehash = resolution.namehash('hello.world.crypto'); const childhash = resolution.childhash( resolution.namehash('world.crypto'), 'hello', NamingServiceName.CNS, ); expect(namehash).toBe(childhash); }); it('checks childhash multi level domain', () => { const label = 'ich'; const parent = 'ni.san.yon.hello.world.crypto'; const domain = `${label}.${parent}`; const namehash = resolution.namehash(domain); const childhash = resolution.childhash( resolution.namehash(parent), 'ich', NamingServiceName.CNS, ); expect(childhash).toBe(namehash); }); }); }); describe('#namehash', () => { it('supports options', async () => { expect(resolution.namehash('operadingo4.crypto')).toEqual( '0x70f542f09763d3ab404a6d87f6a2fad7d49f01b09c44064b4227d165ead5cf25', ); expect( resolution.namehash('operadingo4.crypto', { prefix: false }), ).toEqual( '70f542f09763d3ab404a6d87f6a2fad7d49f01b09c44064b4227d165ead5cf25', ); expect( resolution.namehash('operadingo4.crypto', { format: 'dec' }), ).toEqual( '51092378573785850370557709888128643877973998831507731627523713553233928900389', ); }); }); describe('Providers', () => { it('should throw error when FetchProvider throws FetchError', async () => { const url = protocolLink(); const provider = new FetchProvider(NamingServiceName.CNS, url); resolution = new Resolution({ blockchain: { cns: { url, provider } } }); jest .spyOn(provider as any, 'fetch') .mockRejectedValue(new FetchError('error', 'error_type')); await expectResolutionErrorCode( resolution.record(CryptoDomainWithEmptyResolver, 'No.such.record'), ResolutionErrorCode.NamingServiceDown, ); }); it('should throw error when FetchProvider throws Error', async () => { const url = protocolLink(); const provider = new FetchProvider(NamingServiceName.CNS, url); resolution = new Resolution({ blockchain: { cns: { url, provider } } }); jest .spyOn(provider as any, 'fetch') .mockRejectedValue(new Error('error_up')); await expect( resolution.record(CryptoDomainWithEmptyResolver, 'No.such.record'), ).rejects.toEqual(new Error('error_up')); }); }); });