From bb5ba2dcf91bc0941ca17d9daad38702d5c447ed Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 29 Oct 2014 14:52:09 +0100 Subject: [PATCH] whisper in js --- alethzero/js/main.js | 603 +++++++++++++++---------------------- libqethereum/QEthereum.cpp | 9 +- third/js/main.js | 603 +++++++++++++++---------------------- 3 files changed, 487 insertions(+), 728 deletions(-) diff --git a/alethzero/js/main.js b/alethzero/js/main.js index 0bdd8dbbc..32064ce26 100644 --- a/alethzero/js/main.js +++ b/alethzero/js/main.js @@ -3,6 +3,179 @@ return o instanceof Promise } + function flattenPromise (obj) { + if (obj instanceof Promise) { + return Promise.resolve(obj); + } + + if (obj instanceof Array) { + return new Promise(function (resolve) { + var promises = obj.map(function (o) { + return flattenPromise(o); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < obj.length; i++) { + obj[i] = res[i]; + } + resolve(obj); + }); + }); + } + + if (obj instanceof Object) { + return new Promise(function (resolve) { + var keys = Object.keys(obj); + var promises = keys.map(function (key) { + return flattenPromise(obj[key]); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < keys.length; i++) { + obj[keys[i]] = res[i]; + } + resolve(obj); + }); + }); + } + + return Promise.resolve(obj); + }; + + var ethMethods = function () { + var blockCall = function (args) { + return typeof args[0] === "string" ? "blockByHash" : "blockByNumber"; + }; + + var transactionCall = function (args) { + return typeof args[0] === "string" ? 'transactionByHash' : 'transactonByNumber'; + }; + + var uncleCall = function (args) { + return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber'; + }; + + var methods = [ + { name: 'balanceAt', call: 'balanceAt' }, + { name: 'stateAt', call: 'stateAt' }, + { name: 'countAt', call: 'countAt'}, + { name: 'codeAt', call: 'codeAt' }, + { name: 'transact', call: 'transact' }, + { name: 'call', call: 'call' }, + { name: 'block', call: blockCall }, + { name: 'transaction', call: transactionCall }, + { name: 'uncle', call: uncleCall }, + { name: 'compile', call: 'compile' } + ]; + return methods; + }; + + var ethProperties = function () { + return [ + { name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' }, + { name: 'listening', getter: 'listening', setter: 'setListening' }, + { name: 'mining', getter: 'mining', setter: 'setMining' }, + { name: 'gasPrice', getter: 'gasPrice' }, + { name: 'accounts', getter: 'accounts' }, + { name: 'peerCount', getter: 'peerCount' }, + { name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' }, + { name: 'number', getter: 'number'} + ]; + }; + + var dbMethods = function () { + return [ + { name: 'put', call: 'put' }, + { name: 'get', call: 'get' }, + { name: 'putString', call: 'putString' }, + { name: 'getString', call: 'getString' } + ]; + }; + + var shhMethods = function () { + return [ + { name: 'post', call: 'post' }, + { name: 'newIdentity', call: 'newIdentity' }, + { name: 'haveIdentity', call: 'haveIdentity' }, + { name: 'newGroup', call: 'newGroup' }, + { name: 'addToGroup', call: 'addToGroup' } + ]; + }; + + var ethWatchMethods = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter'; + }; + + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'uninstallFilter' }, + { name: 'getMessages', call: 'getMessages' } + ]; + }; + + var shhWatchMethods = function () { + return [ + { name: 'newFilter', call: 'shhNewFilter' }, + { name: 'uninstallFilter', call: 'shhUninstallFilter' }, + { name: 'getMessage', call: 'shhGetMessages' } + ]; + }; + + var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + obj[method.name] = function () { + return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { + var call = typeof method.call === "function" ? method.call(args) : method.call; + return {call: call, args: args}; + }).then(function (request) { + return new Promise(function (resolve, reject) { + web3.provider.send(request, function (result) { + if (result) { + resolve(result); + } else { + reject(result); + } + }); + }); + }).catch(function( err) { + console.error(err); + }); + }; + }); + }; + + var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + return new Promise(function(resolve, reject) { + web3.provider.send({call: property.getter}, function(result) { + resolve(result); + }); + }); + }; + if (property.setter) { + proto.set = function (val) { + return flattenPromise([val]).then(function (args) { + return new Promise(function (resolve) { + web3.provider.send({call: property.setter, args: args}, function (result) { + if (result) { + resolve(result); + } else { + reject(result); + } + }); + }); + }).catch(function (err) { + console.error(err); + }); + } + } + Object.defineProperty(obj, property.name, proto); + }); + }; + var web3 = { _callbacks: {}, _events: {}, @@ -33,342 +206,77 @@ return str; }, - fromAscii: function(str, pad) { - if(pad === undefined) { - pad = 32 - } + toDecimal: function (val) { + return parseInt(val, 16); + }, + fromAscii: function(str, pad) { + pad = pad === undefined ? 32 : pad; var hex = this.toHex(str); - while(hex.length < pad*2) hex += "00"; - return hex }, eth: { prototype: Object(), - - block: function(numberOrHash) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'blockByHash' : 'blockByNumber'; - web3.provider.send({call: call, args: [numberOrHash]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - transaction: function(numberOrHash, nth) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'transactionByHash' : 'transactonByNumber'; - web3.provider.send({call: call, args: [numberOrHash, nth]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - uncle: function(numberOrHash, nth) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'uncleByHash' : 'uncleByNumber'; - web3.provider.send({call: call, args: [numberOrHash, nth]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - transact: function(params) { - if(params === undefined) { - params = {}; - } - - if(params.endowment !== undefined) - params.value = params.endowment; - if(params.code !== undefined) - params.data = params.code; - - - var promises = [] - if(isPromise(params.to)) { - promises.push(params.to.then(function(_to) { params.to = _to; })); - } - if(isPromise(params.from)) { - promises.push(params.from.then(function(_from) { params.from = _from; })); - } - - if(typeof params.data !== "object" || isPromise(params.data)) { - params.data = [params.data] - } - - var data = params.data; - for(var i = 0; i < params.data.length; i++) { - if(isPromise(params.data[i])) { - var promise = params.data[i]; - var _i = i; - promises.push(promise.then(function(_arg) { params.data[_i] = _arg; })); - } - } - - // Make sure everything is string - var fields = ["value", "gas", "gasPrice"]; - for(var i = 0; i < fields.length; i++) { - if(params[fields[i]] === undefined) { - params[fields[i]] = ""; - } - params[fields[i]] = params[fields[i]].toString(); - } - - // Load promises then call the last "transact". - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - params.data = params.data.join(""); - web3.provider.send({call: "transact", args: [params]}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }) - }, - - compile: function(code) { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "compile", args: [code]}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }, - - balanceAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "balanceAt", args: [address]}, function(balance) { - resolve(balance); - }); - }); - }); - }, - - countAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "countAt", args: [address]}, function(count) { - resolve(count); - }); - }); - }); - }, - - codeAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "codeAt", args: [address]}, function(code) { - resolve(code); - }); - }); - }); - }, - - storageAt: function(address, storageAddress) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - if(isPromise(storageAddress)) { - promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "stateAt", args: [address, storageAddress]}, function(entry) { - resolve(entry); - }); - }); - }); + watch: function (params) { + return new Filter(params, ethWatch); }, + }, - stateAt: function(address, storageAddress) { - return this.storageAt(address, storageAddress); - }, - - call: function(params) { - if(params === undefined) { - params = {}; - } - - if(params.endowment !== undefined) - params.value = params.endowment; - if(params.code !== undefined) - params.data = params.code; - - - var promises = [] - if(isPromise(params.to)) { - promises.push(params.to.then(function(_to) { params.to = _to; })); - } - if(isPromise(params.from)) { - promises.push(params.from.then(function(_from) { params.from = _from; })); - } - - if(isPromise(params.data)) { - promises.push(params.data.then(function(_code) { params.data = _code; })); - } else { - if(typeof params.data === "object") { - data = ""; - for(var i = 0; i < params.data.length; i++) { - data += params.data[i] - } - } else { - data = params.data; - } - } - - // Make sure everything is string - var fields = ["value", "gas", "gasPrice"]; - for(var i = 0; i < fields.length; i++) { - if(params[fields[i]] === undefined) { - params[fields[i]] = ""; - } - params[fields[i]] = params[fields[i]].toString(); - } - - // Load promises then call the last "transact". - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "call", args: params}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }) - }, + db: { + prototype: Object() + }, - watch: function(params) { - return new Filter(params); - }, + shh: { + prototype: Object(), + watch: function (params) { + return new Filter(params, shhWatch); + } }, - on: function(event, cb) { + on: function(event, id, cb) { if(web3._events[event] === undefined) { - web3._events[event] = []; + web3._events[event] = {}; } - web3._events[event].push(cb); - + web3._events[event][id] = cb; return this }, - off: function(event, cb) { + off: function(event, id) { if(web3._events[event] !== undefined) { - var callbacks = web3._events[event]; - for(var i = 0; i < callbacks.length; i++) { - if(callbacks[i] === cb) { - delete callbacks[i]; - } - } + delete web3._events[event][id]; } return this }, - trigger: function(event, data) { + trigger: function(event, id, data) { var callbacks = web3._events[event]; - if(callbacks !== undefined) { - for(var i = 0; i < callbacks.length; i++) { - // Figure out whether the returned data was an array - // array means multiple return arguments (multiple params) - if(data instanceof Array) { - callbacks[i].apply(this, data); - } else { - callbacks[i].call(this, undefined, data); - } - } + if (!callbacks || !callbacks[id]) { + return; } + var cb = callbacks[id]; + cb(data); }, }; var eth = web3.eth; - // Eth object properties - Object.defineProperty(eth, "gasPrice", { - get: function() { - return "10000000000000" - } - }); + setupMethods(eth, ethMethods()); + setupProperties(eth, ethProperties()); + setupMethods(web3.db, dbMethods()); + setupMethods(web3.shh, shhMethods()); - Object.defineProperty(eth, "coinbase", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "coinbase"}, function(coinbase) { - resolve(coinbase); - }); - }); - }, - }); - - Object.defineProperty(eth, "listening", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "listening"}, function(listening) { - resolve(listening); - }); - }); - }, - }); - - - Object.defineProperty(eth, "mining", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "mining"}, function(mining) { - resolve(mining); - }); - }); - }, - }); - - Object.defineProperty(eth, "peerCount", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "peerCount"}, function(peerCount) { - resolve(peerCount); - }); - }); - }, - }); + var ethWatch = { + changed: 'changed' + }; + setupMethods(ethWatch, ethWatchMethods()); + var shhWatch = { + changed: 'shhChanged' + }; + setupMethods(shhWatch, shhWatchMethods()); var ProviderManager = function() { this.queued = []; @@ -393,14 +301,11 @@ ProviderManager.prototype.send = function(data, cb) { data._id = this.id; - if(cb) { + if (cb) { web3._callbacks[data._id] = cb; } - if(data.args === undefined) { - data.args = []; - } - + data.args = data.args || []; this.id++; if(this.provider !== undefined) { @@ -451,33 +356,20 @@ web3.setProvider = function(provider) { provider.onmessage = messageHandler; - web3.provider.set(provider); - web3.provider.sendQueued(); }; - var filters = []; - var Filter = function(options) { - filters.push(this); - + var Filter = function(options, impl) { + this.impl = impl; this.callbacks = []; - this.options = options; - - var call; - if(options === "chain" || options === "pending") { - call = "newFilterString" - } else if(typeof options === "object") { - call = "newFilter" - } - var self = this; // Cheaper than binding - this.promise = new Promise(function(resolve, reject) { - web3.provider.send({call: call, args: [options]}, function(id) { - self.id = id; - web3.provider.startPolling({call: "changed", args: [id]}, id); - resolve(id); - }); + var self = this; + this.promise = impl.newFilter(options); + this.promise.then(function (id) { + self.id = id; + web3.on(impl.changed, id, self.trigger.bind(self)); + web3.provider.startPolling({call: impl.changed, args: [id]}, id); }); }; @@ -488,53 +380,39 @@ }); }; - Filter.prototype.trigger = function(messages, id) { - if(id == this.id) { - for(var i = 0; i < this.callbacks.length; i++) { - this.callbacks[i].call(this, messages); - } + Filter.prototype.trigger = function(messages) { + for(var i = 0; i < this.callbacks.length; i++) { + this.callbacks[i].call(this, messages); } }; Filter.prototype.uninstall = function() { - this.promise.then(function(id) { - web3.provider.send({call: "uninstallFilter", args:[id]}); + var self = this; + this.promise.then(function (id) { + self.impl.uninstallFilter(id); web3.provider.stopPolling(id); + web3.off(impl.changed, id); }); }; Filter.prototype.messages = function() { - var self=this; - return Promise.all([this.promise]).then(function() { - var id = self.id - return new Promise(function(resolve, reject) { - web3.provider.send({call: "getMessages", args: [id]}, function(messages) { - resolve(messages); - }); - }); + var self = this; + return this.promise.then(function (id) { + return self.impl.getMessages(id); }); }; - // Register to the messages callback. "messages" will be emitted when new messages - // from the client have been created. - web3.on("messages", function(messages, id) { - for(var i = 0; i < filters.length; i++) { - filters[i].trigger(messages, id); - } - }); - function messageHandler(data) { if(data._event !== undefined) { - web3.trigger(data._event, data.data); - } else { - if(data._id) { - var cb = web3._callbacks[data._id]; - if(cb) { - cb.call(this, data.data) - - // Remove the "trigger" callback - delete web3._callbacks[data._id]; - } + web3.trigger(data._event, data._id, data.data); + return; + } + + if(data._id) { + var cb = web3._callbacks[data._id]; + if (cb) { + cb.call(this, data.data) + delete web3._callbacks[data._id]; } } } @@ -549,4 +427,5 @@ */ window.web3 = web3; + })(this); diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index b5d40b4f2..25b341146 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -136,8 +136,8 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) if (elem.contains("result") && elem["result"].toBool() == true) { QJsonObject res; - res["_event"] = QString::fromStdString("messages"); - res["data"] = (int)m_watches[i]; // we can do that couse poll is synchronous + res["_event"] = _addInfo; + res["_id"] = (int)m_watches[i]; // we can do that couse poll is synchronous response(QString::fromUtf8(QJsonDocument(res).toJson())); } } @@ -154,8 +154,9 @@ void QWebThree::onDataProcessed(QString _json, QString _addInfo) for (auto e: elem["result"].toArray()) { QJsonObject res; - res["_event"] = QString::fromStdString("messages"); - res["data"] = e; //TODO somehow send watch id? + res["_event"] = _addInfo; + res["_id"] = (int)m_shhWatches[i]; + res["data"] = e; response(QString::fromUtf8(QJsonDocument(res).toJson())); } } diff --git a/third/js/main.js b/third/js/main.js index 0bdd8dbbc..32064ce26 100644 --- a/third/js/main.js +++ b/third/js/main.js @@ -3,6 +3,179 @@ return o instanceof Promise } + function flattenPromise (obj) { + if (obj instanceof Promise) { + return Promise.resolve(obj); + } + + if (obj instanceof Array) { + return new Promise(function (resolve) { + var promises = obj.map(function (o) { + return flattenPromise(o); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < obj.length; i++) { + obj[i] = res[i]; + } + resolve(obj); + }); + }); + } + + if (obj instanceof Object) { + return new Promise(function (resolve) { + var keys = Object.keys(obj); + var promises = keys.map(function (key) { + return flattenPromise(obj[key]); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < keys.length; i++) { + obj[keys[i]] = res[i]; + } + resolve(obj); + }); + }); + } + + return Promise.resolve(obj); + }; + + var ethMethods = function () { + var blockCall = function (args) { + return typeof args[0] === "string" ? "blockByHash" : "blockByNumber"; + }; + + var transactionCall = function (args) { + return typeof args[0] === "string" ? 'transactionByHash' : 'transactonByNumber'; + }; + + var uncleCall = function (args) { + return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber'; + }; + + var methods = [ + { name: 'balanceAt', call: 'balanceAt' }, + { name: 'stateAt', call: 'stateAt' }, + { name: 'countAt', call: 'countAt'}, + { name: 'codeAt', call: 'codeAt' }, + { name: 'transact', call: 'transact' }, + { name: 'call', call: 'call' }, + { name: 'block', call: blockCall }, + { name: 'transaction', call: transactionCall }, + { name: 'uncle', call: uncleCall }, + { name: 'compile', call: 'compile' } + ]; + return methods; + }; + + var ethProperties = function () { + return [ + { name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' }, + { name: 'listening', getter: 'listening', setter: 'setListening' }, + { name: 'mining', getter: 'mining', setter: 'setMining' }, + { name: 'gasPrice', getter: 'gasPrice' }, + { name: 'accounts', getter: 'accounts' }, + { name: 'peerCount', getter: 'peerCount' }, + { name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' }, + { name: 'number', getter: 'number'} + ]; + }; + + var dbMethods = function () { + return [ + { name: 'put', call: 'put' }, + { name: 'get', call: 'get' }, + { name: 'putString', call: 'putString' }, + { name: 'getString', call: 'getString' } + ]; + }; + + var shhMethods = function () { + return [ + { name: 'post', call: 'post' }, + { name: 'newIdentity', call: 'newIdentity' }, + { name: 'haveIdentity', call: 'haveIdentity' }, + { name: 'newGroup', call: 'newGroup' }, + { name: 'addToGroup', call: 'addToGroup' } + ]; + }; + + var ethWatchMethods = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter'; + }; + + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'uninstallFilter' }, + { name: 'getMessages', call: 'getMessages' } + ]; + }; + + var shhWatchMethods = function () { + return [ + { name: 'newFilter', call: 'shhNewFilter' }, + { name: 'uninstallFilter', call: 'shhUninstallFilter' }, + { name: 'getMessage', call: 'shhGetMessages' } + ]; + }; + + var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + obj[method.name] = function () { + return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { + var call = typeof method.call === "function" ? method.call(args) : method.call; + return {call: call, args: args}; + }).then(function (request) { + return new Promise(function (resolve, reject) { + web3.provider.send(request, function (result) { + if (result) { + resolve(result); + } else { + reject(result); + } + }); + }); + }).catch(function( err) { + console.error(err); + }); + }; + }); + }; + + var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + return new Promise(function(resolve, reject) { + web3.provider.send({call: property.getter}, function(result) { + resolve(result); + }); + }); + }; + if (property.setter) { + proto.set = function (val) { + return flattenPromise([val]).then(function (args) { + return new Promise(function (resolve) { + web3.provider.send({call: property.setter, args: args}, function (result) { + if (result) { + resolve(result); + } else { + reject(result); + } + }); + }); + }).catch(function (err) { + console.error(err); + }); + } + } + Object.defineProperty(obj, property.name, proto); + }); + }; + var web3 = { _callbacks: {}, _events: {}, @@ -33,342 +206,77 @@ return str; }, - fromAscii: function(str, pad) { - if(pad === undefined) { - pad = 32 - } + toDecimal: function (val) { + return parseInt(val, 16); + }, + fromAscii: function(str, pad) { + pad = pad === undefined ? 32 : pad; var hex = this.toHex(str); - while(hex.length < pad*2) hex += "00"; - return hex }, eth: { prototype: Object(), - - block: function(numberOrHash) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'blockByHash' : 'blockByNumber'; - web3.provider.send({call: call, args: [numberOrHash]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - transaction: function(numberOrHash, nth) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'transactionByHash' : 'transactonByNumber'; - web3.provider.send({call: call, args: [numberOrHash, nth]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - uncle: function(numberOrHash, nth) { - return new Promise(function(resolve, reject) { - var call = typeof numberOrHash === "string" ? 'uncleByHash' : 'uncleByNumber'; - web3.provider.send({call: call, args: [numberOrHash, nth]}, function(block) { - if(block) - resolve(block); - else - reject("not found"); - }); - }); - }, - - transact: function(params) { - if(params === undefined) { - params = {}; - } - - if(params.endowment !== undefined) - params.value = params.endowment; - if(params.code !== undefined) - params.data = params.code; - - - var promises = [] - if(isPromise(params.to)) { - promises.push(params.to.then(function(_to) { params.to = _to; })); - } - if(isPromise(params.from)) { - promises.push(params.from.then(function(_from) { params.from = _from; })); - } - - if(typeof params.data !== "object" || isPromise(params.data)) { - params.data = [params.data] - } - - var data = params.data; - for(var i = 0; i < params.data.length; i++) { - if(isPromise(params.data[i])) { - var promise = params.data[i]; - var _i = i; - promises.push(promise.then(function(_arg) { params.data[_i] = _arg; })); - } - } - - // Make sure everything is string - var fields = ["value", "gas", "gasPrice"]; - for(var i = 0; i < fields.length; i++) { - if(params[fields[i]] === undefined) { - params[fields[i]] = ""; - } - params[fields[i]] = params[fields[i]].toString(); - } - - // Load promises then call the last "transact". - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - params.data = params.data.join(""); - web3.provider.send({call: "transact", args: [params]}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }) - }, - - compile: function(code) { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "compile", args: [code]}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }, - - balanceAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "balanceAt", args: [address]}, function(balance) { - resolve(balance); - }); - }); - }); - }, - - countAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "countAt", args: [address]}, function(count) { - resolve(count); - }); - }); - }); - }, - - codeAt: function(address) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "codeAt", args: [address]}, function(code) { - resolve(code); - }); - }); - }); - }, - - storageAt: function(address, storageAddress) { - var promises = []; - - if(isPromise(address)) { - promises.push(address.then(function(_address) { address = _address; })); - } - - if(isPromise(storageAddress)) { - promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; })); - } - - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "stateAt", args: [address, storageAddress]}, function(entry) { - resolve(entry); - }); - }); - }); + watch: function (params) { + return new Filter(params, ethWatch); }, + }, - stateAt: function(address, storageAddress) { - return this.storageAt(address, storageAddress); - }, - - call: function(params) { - if(params === undefined) { - params = {}; - } - - if(params.endowment !== undefined) - params.value = params.endowment; - if(params.code !== undefined) - params.data = params.code; - - - var promises = [] - if(isPromise(params.to)) { - promises.push(params.to.then(function(_to) { params.to = _to; })); - } - if(isPromise(params.from)) { - promises.push(params.from.then(function(_from) { params.from = _from; })); - } - - if(isPromise(params.data)) { - promises.push(params.data.then(function(_code) { params.data = _code; })); - } else { - if(typeof params.data === "object") { - data = ""; - for(var i = 0; i < params.data.length; i++) { - data += params.data[i] - } - } else { - data = params.data; - } - } - - // Make sure everything is string - var fields = ["value", "gas", "gasPrice"]; - for(var i = 0; i < fields.length; i++) { - if(params[fields[i]] === undefined) { - params[fields[i]] = ""; - } - params[fields[i]] = params[fields[i]].toString(); - } - - // Load promises then call the last "transact". - return Promise.all(promises).then(function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "call", args: params}, function(data) { - if(data[1]) - reject(data[0]); - else - resolve(data[0]); - }); - }); - }) - }, + db: { + prototype: Object() + }, - watch: function(params) { - return new Filter(params); - }, + shh: { + prototype: Object(), + watch: function (params) { + return new Filter(params, shhWatch); + } }, - on: function(event, cb) { + on: function(event, id, cb) { if(web3._events[event] === undefined) { - web3._events[event] = []; + web3._events[event] = {}; } - web3._events[event].push(cb); - + web3._events[event][id] = cb; return this }, - off: function(event, cb) { + off: function(event, id) { if(web3._events[event] !== undefined) { - var callbacks = web3._events[event]; - for(var i = 0; i < callbacks.length; i++) { - if(callbacks[i] === cb) { - delete callbacks[i]; - } - } + delete web3._events[event][id]; } return this }, - trigger: function(event, data) { + trigger: function(event, id, data) { var callbacks = web3._events[event]; - if(callbacks !== undefined) { - for(var i = 0; i < callbacks.length; i++) { - // Figure out whether the returned data was an array - // array means multiple return arguments (multiple params) - if(data instanceof Array) { - callbacks[i].apply(this, data); - } else { - callbacks[i].call(this, undefined, data); - } - } + if (!callbacks || !callbacks[id]) { + return; } + var cb = callbacks[id]; + cb(data); }, }; var eth = web3.eth; - // Eth object properties - Object.defineProperty(eth, "gasPrice", { - get: function() { - return "10000000000000" - } - }); + setupMethods(eth, ethMethods()); + setupProperties(eth, ethProperties()); + setupMethods(web3.db, dbMethods()); + setupMethods(web3.shh, shhMethods()); - Object.defineProperty(eth, "coinbase", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "coinbase"}, function(coinbase) { - resolve(coinbase); - }); - }); - }, - }); - - Object.defineProperty(eth, "listening", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "listening"}, function(listening) { - resolve(listening); - }); - }); - }, - }); - - - Object.defineProperty(eth, "mining", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "mining"}, function(mining) { - resolve(mining); - }); - }); - }, - }); - - Object.defineProperty(eth, "peerCount", { - get: function() { - return new Promise(function(resolve, reject) { - web3.provider.send({call: "peerCount"}, function(peerCount) { - resolve(peerCount); - }); - }); - }, - }); + var ethWatch = { + changed: 'changed' + }; + setupMethods(ethWatch, ethWatchMethods()); + var shhWatch = { + changed: 'shhChanged' + }; + setupMethods(shhWatch, shhWatchMethods()); var ProviderManager = function() { this.queued = []; @@ -393,14 +301,11 @@ ProviderManager.prototype.send = function(data, cb) { data._id = this.id; - if(cb) { + if (cb) { web3._callbacks[data._id] = cb; } - if(data.args === undefined) { - data.args = []; - } - + data.args = data.args || []; this.id++; if(this.provider !== undefined) { @@ -451,33 +356,20 @@ web3.setProvider = function(provider) { provider.onmessage = messageHandler; - web3.provider.set(provider); - web3.provider.sendQueued(); }; - var filters = []; - var Filter = function(options) { - filters.push(this); - + var Filter = function(options, impl) { + this.impl = impl; this.callbacks = []; - this.options = options; - - var call; - if(options === "chain" || options === "pending") { - call = "newFilterString" - } else if(typeof options === "object") { - call = "newFilter" - } - var self = this; // Cheaper than binding - this.promise = new Promise(function(resolve, reject) { - web3.provider.send({call: call, args: [options]}, function(id) { - self.id = id; - web3.provider.startPolling({call: "changed", args: [id]}, id); - resolve(id); - }); + var self = this; + this.promise = impl.newFilter(options); + this.promise.then(function (id) { + self.id = id; + web3.on(impl.changed, id, self.trigger.bind(self)); + web3.provider.startPolling({call: impl.changed, args: [id]}, id); }); }; @@ -488,53 +380,39 @@ }); }; - Filter.prototype.trigger = function(messages, id) { - if(id == this.id) { - for(var i = 0; i < this.callbacks.length; i++) { - this.callbacks[i].call(this, messages); - } + Filter.prototype.trigger = function(messages) { + for(var i = 0; i < this.callbacks.length; i++) { + this.callbacks[i].call(this, messages); } }; Filter.prototype.uninstall = function() { - this.promise.then(function(id) { - web3.provider.send({call: "uninstallFilter", args:[id]}); + var self = this; + this.promise.then(function (id) { + self.impl.uninstallFilter(id); web3.provider.stopPolling(id); + web3.off(impl.changed, id); }); }; Filter.prototype.messages = function() { - var self=this; - return Promise.all([this.promise]).then(function() { - var id = self.id - return new Promise(function(resolve, reject) { - web3.provider.send({call: "getMessages", args: [id]}, function(messages) { - resolve(messages); - }); - }); + var self = this; + return this.promise.then(function (id) { + return self.impl.getMessages(id); }); }; - // Register to the messages callback. "messages" will be emitted when new messages - // from the client have been created. - web3.on("messages", function(messages, id) { - for(var i = 0; i < filters.length; i++) { - filters[i].trigger(messages, id); - } - }); - function messageHandler(data) { if(data._event !== undefined) { - web3.trigger(data._event, data.data); - } else { - if(data._id) { - var cb = web3._callbacks[data._id]; - if(cb) { - cb.call(this, data.data) - - // Remove the "trigger" callback - delete web3._callbacks[data._id]; - } + web3.trigger(data._event, data._id, data.data); + return; + } + + if(data._id) { + var cb = web3._callbacks[data._id]; + if (cb) { + cb.call(this, data.data) + delete web3._callbacks[data._id]; } } } @@ -549,4 +427,5 @@ */ window.web3 = web3; + })(this);