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

iframe communication working

This commit is contained in:
brunobar79 2018-08-11 02:35:20 -04:00
parent 5ef80495cf
commit 78a1cd3314
8 changed files with 214 additions and 72 deletions

View File

@ -48,8 +48,7 @@
"https://*/*" "https://*/*"
], ],
"js": [ "js": [
"contentscript.js", "contentscript.js"
"vendor/ledger/content-script.js"
], ],
"run_at": "document_start", "run_at": "document_start",
"all_frames": true "all_frames": true

View File

@ -68,8 +68,6 @@ initialize().catch(log.error)
// setup metamask mesh testing container // setup metamask mesh testing container
setupMetamaskMeshMetrics() setupMetamaskMeshMetrics()
/** /**
* An object representing a transaction, in whatever state it is in. * An object representing a transaction, in whatever state it is in.
* @typedef TransactionMeta * @typedef TransactionMeta
@ -446,3 +444,4 @@ extension.runtime.onInstalled.addListener(function (details) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
} }
}) })

View File

@ -5,64 +5,178 @@ const {EventEmitter} = require('events')
// HD path differs from eth-hd-keyring - MEW, Parity, Geth and Official Ledger clients use same unusual derivation for Ledger // HD path differs from eth-hd-keyring - MEW, Parity, Geth and Official Ledger clients use same unusual derivation for Ledger
const hdPathString = `m/44'/60'/0'` const hdPathString = `m/44'/60'/0'`
const type = 'Ledger Hardware Keyring' const type = 'Ledger Hardware Keyring'
const ORIGIN = 'http://localhost:9000'
class LedgerKeyring extends EventEmitter { class LedgerKeyring extends EventEmitter {
constructor (opts = {}) { constructor (opts = {}) {
super() super()
this.type = type this.type = type
this.page = 0
this.perPage = 5
this.unlockedAccount = 0
this.paths = {}
this.iframe = null
this.setupIframe()
this.deserialize(opts) this.deserialize(opts)
} }
setupIframe(){
this.iframe = document.createElement('iframe')
this.iframe.src = ORIGIN
console.log('Injecting ledger iframe')
document.head.appendChild(this.iframe)
/*
Passing messages from iframe to background script
*/
console.log('[LEDGER]: LEDGER FROM-IFRAME LISTENER READY')
}
sendMessage(msg, cb) {
console.log('[LEDGER]: SENDING MESSAGE TO IFRAME', msg)
this.iframe.contentWindow.postMessage({...msg, target: 'LEDGER-IFRAME'}, '*')
window.addEventListener('message', event => {
if(event.origin !== ORIGIN) return false
if (event.data && event.data.action && event.data.action.search(name) !== -1) {
console.log('[LEDGER]: GOT MESAGE FROM IFRAME', event.data)
cb(event.data)
}
})
}
serialize () { serialize () {
return Promise.resolve({hdPath: this.hdPath, accounts: this.accounts}) return Promise.resolve({hdPath: this.hdPath, accounts: this.accounts})
} }
deserialize (opts = {}) { deserialize (opts = {}) {
this.hdPath = opts.hdPath || hdPathString this.hdPath = opts.hdPath || hdPathString
this.unlocked = opts.unlocked || false
this.accounts = opts.accounts || [] this.accounts = opts.accounts || []
return Promise.resolve() return Promise.resolve()
} }
async addAccounts (n = 1) { isUnlocked () {
return new Promise((resolve, reject) => { return this.unlocked
extension.runtime.sendMessage({ }
action: 'ledger-add-account',
n,
})
extension.runtime.onMessage.addListener(({action, success, payload}) => { setAccountToUnlock (index) {
if (action === 'ledger-sign-transaction') { this.unlockedAccount = parseInt(index, 10)
}
unlock () {
if (this.isUnlocked()) return Promise.resolve('already unlocked')
return new Promise((resolve, reject) => {
this.sendMessage({
action: 'ledger-unlock',
params: {
hdPath: this.hdPath,
},
},
({action, success, payload}) => {
if (success) { if (success) {
resolve(payload) resolve(payload)
} else { } else {
reject(payload) reject(payload)
} }
}
}) })
}) })
} }
async getAccounts () { async addAccounts (n = 1) {
return this.accounts.slice() return new Promise((resolve, reject) => {
this.unlock()
.then(_ => {
this.sendMessage({
action: 'ledger-add-account',
params: {
n,
},
},
({action, success, payload}) => {
if (success) {
resolve(payload)
} else {
reject(payload)
}
})
})
})
}
getFirstPage () {
this.page = 0
return this.__getPage(1)
}
getNextPage () {
return this.__getPage(1)
}
getPreviousPage () {
return this.__getPage(-1)
}
__getPage (increment) {
this.page += increment
if (this.page <= 0) { this.page = 1 }
return new Promise((resolve, reject) => {
this.unlock()
.then(_ => {
this.sendMessage({
action: 'ledger-get-page',
params: {
page: this.page,
},
},
({action, success, payload}) => {
if (success) {
resolve(payload)
} else {
reject(payload)
}
})
})
})
}
getAccounts () {
return Promise.resolve(this.accounts.slice())
}
removeAccount (address) {
if (!this.accounts.map(a => a.toLowerCase()).includes(address.toLowerCase())) {
throw new Error(`Address ${address} not found in this keyring`)
}
this.accounts = this.accounts.filter(a => a.toLowerCase() !== address.toLowerCase())
} }
// tx is an instance of the ethereumjs-transaction class. // tx is an instance of the ethereumjs-transaction class.
async signTransaction (address, tx) { async signTransaction (address, tx) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
extension.runtime.sendMessage({ this.unlock()
.then(_ => {
console.log('[LEDGER]: sending message ', 'ledger-sign-transaction')
this.sendMessage({
action: 'ledger-sign-transaction', action: 'ledger-sign-transaction',
params: {
address, address,
tx, tx,
}) },
},
extension.runtime.onMessage.addListener(({action, success, payload}) => { ({action, success, payload}) => {
if (action === 'ledger-sign-transaction') {
if (success) { if (success) {
resolve(payload) resolve(payload)
} else { } else {
reject(payload) reject(payload)
} }
} })
}) })
}) })
} }
@ -74,20 +188,23 @@ class LedgerKeyring extends EventEmitter {
// For personal_sign, we need to prefix the message: // For personal_sign, we need to prefix the message:
async signPersonalMessage (withAccount, message) { async signPersonalMessage (withAccount, message) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
extension.runtime.sendMessage({ this.unlock()
.then(_ => {
console.log('[LEDGER]: sending message ', 'ledger-sign-personal-message')
this.sendMessage({
action: 'ledger-sign-personal-message', action: 'ledger-sign-personal-message',
params: {
withAccount, withAccount,
message, message,
}) },
},
extension.runtime.onMessage.addListener(({action, success, payload}) => { ({action, success, payload}) => {
if (action === 'ledger-sign-personal-message') {
if (success) { if (success) {
resolve(payload) resolve(payload)
} else { } else {
reject(payload) reject(payload)
} }
} })
}) })
}) })
} }
@ -99,6 +216,14 @@ class LedgerKeyring extends EventEmitter {
async exportAccount (address) { async exportAccount (address) {
throw new Error('Not supported on this device') throw new Error('Not supported on this device')
} }
forgetDevice () {
this.accounts = []
this.unlocked = false
this.page = 0
this.unlockedAccount = 0
this.paths = {}
}
} }
LedgerKeyring.type = type LedgerKeyring.type = type

