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

Conversation

JohnnyJumper
Copy link

This pull request allows users of ipfs-companion to look up ipfs websites that are connected to various decentralized domains such as .crypto .zil .eth.

Resolution library is making a direct blockchain call to various chains to resolve the ipfshash attached to the domain. For domains ending on .zil, it talks with Zilliqa registry contract located over here: zil1jcgu2wlx6xejqk9jw3aaankw6lsjzeunx2j0jz
for domains ending on .crypto or .eth the library talks with these smart-contracts respectively:
0xD1E5b0FF1287aA9f9A268759062E4Ab08b9Dacbe and 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e

The library weighs about 300kB according to bundlephobia

Once the ipfs hash is retrieved from the domain it redirects users to a public gateway and later being picked up by ipfs companion code which respectfully redirects it to the localhost.

This makes it very convenient for the user to browse the ipfs websites without the need to remember or store somewhere Ipfshash. instead, they can use a simple domain.

Domains to play around:

brad.crypto
johnnyjumper.zil
myetherwallet.crypto
brantly.eth

@JohnnyJumper
Copy link
Author

The build is failed because of the restrictions on file size.
My local machine is saying that the file size becomes: 4.386131 Mb which is not that much bigger than 4Mb. Will it be possible to lift the size restriction or to increase it to 4.5Mb?

Copy link
Member

@lidel lidel left a comment

Choose a reason for hiding this comment

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

Thank you for submitting this PR! ❤️
I really appreciate how surgical it is: very good starting point to start discussion and flesh out details. 🙏

The bundle size problem can be solved by putting the lib in a separate file, we already do that for ipfs libs, probably same .js could be reused (see webpack config). Context: 4MB per .js file is hard limit imposed by browser extension stores and can't be changed.

I've highlighted some minor nits inline below, but would like to highlight key concerns at play here:

User privacy and communicating what is happening to the user

Lack of DNSLink complicates things privacy-wise. We are unable to leverage hierarchical and decentralized nature of DNS for resolving content path, and instead this PR uses a centralized third party HTTP API for resolving blockchain domains.

This HTTP API is not controlled by IPFS project and current implementation:

  • introduces new surface for privacy considerations
  • sidesteps user consent (they trust IPFS project but not a third party API)
  • keeps users in the dark about sending names of browsed hostnames to centralized service
  • makes it more difficult for us to pass extension review at Mozilla and Google

Based on our prior encounters with reviews at browser extension stores, two things need to be present:

(1) Make it explicit opt-in, for example:

  • Add a new toggle in Experiments on Preferences screen ("Support UnstoppableDomains" or similar).
    • It should be disabled by default, so no data about browsing habits is sent to Infura without explicit user consent.
    • When user tries to open one of blockchain TLDs and the toggle is OFF, there should be a prompt opened in a new tab, asking user if they want to enable resolving blockchain domains over centralized provider only for this one time, always (sets toggle to ON and removes future prompts), or abort (inspiration: this screen from uBlock)

(2) add "Privacy When Using Blockchain Domains" section to /docs/privacy-policy.md explaining what is sent where, and that it requires explicit opt-in from the user + bump "Last Update" in the header

Comment on lines 4 to 9
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

add-on/src/lib/blockchain-domains.js Outdated Show resolved Hide resolved
add-on/src/lib/blockchain-domains.js Outdated Show resolved Hide resolved
@@ -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"

add-on/src/lib/ipfs-companion.js Outdated Show resolved Hide resolved
add-on/src/pages/loading/loading.html Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@JohnnyJumper
Copy link
Author

JohnnyJumper commented May 6, 2020

@lidel there is a question I couldn't found an answer in the code base. I added the localization for en and Russian language of option_supportUnstoppableDomain_title and description as well as called the browser.in18.getMessage but the chrome extension on my end didn't resolve the text at all. Mozilla Firefox on the other hand resolved and returned everything correctly. Would you have any ideas what steps did I miss for this one to work?

