idmgmt - eth_sign support + notifications

feature/default_network_editable
kumavis 9 years ago
parent 6cc7c08b75
commit 82983e5eff
  1. 20
      app/scripts/background.js
  2. 82
      app/scripts/lib/config-manager.js
  3. 99
      app/scripts/lib/idStore.js
  4. 30
      app/scripts/lib/notifications.js
  5. 4
      package.json

@ -7,7 +7,8 @@ const EthStore = require('eth-store')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const MetaMaskProvider = require('web3-provider-engine/zero.js') const MetaMaskProvider = require('web3-provider-engine/zero.js')
const IdentityStore = require('./lib/idStore') const IdentityStore = require('./lib/idStore')
const createTxNotification = require('./lib/tx-notification.js') const createTxNotification = require('./lib/notifications.js').createTxNotification
const createMsgNotification = require('./lib/notifications.js').createMsgNotification
const configManager = require('./lib/config-manager-singleton') const configManager = require('./lib/config-manager-singleton')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const HostStore = require('./lib/remote-store.js').HostStore const HostStore = require('./lib/remote-store.js').HostStore
@ -55,13 +56,18 @@ var idStore = new IdentityStore()
var providerOpts = { var providerOpts = {
rpcUrl: configManager.getCurrentRpcAddress(), rpcUrl: configManager.getCurrentRpcAddress(),
// account mgmt
getAccounts: function(cb){ getAccounts: function(cb){
var selectedAddress = idStore.getSelectedAddress() var selectedAddress = idStore.getSelectedAddress()
var result = selectedAddress ? [selectedAddress] : [] var result = selectedAddress ? [selectedAddress] : []
cb(null, result) cb(null, result)
}, },
// tx signing
approveTransaction: addUnconfirmedTx, approveTransaction: addUnconfirmedTx,
signTransaction: idStore.signTransaction.bind(idStore), signTransaction: idStore.signTransaction.bind(idStore),
// msg signing
approveMessage: addUnconfirmedMsg,
signMessage: idStore.signMessage.bind(idStore),
} }
var provider = MetaMaskProvider(providerOpts) var provider = MetaMaskProvider(providerOpts)
var web3 = new Web3(provider) var web3 = new Web3(provider)
@ -209,7 +215,7 @@ function updateBadge(state){
} }
// //
// Add unconfirmed Tx // Add unconfirmed Tx + Msg
// //
function addUnconfirmedTx(txParams, cb){ function addUnconfirmedTx(txParams, cb){
@ -222,6 +228,16 @@ function addUnconfirmedTx(txParams, cb){
}) })
} }
function addUnconfirmedMsg(msgParams, cb){
var msgId = idStore.addUnconfirmedMessage(msgParams, cb)
createMsgNotification({
title: 'New Unsigned Message',
msgParams: msgParams,
confirm: idStore.approveMessage.bind(idStore, msgId, noop),
cancel: idStore.cancelMessage.bind(idStore, msgId),
})
}
// //
// config // config
// //

