migrate to ProviderEngine zero-client

This commit is contained in:
kumavis 2015-12-18 22:05:16 -08:00
parent cc935a1eba
commit 72a747165d
7 changed files with 286 additions and 172 deletions

View File

@ -1,7 +1,10 @@
const ZeroClientProvider = require('web3-provider-skeleton')
// const PortStream = require('./lib/port-stream.js')
const identitiesUrl = 'https://alpha.metamask.io/identities/' const identitiesUrl = 'https://alpha.metamask.io/identities/'
const messagingChannelName = 'metamask'
var unsignedTxs = {} // var unsignedTxs = {}
var zeroClient = ZeroClientProvider()
// setup badge click handler // setup badge click handler
chrome.browserAction.onClicked.addListener(function(activeTab) { chrome.browserAction.onClicked.addListener(function(activeTab) {
@ -10,118 +13,131 @@ chrome.browserAction.onClicked.addListener(function(activeTab) {
// setup messaging // setup messaging
chrome.runtime.onConnect.addListener(connectRemote) chrome.runtime.onConnect.addListener(connectRemote)
chrome.runtime.onConnectExternal.addListener(connectRemote) // chrome.runtime.onConnectExternal.addListener(connectRemote)
function connectRemote(remote){ function connectRemote(remotePort){
remote.onMessage.addListener(handleMessage) remotePort.onMessage.addListener(onRpcRequest.bind(null, remotePort))
exportUnsignedTxs(remote)
} }
// load from storage function onRpcRequest(remotePort, payload){
chrome.storage.sync.get(function(data){ zeroClient.sendAsync(payload, function onPayloadHandled(err, response){
for (var key in data) { if (err) throw err
var serialized = data[key] // console.log('MetaMaskPlugin - RPC complete:', payload, '->', response)
var tx = deserializeTx(serialized) // if (typeof response !== 'object') {
var hash = simpleHash(serialized) // if (!response) {
unsignedTxs[hash] = tx // console.warn('-------------------------------')
} // console.warn(payload, '->', response)
updateBadge() // console.warn('-------------------------------')
}) // }
remotePort.postMessage(response)
})
}
// listen to storage changes // // load from storage
chrome.storage.onChanged.addListener(function(changes, namespace) { // chrome.storage.sync.get(function(data){
for (key in changes) { // for (var key in data) {
var storageChange = changes[key] // var serialized = data[key]
if (storageChange.oldValue && !storageChange.newValue) { // var tx = deserializeTx(serialized)
// was removed // var hash = simpleHash(serialized)
removeTransaction(storageChange.oldValue) // unsignedTxs[hash] = tx
} else if (!storageChange.oldValue && storageChange.newValue) { // }
// was added // updateBadge()
addTransaction(deserializeTx(storageChange.newValue)) // })
}
} // // listen to storage changes
}) // chrome.storage.onChanged.addListener(function(changes, namespace) {
// for (key in changes) {
// var storageChange = changes[key]
// if (storageChange.oldValue && !storageChange.newValue) {
// // was removed
// removeTransaction(storageChange.oldValue)
// } else if (!storageChange.oldValue && storageChange.newValue) {
// // was added
// addTransaction(deserializeTx(storageChange.newValue))
// }
// }
// })
// setup badge text // setup badge text
updateBadge() // updateBadge()
function handleMessage(msg){ // function updateBadge(){
console.log('got message!', msg.type) // var label = ''
switch(msg.type){ // var count = Object.keys(unsignedTxs).length
// if (count) {
// label = String(count)
// }
// chrome.browserAction.setBadgeText({text: label})
// chrome.browserAction.setBadgeBackgroundColor({color: '#506F8B'})
// }
// function handleMessage(msg){
// console.log('got message!', msg.type)
// switch(msg.type){
case 'addUnsignedTx': // case 'addUnsignedTx':
addTransaction(msg.payload) // addTransaction(msg.payload)
return // return
case 'removeUnsignedTx': // case 'removeUnsignedTx':
removeTransaction(msg.payload) // removeTransaction(msg.payload)
return // return
} // }
} // }
function addTransaction(tx){ // function addTransaction(tx){
var serialized = serializeTx(tx) // var serialized = serializeTx(tx)
var hash = simpleHash(serialized) // var hash = simpleHash(serialized)
unsignedTxs[hash] = tx // unsignedTxs[hash] = tx
var data = {} // var data = {}
data[hash] = serialized // data[hash] = serialized
chrome.storage.sync.set(data) // chrome.storage.sync.set(data)
// trigger ui changes // // trigger ui changes
updateBadge() // updateBadge()
} // }
function removeTransaction(serialized){ // function removeTransaction(serialized){
var hash = simpleHash(serialized) // var hash = simpleHash(serialized)
delete unsignedTxs[hash] // delete unsignedTxs[hash]
var data = {} // var data = {}
data[hash] = undefined // data[hash] = undefined
chrome.storage.sync.set(data) // chrome.storage.sync.set(data)
// trigger ui changes // // trigger ui changes
updateBadge() // updateBadge()
} // }
function exportUnsignedTxs(remote){ // function exportUnsignedTxs(remote){
console.log('exporting txs!', unsignedTxs) // console.log('exporting txs!', unsignedTxs)
var data = { // var data = {
type: 'importUnsignedTxs', // type: 'importUnsignedTxs',
payload: getValues(unsignedTxs), // payload: getValues(unsignedTxs),
} // }
remote.postMessage(data) // remote.postMessage(data)
} // }
function updateBadge(){ // function simpleHash(input) {
var label = '' // var hash = 0, i, chr, len
var count = Object.keys(unsignedTxs).length // if (input.length == 0) return hash
if (count) { // for (i = 0, len = input.length; i < len; i++) {
label = String(count) // chr = input.charCodeAt(i)
} // hash = ((hash << 5) - hash) + chr
chrome.browserAction.setBadgeText({text: label}) // hash |= 0 // Convert to 32bit integer
chrome.browserAction.setBadgeBackgroundColor({color: '#506F8B'}) // }
} // return hash
// }
function simpleHash(input) { // function serializeTx(tx){
var hash = 0, i, chr, len // return JSON.stringify(tx)
if (input.length == 0) return hash // }
for (i = 0, len = input.length; i < len; i++) {
chr = input.charCodeAt(i)
hash = ((hash << 5) - hash) + chr
hash |= 0 // Convert to 32bit integer
}
return hash
}
function serializeTx(tx){ // function deserializeTx(tx){
return JSON.stringify(tx) // return JSON.parse(tx)
} // }
function deserializeTx(tx){ // function getValues(obj){
return JSON.parse(tx) // var output = []
} // for (var key in obj) {
// output.push(obj[key])
function getValues(obj){ // }
var output = [] // return output
for (var key in obj) { // }
output.push(obj[key])
}
return output
}

View File

@ -1,6 +1,7 @@
const allowedMessageTarget = 'metamask' const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
const allowedMessageType = 'addUnsignedTx' const PortStream = require('./lib/port-stream.js')
console.log('content script!')
// inject in-page script // inject in-page script
var scriptTag = document.createElement('script') var scriptTag = document.createElement('script')
@ -9,17 +10,14 @@ scriptTag.onload = function() { this.parentNode.removeChild(this) }
var container = document.head || document.documentElement var container = document.head || document.documentElement
container.appendChild(scriptTag) container.appendChild(scriptTag)
// setup connection with background // setup communication to page and plugin
var metamaskPlugin = chrome.runtime.connect({name: 'metamask'}) var pageStream = new LocalMessageDuplexStream({
name: 'contentscript',
target: 'inpage',
})
var pluginPort = chrome.runtime.connect({name: 'metamask'})
var pluginStream = new PortStream(pluginPort)
// forward messages from inpage to background // forward communication across
window.addEventListener('message', receiveMessage, false); pageStream.pipe(pluginStream)
function receiveMessage(event){ pluginStream.pipe(pageStream)
var msg = event.data
// validate message type
if (typeof msg !== 'object') return
if (msg.to !== allowedMessageTarget) return
if (msg.type !== allowedMessageType) return
// forward message
metamaskPlugin.postMessage(msg)
}

View File

@ -1,59 +1,18 @@
const web3 = require('web3') const Web3 = require('web3')
const BlockAppsWeb3Provider = require('blockapps-web3') const StreamProvider = require('./lib/stream-provider.js')
const Transaction = require('ethereumjs-tx') const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
require('object.entries').shim()
// const rpcUrl = 'https://rpc.metamask.io'
// var provider = new MetamaskProvider(forwardPayload, rpcUrl)
var provider = new BlockAppsWeb3Provider({
host: 'http://hacknet.blockapps.net',
// host: 'http://api.blockapps.net',
transaction_signer: {
// Can be any object that implements the following methods:
hasAddress: function(address, callback) {
console.log('metamask provider - asked for address ownership', address)
callback(null, true)
},
signTransaction: function(txParams, callback) {
txParams.gasLimit = txParams.gas
var tx = new Transaction(txParams)
tx.sign(new Buffer('0d0ba14043088cd629a978b49c8691deca5926f0271432bc0064e4745bac0a9f', 'hex'))
callback(null, '0x'+tx.serialize().toString('hex'))
},
},
coinbase: '0x00000000000',
accounts: ['0x985095ef977ba75fb2bb79cd5c4b84c81392dff6'],
// host: function(){ debugger },
});
const documentOrigin = window.location.origin
const allowedMessageTarget = 'metamask'
const allowedMessageType = 'addUnsignedTx'
web3.setProvider(provider)
// disable setProvider
web3.setProvider = function(){}
// injecting web3
console.log('Metamask injected web3')
// log all the stuff! // setup plugin communication
// provider.verbosity = 1 var pluginStream = new LocalMessageDuplexStream({
name: 'inpage',
// web3.currentProvider.vm.onStep = function(data, cb){ target: 'contentscript',
// console.log(data) })
// cb() var remoteProvider = new StreamProvider()
// } remoteProvider.pipe(pluginStream).pipe(remoteProvider)
// create web3
var web3 = new Web3(remoteProvider)
window.web3 = web3 window.web3 = web3
web3.setProvider = function(){}
console.log('Metamask injected web3')
function forwardPayload(payload){
window.postMessage({
to: allowedMessageTarget,
type: allowedMessageType,
payload: payload,
}, documentOrigin)
}

View File

@ -0,0 +1,53 @@
const Duplex = require('readable-stream').Duplex
const inherits = require('util').inherits
module.exports = LocalMessageDuplexStream
inherits(LocalMessageDuplexStream, Duplex)
function LocalMessageDuplexStream(opts){
Duplex.call(this, {
objectMode: true,
})
// this._origin = opts.origin
this._name = opts.name
this._target = opts.target
// console.log('LocalMessageDuplexStream ('+this._name+') - initialized...')
window.addEventListener('message', this._onMessage.bind(this), false)
}
// private
LocalMessageDuplexStream.prototype._onMessage = function(event){
var msg = event.data
// console.log('LocalMessageDuplexStream ('+this._name+') - heard message...')
// validate message
if (event.origin !== location.origin) return //console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (event.origin !== location.origin) ')
if (typeof msg !== 'object') return //console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (typeof msg !== "object") ')
if (msg.target !== this._name) return //console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (msg.target !== this._name) ', msg.target, this._name)
if (!msg.data) return //console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (!msg.data) ')
// console.log('LocalMessageDuplexStream ('+this._name+') - accepted', msg.data)
// forward message
this.push(msg.data)
}
// stream plumbing
LocalMessageDuplexStream.prototype._read = noop
LocalMessageDuplexStream.prototype._write = function(data, encoding, cb){
// console.log('LocalMessageDuplexStream ('+this._name+') - sending message...')
var message = {
target: this._target,
data: data,
}
window.postMessage(message, location.origin)
cb()
}
// util
function noop(){}

View File

@ -0,0 +1,36 @@
const Duplex = require('readable-stream').Duplex
const inherits = require('util').inherits
module.exports = PortDuplexStream
inherits(PortDuplexStream, Duplex)
function PortDuplexStream(port){
Duplex.call(this, {
objectMode: true,
})
this._port = port
port.onMessage.addListener(this._onMessage.bind(this))
}
// private
PortDuplexStream.prototype._onMessage = function(msg){
// console.log('PortDuplexStream - saw message', msg)
this.push(msg)
}
// stream plumbing
PortDuplexStream.prototype._read = noop
PortDuplexStream.prototype._write = function(msg, encoding, cb){
// console.log('PortDuplexStream - sent message', msg)
this._port.postMessage(msg)
cb()
}
// util
function noop(){}

View File

@ -0,0 +1,50 @@
const Duplex = require('readable-stream').Duplex
const inherits = require('util').inherits
module.exports = StreamProvider
inherits(StreamProvider, Duplex)
function StreamProvider(){
Duplex.call(this, {
objectMode: true,
})
this._handlers = {}
}
// public
StreamProvider.prototype.send = function(payload){
throw new Error('StreamProvider - does not support synchronous RPC calls')
}
StreamProvider.prototype.sendAsync = function(payload, callback){
// console.log('StreamProvider - sending payload', payload)
this._handlers[payload.id] = callback
this.push(payload)
}
// private
StreamProvider.prototype._onResponse = function(payload){
// console.log('StreamProvider - got response', payload)
var callback = this._handlers[payload.id]
if (!callback) throw new Error('StreamProvider - Unknown response id')
delete this._handlers[payload.id]
callback(null, payload)
}
// stream plumbing
StreamProvider.prototype._read = noop
StreamProvider.prototype._write = function(msg, encoding, cb){
this._onResponse(msg)
cb()
}
// util
function noop(){}

View File

@ -8,7 +8,9 @@
"ethereumjs-tx": "^0.6.7", "ethereumjs-tx": "^0.6.7",
"ethereumjs-util": "^1.3.5", "ethereumjs-util": "^1.3.5",
"object.entries": "^1.0.2", "object.entries": "^1.0.2",
"web3": "^0.9.2" "readable-stream": "^2.0.5",
"web3": "^0.15.1",
"web3-provider-engine": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"grunt": "~0.4.1", "grunt": "~0.4.1",