Skip to content

Commit

Permalink
Ipfs cid v1 base32 (#7362)
Browse files Browse the repository at this point in the history
add ipfs gateway to advanced settings
use ipfs gateway from settings
use ipfs.dweb.link as default CID gateway
disallow gateway.ipfs.io as gateway
  • Loading branch information
pldespaigne authored and rekmarks committed Dec 12, 2019
1 parent f49bc58 commit 0ef7f60
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 24 deletions.
16 changes: 14 additions & 2 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,18 @@
"invalidSeedPhrase": {
"message": "Invalid seed phrase"
},
"ipfsGateway": {
"message": "IPFS Gateway"
},
"ipfsGatewayDescription": {
"message": "Enter the URL of the IPFS CID gateway to use for ENS content resolution."
},
"invalidIpfsGateway": {
"message": "Invalid IPFS Gateway: The value must be a valid URL"
},
"forbiddenIpfsGateway": {
"message": "Forbidden IPFS Gateway: Please specify a CID gateway"
},
"jsonFile": {
"message": "JSON File",
"description": "format for importing an account"
Expand Down Expand Up @@ -1336,7 +1348,7 @@
"message": "Sync data with 3Box (experimental)"
},
"syncWithThreeBoxDescription": {
"message": "Turn on to have your settings backed up with 3Box. This feature is currenty experimental; use at your own risk."
"message": "Turn on to have your settings backed up with 3Box. This feature is currently experimental; use at your own risk."
},
"syncWithThreeBoxDisabled": {
"message": "3Box has been disabled due to an error during the initial sync"
Expand All @@ -1360,7 +1372,7 @@
"message": "Make sure nobody else is looking at your screen when you scan this code"
},
"syncWithMobileComplete": {
"message": "Your data has been synced succesfully. Enjoy the MetaMask mobile app!"
"message": "Your data has been synced successfully. Enjoy the MetaMask mobile app!"
},
"terms": {
"message": "Terms of Use"
Expand Down
12 changes: 12 additions & 0 deletions app/_locales/fr/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,18 @@
"invalidSeedPhrase": {
"message": "Phrase Seed invalide"
},
"ipfsGateway": {
"message": "IPFS Gateway"
},
"ipfsGatewayDescription": {
"message": "Entrez l'URL de la gateway CID IPFS à utiliser pour résoudre les contenus ENS."
},
"invalidIpfsGateway": {
"message": "IPFS Gateway Invalide: la valeur doit être une URL valide"
},
"forbiddenIpfsGateway": {
"message": "IPFS Gateway Interdite: veuillez spécifier une gateway CID"
},
"jsonFile": {
"message": "Fichier JSON",
"description": "format for importing an account"
Expand Down
6 changes: 4 additions & 2 deletions app/scripts/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,10 @@ function setupController (initState, initLangCode) {
},
})

const provider = controller.provider
setupEnsIpfsResolver({ provider })
setupEnsIpfsResolver({
getIpfsGateway: controller.preferencesController.getIpfsGateway.bind(controller.preferencesController),
provider: controller.provider,
})

// submit rpc requests to mesh-metrics
controller.networkController.on('rpc-req', (data) => {
Expand Down
21 changes: 21 additions & 0 deletions app/scripts/controllers/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class PreferencesController {
completedOnboarding: false,
metaMetricsId: null,
metaMetricsSendCount: 0,

// ENS decentralized website resolution
ipfsGateway: 'ipfs.dweb.link',
}, opts.initState)

this.diagnostics = opts.diagnostics
Expand Down Expand Up @@ -608,6 +611,24 @@ class PreferencesController {
return Promise.resolve(true)
}

/**
* A getter for the `ipfsGateway` property
* @returns {string} The current IPFS gateway domain
*/
getIpfsGateway () {
return this.store.getState().ipfsGateway
}

/**
* A setter for the `ipfsGateway` property
* @param {string} domain The new IPFS gateway domain
* @returns {Promise<string>} A promise of the update IPFS gateway domain
*/
setIpfsGateway (domain) {
this.store.updateState({ ipfsGateway: domain })
return Promise.resolve(domain)
}

//
// PRIVATE METHODS
//
Expand Down
7 changes: 6 additions & 1 deletion app/scripts/lib/ens-ipfs/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ async function resolveEnsToIpfsContentId ({ provider, name }) {
if (isEIP1577Compliant[0]) {
const contentLookupResult = await Resolver.contenthash(hash)
const rawContentHash = contentLookupResult[0]
const decodedContentHash = contentHash.decode(rawContentHash)
let decodedContentHash = contentHash.decode(rawContentHash)
const type = contentHash.getCodec(rawContentHash)

if (type === 'ipfs-ns') {
decodedContentHash = contentHash.helpers.cidV0ToV1Base32(decodedContentHash)
}

return { type: type, hash: decodedContentHash }
}
if (isLegacyResolver[0]) {
Expand Down
5 changes: 3 additions & 2 deletions app/scripts/lib/ens-ipfs/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const supportedTopLevelDomains = ['eth']

module.exports = setupEnsIpfsResolver

function setupEnsIpfsResolver ({ provider }) {
function setupEnsIpfsResolver ({ provider, getIpfsGateway }) {

// install listener
const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`)
Expand Down Expand Up @@ -40,12 +40,13 @@ function setupEnsIpfsResolver ({ provider }) {
}

async function attemptResolve ({ tabId, name, path, search, fragment }) {
const ipfsGateway = getIpfsGateway()
extension.tabs.update(tabId, { url: `loading.html` })
let url = `https://app.ens.domains/name/${name}`
try {
const { type, hash } = await resolveEnsToIpfsContentId({ provider, name })
if (type === 'ipfs-ns') {
const resolvedUrl = `https://gateway.ipfs.io/ipfs/${hash}${path}${search || ''}${fragment || ''}`
const resolvedUrl = `https://${hash}.${ipfsGateway}${path}${search || ''}${fragment || ''}`
try {
// check if ipfs gateway has result
const response = await fetch(resolvedUrl, { method: 'HEAD' })
Expand Down
15 changes: 15 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ module.exports = class MetamaskController extends EventEmitter {
setCurrentCurrency: this.setCurrentCurrency.bind(this),
setUseBlockie: this.setUseBlockie.bind(this),
setUseNonceField: this.setUseNonceField.bind(this),
setIpfsGateway: this.setIpfsGateway.bind(this),
setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this),
setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this),
setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this),
Expand Down Expand Up @@ -1841,6 +1842,20 @@ module.exports = class MetamaskController extends EventEmitter {
}
}

/**
* Sets the IPFS gateway to use for ENS content resolution.
* @param {string} val - the host of the gateway to set
* @param {Function} cb - A callback function called when complete.
*/
setIpfsGateway (val, cb) {
try {
this.preferencesController.setIpfsGateway(val)
cb(null)
} catch (err) {
cb(err)
}
}

/**
* Sets whether or not the user will have usage data tracked with MetaMetrics
* @param {boolean} bool - True for users that wish to opt-in, false for users that wish to remain out.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"c3": "^0.6.7",
"classnames": "^2.2.5",
"clone": "^2.1.2",
"content-hash": "^2.4.4",
"content-hash": "^2.5.0",
"copy-to-clipboard": "^3.0.8",
"currency-formatter": "^1.4.2",
"d3": "^5.7.0",
Expand Down
93 changes: 93 additions & 0 deletions ui/app/pages/settings/advanced-tab/advanced-tab.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ export default class AdvancedTab extends PureComponent {
threeBoxSyncingAllowed: PropTypes.bool.isRequired,
setThreeBoxSyncingPermission: PropTypes.func.isRequired,
threeBoxDisabled: PropTypes.bool.isRequired,
setIpfsGateway: PropTypes.func.isRequired,
ipfsGateway: PropTypes.string.isRequired,
}

state = {
autoLogoutTimeLimit: this.props.autoLogoutTimeLimit,
logoutTimeError: '',
ipfsGateway: this.props.ipfsGateway,
ipfsGatewayError: '',
}

renderMobileSync () {
Expand Down Expand Up @@ -354,6 +358,85 @@ export default class AdvancedTab extends PureComponent {
)
}

handleIpfsGatewayChange (url) {
const { t } = this.context

this.setState(() => {
let ipfsGatewayError = ''

try {

const urlObj = new URL(addUrlProtocolPrefix(url))
if (!urlObj.host) {
throw new Error()
}

// don't allow the use of this gateway
if (urlObj.host === 'gateway.ipfs.io') {
throw new Error('Forbidden gateway')
}

} catch (error) {
ipfsGatewayError = (
error.message === 'Forbidden gateway'
? t('forbiddenIpfsGateway')
: t('invalidIpfsGateway')
)
}

return {
ipfsGateway: url,
ipfsGatewayError,
}
})
}

handleIpfsGatewaySave () {

const url = new URL(addUrlProtocolPrefix(this.state.ipfsGateway))
const host = url.host

this.props.setIpfsGateway(host)
}

renderIpfsGatewayControl () {
const { t } = this.context
const { ipfsGatewayError } = this.state

return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<span>{ t('ipfsGateway') }</span>
<div className="settings-page__content-description">
{ t('ipfsGatewayDescription') }
</div>
</div>
<div className="settings-page__content-item">
<div className="settings-page__content-item-col">
<TextField
type="text"
value={this.state.ipfsGateway}
onChange={e => this.handleIpfsGatewayChange(e.target.value)}
error={ipfsGatewayError}
fullWidth
margin="dense"
/>
<Button
type="primary"
className="settings-tab__rpc-save-button"
disabled={Boolean(ipfsGatewayError)}
onClick={() => {
this.handleIpfsGatewaySave()
}}
>
{ t('save') }
</Button>
</div>
</div>
</div>
)
}

renderContent () {
const { warning } = this.props

Expand All @@ -369,6 +452,7 @@ export default class AdvancedTab extends PureComponent {
{ this.renderUseNonceOptIn() }
{ this.renderAutoLogoutTimeLimit() }
{ this.renderThreeBoxControl() }
{ this.renderIpfsGatewayControl() }
</div>
)
}
Expand All @@ -377,3 +461,12 @@ export default class AdvancedTab extends PureComponent {
return this.renderContent()
}
}

function addUrlProtocolPrefix (urlString) {
if (!urlString.match(
/(^http:\/\/)|(^https:\/\/)/
)) {
return 'https://' + urlString
}
return urlString
}
6 changes: 6 additions & 0 deletions ui/app/pages/settings/advanced-tab/advanced-tab.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
setThreeBoxSyncingPermission,
turnThreeBoxSyncingOnAndInitialize,
setUseNonceField,
setIpfsGateway,
} from '../../../store/actions'
import { preferencesSelector } from '../../../selectors/selectors'

Expand All @@ -24,6 +25,7 @@ export const mapStateToProps = state => {
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
ipfsGateway,
} = metamask
const { showFiatInTestnets, autoLogoutTimeLimit } = preferencesSelector(state)

Expand All @@ -36,6 +38,7 @@ export const mapStateToProps = state => {
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
ipfsGateway,
}
}

Expand All @@ -59,6 +62,9 @@ export const mapDispatchToProps = dispatch => {
dispatch(setThreeBoxSyncingPermission(newThreeBoxSyncingState))
}
},
setIpfsGateway: value => {
return dispatch(setIpfsGateway(value))
},
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('AdvancedTab Component', () => {
}
)

assert.equal(root.find('.settings-page__content-row').length, 9)
assert.equal(root.find('.settings-page__content-row').length, 10)
})

it('should update autoLogoutTimeLimit', () => {
Expand Down
4 changes: 4 additions & 0 deletions ui/app/selectors/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,3 +543,7 @@ export function getLastConnectedInfo (state) {
}, {})
return lastConnectedInfoData
}

export function getIpfsGateway (state) {
return state.metamask.ipfsGateway
}
20 changes: 20 additions & 0 deletions ui/app/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ const actions = {
setUseNonceField,
UPDATE_CUSTOM_NONCE: 'UPDATE_CUSTOM_NONCE',
updateCustomNonce,
SET_IPFS_GATEWAY: 'SET_IPFS_GATEWAY',
setIpfsGateway,

SET_PARTICIPATE_IN_METAMETRICS: 'SET_PARTICIPATE_IN_METAMETRICS',
SET_METAMETRICS_SEND_COUNT: 'SET_METAMETRICS_SEND_COUNT',
Expand Down Expand Up @@ -2660,6 +2662,24 @@ function setUseNonceField (val) {
}
}

function setIpfsGateway (val) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`background.setIpfsGateway`)
background.setIpfsGateway(val, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.displayWarning(err.message))
} else {
dispatch({
type: actions.SET_IPFS_GATEWAY,
value: val,
})
}
})
}
}

function updateCurrentLocale (key) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
Expand Down
Loading

0 comments on commit 0ef7f60

Please sign in to comment.