Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unstoppabledomains/feature #875

Closed
68 changes: 68 additions & 0 deletions add-on/src/lib/blockchain-domains.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { Resolution } = require('@unstoppabledomains/resolution')
const browser = require('webextension-polyfill')
// Main tool to resolve blockchain domains
const resolution = new Resolution({
blockchain: {
ens: { url: 'https://mainnet.infura.io/v3/7253dea473954208bede79ae26ae1225' },
cns: { url: 'https://mainnet.infura.io/v3/7253dea473954208bede79ae26ae1225' }
}
})
Copy link
Member

@lidel lidel May 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Is it possible to use different provider than Infura for this (eg. Cloudflare?), or are those APIs proprietary?
Would be much easier to reason about this if user could switch "blockchain gateways" via Preferences, similar to how they can change IPFS gateways right now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to use any public provider similar to infura. Which gateways do you want me to include? infura link here plays just an example of what is possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If provider can be customized, then there should be UI where user can pick a different one. Ideally, on the same screen where user gives consent for sending requests to third party API + at any time on Preferences screen.

I am not familiar with this API: who else is implementing it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infura is playing a role of a public gateway to blockchain. It is an etherium node that opens it's gate to anyone who wishes to communicate with smart contracts without launching the etherium node for themselves. If an end user has an etherium node set up he could delicate the resolution library towards it by using something like localhost:port url there. In case if ipfs-companion has an etherium node launched we can set it up so the request goes through them. I am not very familiar with other solutions on public etherium gateway other than infura.

The drawback is that Infura does require each request send with the project id ( last number after v3 ) you can obtain on registering for infura project on their website https://infura.io. After some rate the project is forced to upgrade to paid membership that's how they make their revenue.

I will create a possibility for an end user to provide their provider url or choose to use infura. The infura project id I chose for this or is newly registered and will require some updates later from ipfs-companion side to have full control of the statistics and ability to upgrade later if needed.

Copy link
Member

@lidel lidel May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this context.

If I understand this correctly, using HTTP API from Infura for resolving .crypto and any other TLD comes with serious downsides:

  • Infura requires API key that will:
    • let a third party service identify users of IPFS Companion without getting anything in return (eg. stats)
    • force IPFS Companion to upgrade to paid membership if our users go over the free quota
    • leak sooner or later: afaik API key will be unencrypted in extension sources so anyone can fetch package from Chrome Web Store, unpack it (its just a ZIP archive..) and freeload using that API key
  • unsure if there is any other public provider that implements this specific API
    • what about Cloudflare's gateway? is their API a drop-in? could we provide it as one of predefined options?
  • unsure if there is an easy way to run localhost resolver that exposes drop-in API that could be used instead of Infura
    • is infura's jsonrpc the same as some localhost service?
    • are there docs / pocs that we could use for tests and to provide as a reference for users who would prefer self-hosting?

@JohnnyJumper its unfamiliar space for me, let me know if I got any of this wrong or missed obvious ways to work around these problems


const loadingPageURL = browser.extension.getURL('dist/pages/loading/loading.html')

function domainResolution (request) {
const url = new URL(request.url)
const domain = url.hostname
console.log('domain = ', domain)
JohnnyJumper marked this conversation as resolved.
Show resolved Hide resolved
if (resolution.isSupportedDomain(domain)) {
console.log('trying to redirect to loading while hash is generating?', loadingPageURL)
browser.tabs.update({ url: loadingPageURL })
console.log('oinside hte redirect...')
return resolution.ipfsHash(domain).then(hash => {
const redirectUrl = `https://cloudflare-ipfs.com/ipfs/${hash}${url.pathname}`
JohnnyJumper marked this conversation as resolved.
Show resolved Hide resolved
browser.tabs.update({ url: redirectUrl })
return { cancel: true }
})
}
}

function parseGoogleSearch (requestDetails) {
const url = new URL(requestDetails.url)
const params = url.searchParams.get('q').trim().toLowerCase()
const q = new URL(url.protocol + '//' + params)
if (
!q.hostname ||
!isValidDNSHostname(q.hostname) ||
!resolution.isSupportedDomain(q.hostname) ||
url.pathname !== '/search'
) {
return
}
browser.tabs.update({ url: q.toString() })
return { cancel: true }
}

