1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Ipfs cid v1 base32 ()

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
This commit is contained in:
pldespaigne 2019-12-12 20:28:07 +01:00 committed by Erik Marks
parent f49bc58c09
commit 0ef7f603d6
14 changed files with 205 additions and 24 deletions

View File

@ -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"
@ -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"
@ -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"

View File

@ -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"

View File

@ -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) => {

View File

@ -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
@ -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
//

View File

@ -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]) {

View File

@ -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}/*`)
@ -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' })

View File

@ -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),
@ -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.

View File

@ -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",

View File

@ -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 () {
@ -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
@ -369,6 +452,7 @@ export default class AdvancedTab extends PureComponent {
{ this.renderUseNonceOptIn() }
{ this.renderAutoLogoutTimeLimit() }
{ this.renderThreeBoxControl() }
{ this.renderIpfsGatewayControl() }
</div>
)
}
@ -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
}

View File

@ -11,6 +11,7 @@ import {
setThreeBoxSyncingPermission,
turnThreeBoxSyncingOnAndInitialize,
setUseNonceField,
setIpfsGateway,
} from '../../../store/actions'
import { preferencesSelector } from '../../../selectors/selectors'
@ -24,6 +25,7 @@ export const mapStateToProps = state => {
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
ipfsGateway,
} = metamask
const { showFiatInTestnets, autoLogoutTimeLimit } = preferencesSelector(state)
@ -36,6 +38,7 @@ export const mapStateToProps = state => {
threeBoxSyncingAllowed,
threeBoxDisabled,
useNonceField,
ipfsGateway,
}
}
@ -59,6 +62,9 @@ export const mapDispatchToProps = dispatch => {
dispatch(setThreeBoxSyncingPermission(newThreeBoxSyncingState))
}
},
setIpfsGateway: value => {
return dispatch(setIpfsGateway(value))
},
}
}

View File

@ -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', () => {

View File

@ -543,3 +543,7 @@ export function getLastConnectedInfo (state) {
}, {})
return lastConnectedInfoData
}
export function getIpfsGateway (state) {
return state.metamask.ipfsGateway
}

View File

@ -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',
@ -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())

View File

@ -6826,16 +6826,6 @@ cids@^0.5.3, cids@~0.5.4, cids@~0.5.6:
multicodec "~0.5.0"
multihashes "~0.4.14"
cids@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cids/-/cids-0.6.0.tgz#0de7056a5246a7c7ebf3134eb4d83b3b8b841a06"
integrity sha512-34wuIeiBZOuvBwUuYR4XooVuXUQI2PYU9VmgM2eB3xkSmQYRlv2kh/dIbmGiLY2GuONlGR3lLtYdVkx1G9yXUg==
dependencies:
class-is "^1.1.0"
multibase "~0.6.0"
multicodec "~0.5.0"
multihashes "~0.4.14"
cids@^0.7.1, cids@~0.7.0, cids@~0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.1.tgz#d8bba49a35a0e82110879b5001abf1039c62347f"
@ -7413,12 +7403,12 @@ content-disposition@0.5.3, content-disposition@^0.5.2, content-disposition@~0.5.
dependencies:
safe-buffer "5.1.2"
content-hash@^2.4.4:
version "2.4.4"
resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.4.4.tgz#4bec87caecfcff8cf1a37645301cbef4728a083f"
integrity sha512-3FaUsqt7VR725pVxe0vIScGI5efmpryIXdVSeXafQ63fb5gRGparAQlAGxTSOiv0yRg7YeliseXuB20ByD1duQ==
content-hash@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.0.tgz#6f8a98feec0360f09213e951c69e57c727093e3a"
integrity sha512-Opn9YSjQQc3Wst/VPwGTdKcNONuQr4yc0dtvEbV82UGDIsc7dE2bdTz3FpLRxfnuOkHx8y9/94sZ3aytmUQ1HA==
dependencies:
cids "^0.6.0"
cids "^0.7.1"
multicodec "^0.5.5"
multihashes "^0.4.15"