mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Merge branch 'master' into Identicon
This commit is contained in:
commit
118da12197
@ -7,6 +7,7 @@
|
||||
- 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.
|
||||
- Account icons are now "identicons" (deterministically generated from the address).
|
||||
- Fixed link to Slack channel.
|
||||
|
||||
# 1.6.0 2016-04-22
|
||||
|
||||
|
@ -7,7 +7,8 @@ const EthStore = require('eth-store')
|
||||
const PortStream = require('./lib/port-stream.js')
|
||||
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
||||
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 setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
const HostStore = require('./lib/remote-store.js').HostStore
|
||||
@ -55,13 +56,18 @@ var idStore = new IdentityStore()
|
||||
|
||||
var providerOpts = {
|
||||
rpcUrl: configManager.getCurrentRpcAddress(),
|
||||
// account mgmt
|
||||
getAccounts: function(cb){
|
||||
var selectedAddress = idStore.getSelectedAddress()
|
||||
var result = selectedAddress ? [selectedAddress] : []
|
||||
cb(null, result)
|
||||
},
|
||||
// tx signing
|
||||
approveTransaction: addUnconfirmedTx,
|
||||
signTransaction: idStore.signTransaction.bind(idStore),
|
||||
// msg signing
|
||||
approveMessage: addUnconfirmedMsg,
|
||||
signMessage: idStore.signMessage.bind(idStore),
|
||||
}
|
||||
var provider = MetaMaskProvider(providerOpts)
|
||||
var web3 = new Web3(provider)
|
||||
@ -131,7 +137,10 @@ function onRpcRequest(remoteStream, payload){
|
||||
// console.log('MetaMaskPlugin - incoming payload:', payload)
|
||||
provider.sendAsync(payload, function onPayloadHandled(err, response){
|
||||
// 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 {
|
||||
remoteStream.write(response)
|
||||
} catch (err) {
|
||||
@ -206,7 +215,7 @@ function updateBadge(state){
|
||||
}
|
||||
|
||||
//
|
||||
// Add unconfirmed Tx
|
||||
// Add unconfirmed Tx + Msg
|
||||
//
|
||||
|
||||
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
|
||||
//
|
||||
|
@ -145,15 +145,25 @@ ConfigManager.prototype.setData = function(data) {
|
||||
this.migrator.saveData(data)
|
||||
}
|
||||
|
||||
//
|
||||
// Tx
|
||||
//
|
||||
|
||||
ConfigManager.prototype.getTxList = function() {
|
||||
var data = this.migrator.getData()
|
||||
if ('transactions' in data) {
|
||||
if (data.transactions !== undefined) {
|
||||
return data.transactions
|
||||
} else {
|
||||
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) {
|
||||
var data = this.migrator.getData()
|
||||
data.transactions = txList
|
||||
@ -201,11 +211,73 @@ ConfigManager.prototype.updateTx = function(tx) {
|
||||
this._saveTxList(transactions)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.unconfirmedTxs = function() {
|
||||
var transactions = this.getTxList()
|
||||
return transactions.filter(tx => tx.status === 'unconfirmed')
|
||||
.reduce((result, tx) => { result[tx.id] = tx; return result }, {})
|
||||
//
|
||||
// Msg
|
||||
//
|
||||
|
||||
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
|
||||
|
||||
|
@ -34,6 +34,7 @@ function IdentityStore(opts = {}) {
|
||||
}
|
||||
// not part of serilized metamask state - only kept in memory
|
||||
this._unconfTxCbs = {}
|
||||
this._unconfMsgCbs = {}
|
||||
}
|
||||
|
||||
//
|
||||
@ -140,6 +141,10 @@ IdentityStore.prototype.exportAccount = function(address, cb) {
|
||||
cb(null, privateKey)
|
||||
}
|
||||
|
||||
//
|
||||
// Transactions
|
||||
//
|
||||
|
||||
// comes from dapp via zero-client hooked-wallet provider
|
||||
IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){
|
||||
|
||||
@ -170,7 +175,6 @@ IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){
|
||||
// comes from metamask ui
|
||||
IdentityStore.prototype.approveTransaction = function(txId, cb){
|
||||
var txData = configManager.getTx(txId)
|
||||
var txParams = txData.txParams
|
||||
var approvalCb = this._unconfTxCbs[txId] || noop
|
||||
|
||||
// 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
|
||||
//
|
||||
@ -351,14 +422,30 @@ function IdManagement(opts) {
|
||||
txParams.nonce = ethUtil.addHexPrefix(txParams.nonce)
|
||||
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
|
||||
var hash = '0x' + tx.hash().toString('hex')
|
||||
var txHash = ethUtil.bufferToHex(tx.hash())
|
||||
var metaTx = configManager.getTx(txParams.metamaskId)
|
||||
metaTx.hash = hash
|
||||
metaTx.hash = txHash
|
||||
configManager.updateTx(metaTx)
|
||||
|
||||
var rawTx = '0x'+tx.serialize().toString('hex')
|
||||
return '0x'+LightwalletSigner.signTx(this.keyStore, this.derivedKey, rawTx, txParams.from, this.hdPathString)
|
||||
// return raw serialized tx
|
||||
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(){
|
||||
@ -366,7 +453,8 @@ function IdManagement(opts) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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')
|
||||
var notificationHandlers = {}
|
||||
|
||||
module.exports = createTxNotification
|
||||
|
||||
module.exports = {
|
||||
createTxNotification: createTxNotification,
|
||||
createMsgNotification: createMsgNotification,
|
||||
}
|
||||
|
||||
// notification button press
|
||||
chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
|
||||
@ -47,3 +49,27 @@ function createTxNotification(opts){
|
||||
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-store": "^1.1.0",
|
||||
"ethereumjs-tx": "^1.0.0",
|
||||
"ethereumjs-util": "^4.3.0",
|
||||
"ethereumjs-util": "^4.4.0",
|
||||
"hat": "0.0.3",
|
||||
"identicon.js": "^1.2.1",
|
||||
"inject-css": "^0.1.1",
|
||||
@ -54,7 +54,7 @@
|
||||
"three.js": "^0.73.2",
|
||||
"through2": "^2.0.1",
|
||||
"web3": "^0.15.1",
|
||||
"web3-provider-engine": "^7.5.0",
|
||||
"web3-provider-engine": "^7.6.2",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -42,7 +42,7 @@ InfoScreen.prototype.render = function() {
|
||||
|
||||
h('div', [
|
||||
h('a', {
|
||||
href: 'https://consensys.slack.com/archives/team-metamask',
|
||||
href: 'http://slack.metamask.io',
|
||||
target: '_blank',
|
||||
onClick(event) { this.navigateTo(event.target.href) },
|
||||
}, 'Join the conversation on Slack'),
|
||||
|
Loading…
Reference in New Issue
Block a user