const rules = {
segmentMinLength: 2,
labelLength: 63,
domainLength: 253,
domainSegment: /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/,
tldSegment: /^[a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/
}

function isValidDNSHostname (hostname) {
if (!hostname || hostname.length > rules.domainLength) return false
const labels = hostname.split('.')
if (labels.length < rules.segmentMinLength) {
return false
}
return labels.every((label, i) => {
if (i < labels.length - 1) {
return rules.domainSegment.test(label) && label.length <= rules.labelLength
}
return rules.tldSegment.test(label)
})
}

exports.domainResolution = domainResolution
exports.parseGoogleSearch = parseGoogleSearch
4 changes: 3 additions & 1 deletion add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicG
const createIpfsProxy = require('./ipfs-proxy')
const { registerSubdomainProxy } = require('./http-proxy')
const { showPendingLandingPages } = require('./on-installed')

const { domainResolution, parseGoogleSearch } = require('./blockchain-domains')
// init happens on addon load in background/background.js
module.exports = async function init () {
// INIT
Expand Down Expand Up @@ -112,6 +112,8 @@ module.exports = async function init () {
onBeforeSendInfoSpec.push('extraHeaders')
}
browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, { urls: ['<all_urls>'] }, onBeforeSendInfoSpec)
browser.webRequest.onBeforeRequest.addListener(parseGoogleSearch, { urls: ['*://*.google.com/*'], types: ['main_frame'] }, ['blocking'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This listener needs to be removed and changed to be vendor-agnostic so it works for users who don't use Google search.

FYSA we already have some prior art which provides support for ipfs://:

https://github.com/ipfs-shipyard/ipfs-companion/blob/793bd6cdbe804f611ed42f7dd32c6fb8d9d224b8/add-on/src/lib/ipfs-request.js#L548-L549

https://github.com/ipfs-shipyard/ipfs-companion/blob/793bd6cdbe804f611ed42f7dd32c6fb8d9d224b8/add-on/src/lib/ipfs-request.js#L143-L147

Ask: (5) remove this listener and instead add this to add-on/src/lib/ipfs-request.js logic after "poor-mans protocol handlers"

browser.webRequest.onBeforeRequest.addListener(domainResolution, { urls: ['*://*.crypto/*', '*://*.zil/*', '*://*.eth/*'], types: ['main_frame'] }, ['blocking'])
JohnnyJumper marked this conversation as resolved.
Show resolved Hide resolved
browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, { urls: ['<all_urls>'] }, ['blocking'])
browser.webRequest.onHeadersReceived.addListener(onHeadersReceived, { urls: ['<all_urls>'] }, ['blocking', 'responseHeaders'])
browser.webRequest.onErrorOccurred.addListener(onErrorOccurred, { urls: ['<all_urls>'], types: ['main_frame'] })
Expand Down
2 changes: 0 additions & 2 deletions add-on/src/lib/ipfs-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const isIPFS = require('is-ipfs')
const isFQDN = require('is-fqdn')
const { pathAtHttpGateway, sameGateway } = require('./ipfs-path')
const { safeURL } = require('./options')

const redirectOptOutHint = 'x-ipfs-companion-no-redirect'
const recoverableNetworkErrors = new Set([
// Firefox
Expand Down Expand Up @@ -170,7 +169,6 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
if (state.dnslinkRedirect) {
const redirectUrl = dnslinkResolver.dnslinkAtGateway(request.url)
if (redirectUrl && isSafeToRedirect(request, runtime)) {
// console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect)
return { redirectUrl }
}
} else if (state.dnslinkDataPreload) {
Expand Down
12 changes: 12 additions & 0 deletions add-on/src/pages/loading/loading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="./style.css" />
JohnnyJumper marked this conversation as resolved.
Show resolved Hide resolved
</head>
<body>
<div class="loader-wrapper">
<span class="loader"><span class="loader-inner"></span></span>
</div>
</body>
</html>
54 changes: 54 additions & 0 deletions add-on/src/pages/loading/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
body {
margin: 0;
padding: 0;
width:100vw;
height: 100vh;
background-color: #eee;
}
.content {
display: flex;
justify-content: center;
align-items: center;
width:100%;
height:100%;
}
.loader-wrapper {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: #242f3f;
display:flex;
justify-content: center;
align-items: center;
}
.loader {
display: inline-block;
width: 30px;
height: 30px;
position: relative;
border: 4px solid #Fff;
animation: loader 2s infinite ease;
}
.loader-inner {
vertical-align: top;
display: inline-block;
width: 100%;
background-color: #fff;
animation: loader-inner 2s infinite ease-in;
}
@keyframes loader {
0% { transform: rotate(0deg);}
25% { transform: rotate(180deg);}
50% { transform: rotate(180deg);}
75% { transform: rotate(360deg);}
100% { transform: rotate(360deg);}
}
@keyframes loader-inner {
0% { height: 0%;}
25% { height: 0%;}
50% { height: 100%;}
75% { height: 100%;}
100% { height: 0%;}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
},
"dependencies": {
"@material/switch": "4.0.0",
"@unstoppabledomains/resolution": "^1.3.6",
JohnnyJumper marked this conversation as resolved.
Show resolved Hide resolved
"browser-process-hrtime": "1.0.0",
"choo": "7.0.0",
"chrome-dgram": "3.0.4",
Expand Down
Loading