Merge branch 'master' into Identicon

feature/default_network_editable
Dan Finlay 9 years ago
commit 118da12197
  1. 1
      CHANGELOG.md
  2. 25
      app/scripts/background.js
  3. 82
      app/scripts/lib/config-manager.js
  4. 111
      app/scripts/lib/idStore.js
  5. 30
      app/scripts/lib/notifications.js
  6. 4
      package.json
  7. 2
      ui/app/info.js

@ -7,6 +7,7 @@
- Clicking accounts in the account list now both selects that account and displays that account's detail view. - Clicking accounts in the account list now both selects that account and displays that account's detail view.
- Selected account is now persisted between sessions, so the current account stays selected. - Selected account is now persisted between sessions, so the current account stays selected.
- Account icons are now "identicons" (deterministically generated from the address). - Account icons are now "identicons" (deterministically generated from the address).
- Fixed link to Slack channel.
# 1.6.0 2016-04-22 # 1.6.0 2016-04-22

@ -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)
@ -131,7 +137,10 @@ function onRpcRequest(remoteStream, payload){
// console.log('MetaMaskPlugin - incoming payload:', payload) // console.log('MetaMaskPlugin - incoming payload:', payload)
provider.sendAsync(payload, function onPayloadHandled(err, response){ provider.sendAsync(payload, function onPayloadHandled(err, response){
// provider engine errors are included in response objects // provider engine errors are included in response objects
if (!payload.isMetamaskInternal) console.log('MetaMaskPlugin - RPC complete:', payload, '->', response) if (!payload.isMetamaskInternal) {
console.log('MetaMaskPlugin - RPC complete:', payload, '->', response)
if (response.error) console.error('Error in RPC response:\n'+response.error.message)
}
try { try {
remoteStream.write(response) remoteStream.write(response)
} catch (err) { } catch (err) {
@ -206,7 +215,7 @@ function updateBadge(state){
} }
// //
// Add unconfirmed Tx // Add unconfirmed Tx + Msg
// //
function addUnconfirmedTx(txParams, cb){ function addUnconfirmedTx(txParams, cb){
@ -219,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
// //
@ -351,14 +422,30 @@ function IdManagement(opts) {
txParams.nonce = ethUtil.addHexPrefix(txParams.nonce) txParams.nonce = ethUtil.addHexPrefix(txParams.nonce)
var tx = new Transaction(txParams) var tx = new Transaction(txParams)
// sign tx
var privKeyHex = this.exportPrivateKey(txParams.from)
var privKey = ethUtil.toBuffer(privKeyHex)
tx.sign(privKey)
// Add the tx hash to the persisted meta-tx object // Add the tx hash to the persisted meta-tx object
var hash = '0x' + tx.hash().toString('hex') var txHash = ethUtil.bufferToHex(tx.hash())
var metaTx = configManager.getTx(txParams.metamaskId) var metaTx = configManager.getTx(txParams.metamaskId)
metaTx.hash = hash metaTx.hash = txHash
configManager.updateTx(metaTx) configManager.updateTx(metaTx)
var rawTx = '0x'+tx.serialize().toString('hex') // return raw serialized tx
return '0x'+LightwalletSigner.signTx(this.keyStore, this.derivedKey, rawTx, txParams.from, this.hdPathString) var rawTx = ethUtil.bufferToHex(tx.serialize())
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(){
@ -366,7 +453,8 @@ function IdManagement(opts) {
} }
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
} }
} }
@ -374,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",
"identicon.js": "^1.2.1", "identicon.js": "^1.2.1",
"inject-css": "^0.1.1", "inject-css": "^0.1.1",
@ -54,7 +54,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.5.0", "web3-provider-engine": "^7.6.2",
"xtend": "^4.0.1" "xtend": "^4.0.1"
}, },
"devDependencies": { "devDependencies": {

@ -42,7 +42,7 @@ InfoScreen.prototype.render = function() {
h('div', [ h('div', [
h('a', { h('a', {
href: 'https://consensys.slack.com/archives/team-metamask', href: 'http://slack.metamask.io',
target: '_blank', target: '_blank',
onClick(event) { this.navigateTo(event.target.href) }, onClick(event) { this.navigateTo(event.target.href) },
}, 'Join the conversation on Slack'), }, 'Join the conversation on Slack'),

Loading…
Cancel
Save