1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

ens-ipfs - refactor for readability (#5568)

* ens-ipfs - refactor for readability

* ens-ipfs - use official ipfs gateway for better performance

* lint - remove unused code

* ens-ipfs - support path and search

* lint - gotta love that linter

* ens-ipfs - improve loading page formatting

* ens-ipfs - loading - redirect to 404 after 1 min timeout

* ens-ipfs - destructure for cleaner code
This commit is contained in:
kumavis 2018-10-21 05:48:15 -04:00 committed by GitHub
parent b0c649a4e3
commit 6d09f60bbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 125 deletions

View File

@ -1,5 +1,9 @@
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MetaMask Loading</title>
<style>
#div-logo {
@ -31,5 +35,11 @@
<div id="div-logo">
<img id="logo" src="./images/loginglogo.svg">
</div>
<script type="text/javascript">
// redirect to 404 after one minute
setTimeout(() => {
location.href = './404.html'
}, 60000)
</script>
</body>
</html>

View File

@ -29,7 +29,7 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
const getObjStructure = require('./lib/getObjStructure')
const ipfsContent = require('./lib/ipfsContent.js')
const setupEnsIpfsResolver = require('./lib/ens-ipfs/setup')
const {
ENVIRONMENT_TYPE_POPUP,
@ -58,7 +58,6 @@ const isIE = !!document.documentMode
// Edge 20+
const isEdge = !isIE && !!window.StyleMedia
let ipfsHandle
let popupIsOpen = false
let notificationIsOpen = false
const openMetamaskTabsIDs = {}
@ -164,7 +163,6 @@ async function initialize () {
const initLangCode = await getFirstPreferredLangCode()
await setupController(initState, initLangCode)
log.debug('MetaMask initialization complete.')
ipfsHandle = ipfsContent(initState.NetworkController.provider)
}
//
@ -269,10 +267,8 @@ function setupController (initState, initLangCode) {
})
global.metamaskController = controller
controller.networkController.on('networkDidChange', () => {
ipfsHandle && ipfsHandle.remove()
ipfsHandle = ipfsContent(controller.networkController.providerStore.getState())
})
const provider = controller.provider
setupEnsIpfsResolver({ provider })
// report failed transactions to Sentry
controller.txController.on(`tx:status-update`, (txId, status) => {

View File

@ -0,0 +1,54 @@
const namehash = require('eth-ens-namehash')
const multihash = require('multihashes')
const Eth = require('ethjs-query')
const EthContract = require('ethjs-contract')
const registrarAbi = require('./contracts/registrar')
const resolverAbi = require('./contracts/resolver')
module.exports = resolveEnsToIpfsContentId
async function resolveEnsToIpfsContentId ({ provider, name }) {
const eth = new Eth(provider)
const hash = namehash.hash(name)
const contract = new EthContract(eth)
// lookup registrar
const chainId = Number.parseInt(await eth.net_version(), 10)
const registrarAddress = getRegistrarForChainId(chainId)
if (!registrarAddress) {
throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`)
}
const Registrar = contract(registrarAbi).at(registrarAddress)
// lookup resolver
const resolverLookupResult = await Registrar.resolver(hash)
const resolverAddress = resolverLookupResult[0]
if (hexValueIsEmpty(resolverAddress)) {
throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`)
}
const Resolver = contract(resolverAbi).at(resolverAddress)
// lookup content id
const contentLookupResult = await Resolver.content(hash)
const contentHash = contentLookupResult[0]
if (hexValueIsEmpty(contentHash)) {
throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
}
const nonPrefixedHex = contentHash.slice(2)
const buffer = multihash.fromHexString(nonPrefixedHex)
const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256'))
return contentId
}
function hexValueIsEmpty(value) {
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
}
function getRegistrarForChainId (chainId) {
switch (chainId) {
// mainnet
case 1:
return '0x314159265dd8dbb310642f98f50c066173c1259b'
// ropsten
case 3:
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
}
}

View File

@ -0,0 +1,63 @@
const urlUtil = require('url')
const extension = require('extensionizer')
const resolveEnsToIpfsContentId = require('./resolver.js')
const supportedTopLevelDomains = ['eth']
module.exports = setupEnsIpfsResolver
function setupEnsIpfsResolver({ provider }) {
// install listener
const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`)
extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns })
// return api object
return {
// uninstall listener
remove () {
extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail)
},
}
async function webRequestDidFail (details) {
const { tabId, url } = details
// ignore requests that are not associated with tabs
if (tabId === -1) return
// parse ens name
const urlData = urlUtil.parse(url)
const { hostname: name, path, search } = urlData
const domainParts = name.split('.')
const topLevelDomain = domainParts[domainParts.length - 1]
// if unsupported TLD, abort
if (!supportedTopLevelDomains.includes(topLevelDomain)) return
// otherwise attempt resolve
attemptResolve({ tabId, name, path, search })
}
async function attemptResolve({ tabId, name, path, search }) {
extension.tabs.update(tabId, { url: `loading.html` })
try {
const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name })
let url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}`
try {
// check if ipfs gateway has result
const response = await fetch(url, { method: 'HEAD' })
// if failure, redirect to 404 page
if (response.status !== 200) {
extension.tabs.update(tabId, { url: '404.html' })
return
}
// otherwise redirect to the correct page
extension.tabs.update(tabId, { url })
} catch (err) {
console.warn(err)
// if HEAD fetch failed, redirect so user can see relevant error page
extension.tabs.update(tabId, { url })
}
} catch (err) {
console.warn(err)
extension.tabs.update(tabId, { url: `error.html?name=${name}` })
}
}
}