Nevermind rebuilding the app solved the issue

@lidel
Copy link
Member

lidel commented May 11, 2020

After understanding how centralized and specific to Ethereum ecosystem the API is (#875 (comment)) I'd like us to pause this work and discuss how we can work around centralization and reusability problems introduced in the initial version of PR.

Problem space

When user wants to open "foo.crypto" and does not run ethereum client locally, there are actually two "gateways" involved:

  1. "Ethereum gateway" – used for resolving blockchain domain name to IPFS content path
  2. "IPFS gateway" – used for loading actual data from IPFS using mentioned content path

Our concern lies in resolution step (1), loading from IPFS gateway (2) is ok.

For (2) multiple public gateways exist, user can run their own, even on localhost by installing ipfs-desktop or go-ipfs (decentralized self-hosting on localhost is default mode we encourage for IPFS Companion users)

Sadly the rich user choice present for (2) does not matter much when for the first step (1) we have to rely on a centralized HTTP API

Generic way of delegated name resolution: DNSLink

Note that we already support for other naming systems that do not require IPFS Companion to compromise on any of the things mentioned in #875 (review) and #875 (comment). And that support is enabled by default because we don't need to worry about privacy ramifications of using a third party HTTP API.

When it comes to decentralized naming systems, I firmly believe, at the very minimum, we should rely on DNS + DNSLink in cases where shipping full blockchain client is not feasible (IPFS Companion is such case). If we start embedding libraries for each naming system, start calling third-party HTTP APIs it will get out of hand really fast.

DNSLink gateway (example: https://eth.link) is much better for both user privacy and maintenance burden in projects which want to implement support for alternative naming systems.

DNSLink gateway:

  • removes all the complexity from clients and reuse existing DNSLink standard
  • removes centralization vector and the single point of failure
    • DNS caching would protect most of users from temporary outrages
    • Everyone can run their own gateway, including one on localhost
    • no proprietary APIs: DNS + DNSLink = no vendor lock-in
      • DNS client capable of resolving TXT record is all you need
      • out of the box support in go-ipfs, all public IPFS gateways and ipfs-companion without any additional code:
        (https://ipfs.io/ipns/ipfs.eth.link/)

I believe someone mentioned DNSLink gateway on the roadmap for Unstoppable Domains: what is ETA? Can we wait with this until that lands?

Ideally, Unstoppable Domains would run own gateway and IPFS could do something similar to ipfs/kubo#6448 without any custom code anywhere else. And no opt-in would be necessary.

Summary and ways to move forward

To summarize, I see ~two ways to move forward with the PR:

  1. If you use DNSLink, you gain instant interop with entire IPFS ecosystem (and anything else that understands DNSLink) and we can enable seamless resolution of your TLDs by default in IPFS Companion without any opt-ins.
    DNS resolution proved to be ok with extension stores (as its "just DNS") and also gives you nice URLs in address bar (http://foo.crypto.ipns.localhost).

  2. If you really want to delegate blockchain domain names resolution to a centralized HTTP API, this needs to be an opt-in (for example, ask user on first load of *.crypto URL).
    This is because user needs to know part of their browsing history will be sent to a third party over HTTP. Then we need to figure out if infura is OK with shipping unencrypted API key with browser extension and what happens when we hit usage limits and need to start paying. There also needs to be UI for changing the default API URL to a different endpoint, preferably a localhost one (encouraging user to run their own resolver).

  3. We could also do a mixture of both: DNSLink as the default and HTTP API as opt-in for users who want to use own localhost resolver
    (removes all concerns related to a third party HTTP API being the default, while maximizing interop)

I hope this clarifies where the problem lies and what are our options here.
Let me know your thoughts how you wish to proceed, will be happy to support either way.

@lidel
Copy link
Member

lidel commented Sep 22, 2020

I'm closing this PR: UnstoppableDomains decided to go with DNSLink interop:

@lidel lidel closed this Sep 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants