mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'dev' into network-indicator-refactor
This commit is contained in:
commit
4e62ae98bd
@ -6,6 +6,8 @@
|
|||||||
- Add a check for when a tx is included in a block.
|
- Add a check for when a tx is included in a block.
|
||||||
- Minor modifications to network display.
|
- Minor modifications to network display.
|
||||||
- Network now displays properly for pending transactions.
|
- Network now displays properly for pending transactions.
|
||||||
|
- Implement replay attack protections allowed by EIP 155.
|
||||||
|
- Fix bug where sometimes loading account data would fail by querying a future block.
|
||||||
|
|
||||||
## 2.14.1 2016-12-20
|
## 2.14.1 2016-12-20
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ const controller = new MetamaskController({
|
|||||||
setData,
|
setData,
|
||||||
loadData,
|
loadData,
|
||||||
})
|
})
|
||||||
const keyringController = controller.keyringController
|
|
||||||
const txManager = controller.txManager
|
const txManager = controller.txManager
|
||||||
function triggerUi () {
|
function triggerUi () {
|
||||||
if (!popupIsOpen) notification.show()
|
if (!popupIsOpen) notification.show()
|
||||||
@ -81,13 +80,11 @@ function setupControllerConnection (stream) {
|
|||||||
stream.pipe(dnode).pipe(stream)
|
stream.pipe(dnode).pipe(stream)
|
||||||
dnode.on('remote', (remote) => {
|
dnode.on('remote', (remote) => {
|
||||||
// push updates to popup
|
// push updates to popup
|
||||||
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
|
var sendUpdate = remote.sendUpdate.bind(remote)
|
||||||
controller.listeners.push(remote)
|
controller.on('update', sendUpdate)
|
||||||
keyringController.on('update', controller.sendUpdate.bind(controller))
|
|
||||||
|
|
||||||
// teardown on disconnect
|
// teardown on disconnect
|
||||||
eos(stream, () => {
|
eos(stream, () => {
|
||||||
controller.ethStore.removeListener('update', controller.sendUpdate.bind(controller))
|
controller.removeListener('update', sendUpdate)
|
||||||
popupIsOpen = false
|
popupIsOpen = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -316,20 +316,16 @@ module.exports = class KeyringController extends EventEmitter {
|
|||||||
//
|
//
|
||||||
// This method signs tx and returns a promise for
|
// This method signs tx and returns a promise for
|
||||||
// TX Manager to update the state after signing
|
// TX Manager to update the state after signing
|
||||||
signTransaction (ethTx, selectedAddress, txId, cb) {
|
|
||||||
try {
|
signTransaction (ethTx, selectedAddress, txId) {
|
||||||
const address = normalize(selectedAddress)
|
const address = normalize(selectedAddress)
|
||||||
return this.getKeyringForAccount(address)
|
return this.getKeyringForAccount(address)
|
||||||
.then((keyring) => {
|
.then((keyring) => {
|
||||||
return keyring.signTransaction(address, ethTx)
|
return keyring.signTransaction(address, ethTx)
|
||||||
}).then((tx) => {
|
}).then((tx) => {
|
||||||
this.emit(`${txId}:signed`, {tx, txId, cb})
|
return {tx, txId}
|
||||||
})
|
})
|
||||||
} catch (e) {
|
|
||||||
cb(e)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add Unconfirmed Message
|
// Add Unconfirmed Message
|
||||||
// @object msgParams
|
// @object msgParams
|
||||||
// @function cb
|
// @function cb
|
||||||
|
144
app/scripts/lib/eth-store.js
Normal file
144
app/scripts/lib/eth-store.js
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/* Ethereum Store
|
||||||
|
*
|
||||||
|
* This module is responsible for tracking any number of accounts
|
||||||
|
* and caching their current balances & transaction counts.
|
||||||
|
*
|
||||||
|
* It also tracks transaction hashes, and checks their inclusion status
|
||||||
|
* on each new block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const async = require('async')
|
||||||
|
const clone = require('clone')
|
||||||
|
const EthQuery = require('eth-query')
|
||||||
|
|
||||||
|
module.exports = EthereumStore
|
||||||
|
|
||||||
|
|
||||||
|
inherits(EthereumStore, EventEmitter)
|
||||||
|
function EthereumStore(engine) {
|
||||||
|
const self = this
|
||||||
|
EventEmitter.call(self)
|
||||||
|
self._currentState = {
|
||||||
|
accounts: {},
|
||||||
|
transactions: {},
|
||||||
|
}
|
||||||
|
self._query = new EthQuery(engine)
|
||||||
|
|
||||||
|
engine.on('block', self._updateForBlock.bind(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public
|
||||||
|
//
|
||||||
|
|
||||||
|
EthereumStore.prototype.getState = function () {
|
||||||
|
const self = this
|
||||||
|
return clone(self._currentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype.addAccount = function (address) {
|
||||||
|
const self = this
|
||||||
|
self._currentState.accounts[address] = {}
|
||||||
|
self._didUpdate()
|
||||||
|
if (!self.currentBlockNumber) return
|
||||||
|
self._updateAccount(address, noop)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype.removeAccount = function (address) {
|
||||||
|
const self = this
|
||||||
|
delete self._currentState.accounts[address]
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype.addTransaction = function (txHash) {
|
||||||
|
const self = this
|
||||||
|
self._currentState.transactions[txHash] = {}
|
||||||
|
self._didUpdate()
|
||||||
|
if (!self.currentBlockNumber) return
|
||||||
|
self._updateTransaction(self.currentBlockNumber, txHash, noop)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype.removeTransaction = function (address) {
|
||||||
|
const self = this
|
||||||
|
delete self._currentState.transactions[address]
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// private
|
||||||
|
//
|
||||||
|
|
||||||
|
EthereumStore.prototype._didUpdate = function () {
|
||||||
|
const self = this
|
||||||
|
var state = self.getState()
|
||||||
|
self.emit('update', state)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype._updateForBlock = function (block) {
|
||||||
|
const self = this
|
||||||
|
var blockNumber = '0x' + block.number.toString('hex')
|
||||||
|
self.currentBlockNumber = blockNumber
|
||||||
|
async.parallel([
|
||||||
|
self._updateAccounts.bind(self),
|
||||||
|
self._updateTransactions.bind(self, blockNumber),
|
||||||
|
], function (err) {
|
||||||
|
if (err) return console.error(err)
|
||||||
|
self.emit('block', self.getState())
|
||||||
|
self._didUpdate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype._updateAccounts = function (cb) {
|
||||||
|
var accountsState = this._currentState.accounts
|
||||||
|
var addresses = Object.keys(accountsState)
|
||||||
|
async.each(addresses, this._updateAccount.bind(this), cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype._updateAccount = function (address, cb) {
|
||||||
|
var accountsState = this._currentState.accounts
|
||||||
|
this.getAccount(address, function (err, result) {
|
||||||
|
if (err) return cb(err)
|
||||||
|
result.address = address
|
||||||
|
// only populate if the entry is still present
|
||||||
|
if (accountsState[address]) {
|
||||||
|
accountsState[address] = result
|
||||||
|
}
|
||||||
|
cb(null, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype.getAccount = function (address, cb) {
|
||||||
|
const query = this._query
|
||||||
|
async.parallel({
|
||||||
|
balance: query.getBalance.bind(query, address),
|
||||||
|
nonce: query.getTransactionCount.bind(query, address),
|
||||||
|
code: query.getCode.bind(query, address),
|
||||||
|
}, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype._updateTransactions = function (block, cb) {
|
||||||
|
const self = this
|
||||||
|
var transactionsState = self._currentState.transactions
|
||||||
|
var txHashes = Object.keys(transactionsState)
|
||||||
|
async.each(txHashes, self._updateTransaction.bind(self, block), cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthereumStore.prototype._updateTransaction = function (block, txHash, cb) {
|
||||||
|
const self = this
|
||||||
|
// would use the block here to determine how many confirmations the tx has
|
||||||
|
var transactionsState = self._currentState.transactions
|
||||||
|
self._query.getTransaction(txHash, function (err, result) {
|
||||||
|
if (err) return cb(err)
|
||||||
|
// only populate if the entry is still present
|
||||||
|
if (transactionsState[txHash]) {
|
||||||
|
transactionsState[txHash] = result
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
cb(null, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function noop() {}
|
@ -1,5 +1,6 @@
|
|||||||
|
const EventEmitter = require('events')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const EthStore = require('eth-store')
|
const EthStore = require('./lib/eth-store')
|
||||||
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
||||||
const KeyringController = require('./keyring-controller')
|
const KeyringController = require('./keyring-controller')
|
||||||
const NoticeController = require('./notice-controller')
|
const NoticeController = require('./notice-controller')
|
||||||
@ -13,12 +14,12 @@ const autoFaucet = require('./lib/auto-faucet')
|
|||||||
const nodeify = require('./lib/nodeify')
|
const nodeify = require('./lib/nodeify')
|
||||||
const IdStoreMigrator = require('./lib/idStore-migrator')
|
const IdStoreMigrator = require('./lib/idStore-migrator')
|
||||||
|
|
||||||
module.exports = class MetamaskController {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
|
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
|
super()
|
||||||
this.state = { network: 'loading' }
|
this.state = { network: 'loading' }
|
||||||
this.opts = opts
|
this.opts = opts
|
||||||
this.listeners = []
|
|
||||||
this.configManager = new ConfigManager(opts)
|
this.configManager = new ConfigManager(opts)
|
||||||
this.keyringController = new KeyringController({
|
this.keyringController = new KeyringController({
|
||||||
configManager: this.configManager,
|
configManager: this.configManager,
|
||||||
@ -62,6 +63,7 @@ module.exports = class MetamaskController {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.ethStore.on('update', this.sendUpdate.bind(this))
|
this.ethStore.on('update', this.sendUpdate.bind(this))
|
||||||
|
this.keyringController.on('update', this.sendUpdate.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
getState () {
|
getState () {
|
||||||
@ -165,10 +167,7 @@ module.exports = class MetamaskController {
|
|||||||
sendUpdate () {
|
sendUpdate () {
|
||||||
this.getState()
|
this.getState()
|
||||||
.then((state) => {
|
.then((state) => {
|
||||||
|
this.emit('update', state)
|
||||||
this.listeners.forEach((remote) => {
|
|
||||||
remote.sendUpdate(state)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,10 +184,23 @@ module.exports = class MetamaskController {
|
|||||||
},
|
},
|
||||||
// tx signing
|
// tx signing
|
||||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
approveTransaction: this.newUnsignedTransaction.bind(this),
|
||||||
signTransaction: (...args) => {
|
signTransaction: (txParams, cb) => {
|
||||||
this.setupSigningListners(...args)
|
this.txManager.formatTxForSigining(txParams)
|
||||||
this.txManager.formatTxForSigining(...args)
|
.then(({ethTx, address, txId}) => {
|
||||||
|
return this.keyringController.signTransaction(ethTx, address, txId)
|
||||||
|
})
|
||||||
|
.then(({tx, txId}) => {
|
||||||
|
return this.txManager.resolveSignedTransaction({tx, txId})
|
||||||
|
})
|
||||||
|
.then((rawTx) => {
|
||||||
|
cb(null, rawTx)
|
||||||
this.sendUpdate()
|
this.sendUpdate()
|
||||||
|
this.txManager.emit(`${txParams.metamaskId}:signingComplete`)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
cb(err)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// msg signing
|
// msg signing
|
||||||
@ -266,13 +278,6 @@ module.exports = class MetamaskController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSigningListners (txParams) {
|
|
||||||
var txId = txParams.metamaskId
|
|
||||||
// apply event listeners for signing and formating events
|
|
||||||
this.txManager.once(`${txId}:formatted`, this.keyringController.signTransaction.bind(this.keyringController))
|
|
||||||
this.keyringController.once(`${txId}:signed`, this.txManager.resolveSignedTransaction.bind(this.txManager))
|
|
||||||
}
|
|
||||||
|
|
||||||
enforceTxValidations (txParams) {
|
enforceTxValidations (txParams) {
|
||||||
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
|
||||||
const msg = `Invalid transaction value of ${txParams.value} not a positive number.`
|
const msg = `Invalid transaction value of ${txParams.value} not a positive number.`
|
||||||
@ -453,7 +458,7 @@ module.exports = class MetamaskController {
|
|||||||
return this.state.network
|
return this.state.network
|
||||||
}
|
}
|
||||||
|
|
||||||
markAccountsFound(cb) {
|
markAccountsFound (cb) {
|
||||||
this.configManager.setLostAccounts([])
|
this.configManager.setLostAccounts([])
|
||||||
this.sendUpdate()
|
this.sendUpdate()
|
||||||
cb(null, this.getState())
|
cb(null, this.getState())
|
||||||
|
@ -128,7 +128,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
|
|
||||||
approveTransaction (txId, cb = warn) {
|
approveTransaction (txId, cb = warn) {
|
||||||
this.setTxStatusSigned(txId)
|
this.setTxStatusSigned(txId)
|
||||||
cb()
|
this.once(`${txId}:signingComplete`, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelTransaction (txId, cb = warn) {
|
cancelTransaction (txId, cb = warn) {
|
||||||
@ -137,7 +137,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// formats txParams so the keyringController can sign it
|
// formats txParams so the keyringController can sign it
|
||||||
formatTxForSigining (txParams, cb) {
|
formatTxForSigining (txParams) {
|
||||||
var address = txParams.from
|
var address = txParams.from
|
||||||
var metaTx = this.getTx(txParams.metamaskId)
|
var metaTx = this.getTx(txParams.metamaskId)
|
||||||
var gasMultiplier = metaTx.gasMultiplier
|
var gasMultiplier = metaTx.gasMultiplier
|
||||||
@ -153,9 +153,8 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas)
|
txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas)
|
||||||
txParams.nonce = normalize(txParams.nonce)
|
txParams.nonce = normalize(txParams.nonce)
|
||||||
const ethTx = new Transaction(txParams)
|
const ethTx = new Transaction(txParams)
|
||||||
|
var txId = txParams.metamaskId
|
||||||
// listener is assigned in metamaskController
|
return Promise.resolve({ethTx, address, txId})
|
||||||
this.emit(`${txParams.metamaskId}:formatted`, ethTx, address, txParams.metamaskId, cb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// receives a signed tx object and updates the tx hash
|
// receives a signed tx object and updates the tx hash
|
||||||
@ -167,7 +166,8 @@ module.exports = class TransactionManager extends EventEmitter {
|
|||||||
metaTx.hash = txHash
|
metaTx.hash = txHash
|
||||||
this.updateTx(metaTx)
|
this.updateTx(metaTx)
|
||||||
var rawTx = ethUtil.bufferToHex(tx.serialize())
|
var rawTx = ethUtil.bufferToHex(tx.serialize())
|
||||||
cb(null, rawTx)
|
return Promise.resolve(rawTx)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,7 +49,6 @@
|
|||||||
"eth-bin-to-ops": "^1.0.1",
|
"eth-bin-to-ops": "^1.0.1",
|
||||||
"eth-lightwallet": "^2.3.3",
|
"eth-lightwallet": "^2.3.3",
|
||||||
"eth-query": "^1.0.3",
|
"eth-query": "^1.0.3",
|
||||||
"eth-store": "^1.1.0",
|
|
||||||
"ethereumjs-tx": "^1.0.0",
|
"ethereumjs-tx": "^1.0.0",
|
||||||
"ethereumjs-util": "^4.4.0",
|
"ethereumjs-util": "^4.4.0",
|
||||||
"ethereumjs-wallet": "^0.6.0",
|
"ethereumjs-wallet": "^0.6.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user