@ -145,15 +145,25 @@ ConfigManager.prototype.setData = function(data) {
this.migrator.saveData(data) this.migrator.saveData(data)
} }
//
// Tx
//
ConfigManager.prototype.getTxList = function() { ConfigManager.prototype.getTxList = function() {
var data = this.migrator.getData() var data = this.migrator.getData()
if ('transactions' in data) { if (data.transactions !== undefined) {
return data.transactions return data.transactions
} else { } else {
return [] return []
} }
} }
ConfigManager.prototype.unconfirmedTxs = function() {
var transactions = this.getTxList()
return transactions.filter(tx => tx.status === 'unconfirmed')
.reduce((result, tx) => { result[tx.id] = tx; return result }, {})
}
ConfigManager.prototype._saveTxList = function(txList) { ConfigManager.prototype._saveTxList = function(txList) {
var data = this.migrator.getData() var data = this.migrator.getData()
data.transactions = txList data.transactions = txList
@ -201,12 +211,74 @@ ConfigManager.prototype.updateTx = function(tx) {
this._saveTxList(transactions) this._saveTxList(transactions)
} }
ConfigManager.prototype.unconfirmedTxs = function() { //
var transactions = this.getTxList() // Msg
return transactions.filter(tx => tx.status === 'unconfirmed') //
.reduce((result, tx) => { result[tx.id] = tx; return result }, {})
ConfigManager.prototype.getMsgList = function() {
var data = this.migrator.getData()
if (data.messages !== undefined) {
return data.messages
} else {
return []
}
}
ConfigManager.prototype.unconfirmedMsgs = function() {
var messages = this.getMsgList()
return messages.filter(msg => msg.status === 'unconfirmed')
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
}
ConfigManager.prototype._saveMsgList = function(msgList) {
var data = this.migrator.getData()
data.messages = msgList
this.setData(data)
} }
ConfigManager.prototype.addMsg = function(msg) {
var messages = this.getMsgList()
messages.push(msg)
this._saveMsgList(messages)
}
ConfigManager.prototype.getMsg = function(msgId) {
var messages = this.getMsgList()
var matching = messages.filter(msg => msg.id === msgId)
return matching.length > 0 ? matching[0] : null
}
ConfigManager.prototype.confirmMsg = function(msgId) {
this._setMsgStatus(msgId, 'confirmed')
}
ConfigManager.prototype.rejectMsg = function(msgId) {
this._setMsgStatus(msgId, 'rejected')
}
ConfigManager.prototype._setMsgStatus = function(msgId, status) {
var msg = this.getMsg(msgId)
msg.status = status
this.updateMsg(msg)
}
ConfigManager.prototype.updateMsg = function(msg) {
var messages = this.getMsgList()
var found, index
messages.forEach((otherMsg, i) => {
if (otherMsg.id === msg.id) {
found = true
index = i
}
})
if (found) {
messages[index] = msg
}
this._saveMsgList(messages)
}
// observable // observable
ConfigManager.prototype.subscribe = function(fn){ ConfigManager.prototype.subscribe = function(fn){

@ -34,6 +34,7 @@ function IdentityStore(opts = {}) {
} }
// not part of serilized metamask state - only kept in memory // not part of serilized metamask state - only kept in memory
this._unconfTxCbs = {} this._unconfTxCbs = {}
this._unconfMsgCbs = {}
} }
// //
@ -140,6 +141,10 @@ IdentityStore.prototype.exportAccount = function(address, cb) {
cb(null, privateKey) cb(null, privateKey)
} }
//
// Transactions
//
// comes from dapp via zero-client hooked-wallet provider // comes from dapp via zero-client hooked-wallet provider
IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){ IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){
@ -170,7 +175,6 @@ IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){
// comes from metamask ui // comes from metamask ui
IdentityStore.prototype.approveTransaction = function(txId, cb){ IdentityStore.prototype.approveTransaction = function(txId, cb){
var txData = configManager.getTx(txId) var txData = configManager.getTx(txId)
var txParams = txData.txParams
var approvalCb = this._unconfTxCbs[txId] || noop var approvalCb = this._unconfTxCbs[txId] || noop
// accept tx // accept tx
@ -206,6 +210,73 @@ IdentityStore.prototype.signTransaction = function(txParams, cb){
} }
} }
//
// Messages
//
// comes from dapp via zero-client hooked-wallet provider
IdentityStore.prototype.addUnconfirmedMessage = function(msgParams, cb){
// create txData obj with parameters and meta data
var time = (new Date()).getTime()
var msgId = createId()
var msgData = {
id: msgId,
msgParams: msgParams,
time: time,
status: 'unconfirmed',
}
configManager.addMsg(msgData)
console.log('addUnconfirmedMessage:', msgData)
// keep the cb around for after approval (requires user interaction)
// This cb fires completion to the Dapp's write operation.
this._unconfMsgCbs[msgId] = cb
// signal update
this._didUpdate()
return msgId
}
// comes from metamask ui
IdentityStore.prototype.approveMessage = function(msgId, cb){
var msgData = configManager.getMsg(msgId)
var approvalCb = this._unconfMsgCbs[msgId] || noop
// accept msg
cb()
approvalCb(null, true)
// clean up
configManager.confirmMsg(msgId)
delete this._unconfMsgCbs[msgId]
this._didUpdate()
}
// comes from metamask ui
IdentityStore.prototype.cancelMessage = function(msgId){
var txData = configManager.getMsg(msgId)
var approvalCb = this._unconfMsgCbs[msgId] || noop
// reject tx
approvalCb(null, false)
// clean up
configManager.rejectMsg(msgId)
delete this._unconfTxCbs[msgId]
this._didUpdate()
}
// performs the actual signing, no autofill of params
IdentityStore.prototype.signMessage = function(msgParams, cb){
try {
console.log('signing msg...', msgParams.data)
var rawMsg = this._idmgmt.signMsg(msgParams.from, msgParams.data)
cb(null, rawMsg)
} catch (err) {
cb(err)
}
}
// //
// private // private
// //
@ -352,7 +423,7 @@ function IdManagement(opts) {
var tx = new Transaction(txParams) var tx = new Transaction(txParams)
// sign tx // sign tx
var privKeyHex = ethUtil.addHexPrefix(this.keyStore.exportPrivateKey(txParams.from, this.derivedKey, this.hdPathString)) var privKeyHex = this.exportPrivateKey(txParams.from)
var privKey = ethUtil.toBuffer(privKeyHex) var privKey = ethUtil.toBuffer(privKeyHex)
tx.sign(privKey) tx.sign(privKey)
@ -367,12 +438,23 @@ function IdManagement(opts) {
return rawTx return rawTx
} }
this.signMsg = function(address, message){
// sign message
var privKeyHex = this.exportPrivateKey(address)
var privKey = ethUtil.toBuffer(privKeyHex)
var msgHash = ethUtil.sha3(message)
var msgSig = ethUtil.ecsign(msgHash, privKey)
var rawMsgSig = ethUtil.bufferToHex(concatSig(msgSig.v, msgSig.r, msgSig.s))
return rawMsgSig
}
this.getSeed = function(){ this.getSeed = function(){
return this.keyStore.getSeed(this.derivedKey) return this.keyStore.getSeed(this.derivedKey)
} }
this.exportPrivateKey = function(address) { this.exportPrivateKey = function(address) {
return this.keyStore.exportPrivateKey(address, this.derivedKey, this.hdPathString) var privKeyHex = ethUtil.addHexPrefix(this.keyStore.exportPrivateKey(address, this.derivedKey, this.hdPathString))
return privKeyHex
} }
} }
@ -380,3 +462,14 @@ function IdManagement(opts) {
// util // util
function noop(){} function noop(){}
function concatSig(v, r, s) {
r = ethUtil.fromSigned(r)
s = ethUtil.fromSigned(s)
v = ethUtil.bufferToInt(v)
r = ethUtil.toUnsigned(r).toString('hex')
s = ethUtil.toUnsigned(s).toString('hex')
v = ethUtil.stripHexPrefix(ethUtil.intToHex(v))
return ethUtil.addHexPrefix(r.concat(s, v).toString("hex"))
}

@ -2,8 +2,10 @@ const createId = require('hat')
const uiUtils = require('../../../ui/app/util') const uiUtils = require('../../../ui/app/util')
var notificationHandlers = {} var notificationHandlers = {}
module.exports = createTxNotification module.exports = {
createTxNotification: createTxNotification,
createMsgNotification: createMsgNotification,
}
// notification button press // notification button press
chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){ chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
@ -47,3 +49,27 @@ function createTxNotification(opts){
cancel: opts.cancel, cancel: opts.cancel,
} }
} }
function createMsgNotification(opts){
var message = [
'to be signed by: '+uiUtils.addressSummary(opts.msgParams.from),
'message:\n'+opts.msgParams.data,
].join('\n')
var id = createId()
chrome.notifications.create(id, {
type: 'basic',
iconUrl: '/images/icon-128.png',
title: opts.title,
message: message,
buttons: [{
title: 'confirm',
},{
title: 'cancel',
}]
})
notificationHandlers[id] = {
confirm: opts.confirm,
cancel: opts.cancel,
}
}

@ -32,7 +32,7 @@
"eth-lightwallet": "^2.2.2", "eth-lightwallet": "^2.2.2",
"eth-store": "^1.1.0", "eth-store": "^1.1.0",
"ethereumjs-tx": "^1.0.0", "ethereumjs-tx": "^1.0.0",
"ethereumjs-util": "^4.3.0", "ethereumjs-util": "^4.4.0",
"hat": "0.0.3", "hat": "0.0.3",
"inject-css": "^0.1.1", "inject-css": "^0.1.1",
"metamask-logo": "^1.1.5", "metamask-logo": "^1.1.5",
@ -53,7 +53,7 @@
"three.js": "^0.73.2", "three.js": "^0.73.2",
"through2": "^2.0.1", "through2": "^2.0.1",
"web3": "^0.15.1", "web3": "^0.15.1",
"web3-provider-engine": "^7.6.1", "web3-provider-engine": "^7.6.2",
"xtend": "^4.0.1" "xtend": "^4.0.1"
}, },
"devDependencies": { "devDependencies": {

Loading…
Cancel
Save