View File

@ -0,0 +1,40 @@
const extension = require('extensionizer')
module.exports = setupLedgerIframe
/**
* Injects an iframe into the current document to
* enable the interaction with ledger devices
*/
function setupLedgerIframe () {
const ORIGIN = 'http://localhost:9000'
const ledgerIframe = document.createElement('iframe')
ledgerIframe.src = ORIGIN
console.log('Injecting ledger iframe')
document.head.appendChild(ledgerIframe)
console.log('[LEDGER]: LEDGER BG LISTENER READY')
extension.runtime.onMessage.addListener(({action, params}) => {
console.log('[LEDGER]: GOT MSG FROM THE KEYRING', action, params)
if (action.search('ledger-') !== -1) {
//Forward messages from the keyring to the iframe
sendMessage({action, params})
}
})
function sendMessage(msg) {
ledgerIframe.contentWindow.postMessage({...msg, target: 'LEDGER-IFRAME'}, '*')
}
/*
Passing messages from iframe to background script
*/
console.log('[LEDGER]: LEDGER FROM-IFRAME LISTENER READY')
window.addEventListener('message', event => {
if(event.origin !== ORIGIN) return false
if (event.data && event.data.action && event.data.action.search('ledger-') !== -1) {
// Forward messages from the iframe to the keyring
console.log('[LEDGER] : forwarding msg', event.data)
extension.runtime.sendMessage(event.data)
}
})
}

View File

@ -546,12 +546,11 @@ module.exports = class MetamaskController extends EventEmitter {
keyringName = TrezorKeyring.type keyringName = TrezorKeyring.type
break break
case 'ledger': case 'ledger':
keyringName = TrezorKeyring.type keyringName = LedgerKeyring.type
break break
default: default:
throw new Error('MetamaskController:connectHardware - Unknown device') throw new Error('MetamaskController:connectHardware - Unknown device')
} }
let keyring = await this.keyringController.getKeyringsByType(keyringName)[0] let keyring = await this.keyringController.getKeyringsByType(keyringName)[0]
if (!keyring) { if (!keyring) {
keyring = await this.keyringController.addNewKeyring(keyringName) keyring = await this.keyringController.addNewKeyring(keyringName)
@ -568,10 +567,8 @@ module.exports = class MetamaskController extends EventEmitter {
*/ */
async connectHardware (deviceName, page) { async connectHardware (deviceName, page) {
const oldAccounts = await this.keyringController.getAccounts()
const keyring = await this.getKeyringForDevice(deviceName) const keyring = await this.getKeyringForDevice(deviceName)
let accounts = [] let accounts = []
switch (page) { switch (page) {
case -1: case -1:
accounts = await keyring.getPreviousPage() accounts = await keyring.getPreviousPage()
@ -585,6 +582,7 @@ module.exports = class MetamaskController extends EventEmitter {
// Merge with existing accounts // Merge with existing accounts
// and make sure addresses are not repeated // and make sure addresses are not repeated
const oldAccounts = await this.keyringController.getAccounts()
const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))] const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))]
this.accountTracker.syncWithAddresses(accountsToTrack) this.accountTracker.syncWithAddresses(accountsToTrack)
return accounts return accounts

View File

@ -1,18 +0,0 @@
/*
Passing messages from background script to popup
*/
let port = chrome.runtime.connect({ name: 'ledger' });
port.onMessage.addListener(message => {
window.postMessage(message, window.location.origin);
});
port.onDisconnect.addListener(d => {
port = null;
});
/*
Passing messages from popup to background script
*/
window.addEventListener('message', event => {
if (port && event.source === window && event.data) {
port.postMessage(event.data);
}
});

View File

@ -49,7 +49,6 @@ class ConnectHardwareForm extends Component {
} }
connectToHardwareWallet = (device) => { connectToHardwareWallet = (device) => {
debugger
if (this.state.accounts.length) { if (this.state.accounts.length) {
return null return null
} }