View File

@ -1,46 +0,0 @@
const extension = require('extensionizer')
const resolver = require('./resolver.js')
module.exports = function (provider) {
function ipfsContent (details) {
const name = details.url.substring(7, details.url.length - 1)
let clearTime = null
if (/^.+\.eth$/.test(name) === false) return
extension.tabs.query({active: true}, tab => {
extension.tabs.update(tab.id, { url: 'loading.html' })
clearTime = setTimeout(() => {
return extension.tabs.update(tab.id, { url: '404.html' })
}, 60000)
resolver.resolve(name, provider).then(ipfsHash => {
clearTimeout(clearTime)
let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => {
if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' })
extension.tabs.update(tab.id, { url: url })
})
.catch(err => {
url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
extension.tabs.update(tab.id, {url: url})
return err
})
})
.catch(err => {
clearTimeout(clearTime)
const url = err === 'unsupport' ? 'unsupport' : 'error'
extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`})
})
})
return { cancel: true }
}
extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']})
return {
remove () {
extension.webRequest.onErrorOccurred.removeListener(ipfsContent)
},
}
}

View File

@ -1,71 +0,0 @@
const namehash = require('eth-ens-namehash')
const multihash = require('multihashes')
const HttpProvider = require('ethjs-provider-http')
const Eth = require('ethjs-query')
const EthContract = require('ethjs-contract')
const registrarAbi = require('./contracts/registrar')
const resolverAbi = require('./contracts/resolver')
function ens (name, provider) {
const eth = new Eth(new HttpProvider(getProvider(provider.type)))
const hash = namehash.hash(name)
const contract = new EthContract(eth)
const Registrar = contract(registrarAbi).at(getRegistrar(provider.type))
return new Promise((resolve, reject) => {
if (provider.type === 'mainnet' || provider.type === 'ropsten') {
Registrar.resolver(hash).then((address) => {
if (address === '0x0000000000000000000000000000000000000000') {
reject(null)
} else {
const Resolver = contract(resolverAbi).at(address['0'])
return Resolver.content(hash)
}
}).then((contentHash) => {
if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null)
if (contentHash.ret !== '0x') {
const hex = contentHash['0'].substring(2)
const buf = multihash.fromHexString(hex)
resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256')))
} else {
reject(null)
}
})
} else {
return reject('unsupport')
}
})
}
function getProvider (type) {
switch (type) {
case 'mainnet':
return 'https://mainnet.infura.io/'
case 'ropsten':
return 'https://ropsten.infura.io/'
default:
return 'http://localhost:8545/'
}
}
function getRegistrar (type) {
switch (type) {
case 'mainnet':
return '0x314159265dd8dbb310642f98f50c066173c1259b'
case 'ropsten':
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
default:
return '0x0000000000000000000000000000000000000000'
}
}
module.exports.resolve = function (name, provider) {
const path = name.split('.')
const topLevelDomain = path[path.length - 1]
if (topLevelDomain === 'eth' || topLevelDomain === 'test') {
return ens(name, provider)
} else {
return new Promise((resolve, reject) => {
reject(null)
})
}
}