web3.js/lib/web3/filter.js

230 lines
6.6 KiB
JavaScript
Raw Normal View History

2015-01-13 18:28:49 +01:00
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file filter.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
2015-03-12 16:33:19 +01:00
* Fabian Vogelsteller <fabian@ethdev.com>
2015-01-13 18:28:49 +01:00
* Gav Wood <g@ethdev.com>
* @date 2014
*/
2015-03-26 18:11:05 +01:00
var RequestManager = require('./requestmanager');
var formatters = require('./formatters');
2015-03-08 18:18:52 +01:00
var utils = require('../utils/utils');
2015-04-22 12:54:48 +02:00
/**
* Converts a given topic to a hex string, but also allows null values.
*
* @param {Mixed} value
* @return {String}
*/
var toTopic = function(value){
if(value === null || typeof value === 'undefined')
2015-04-22 15:47:29 +02:00
return null;
value = String(value);
if(value.indexOf('0x') === 0)
2015-04-22 12:54:48 +02:00
return value;
else
2015-08-10 22:11:37 +02:00
return utils.fromUtf8(value);
2015-04-22 12:54:48 +02:00
};
2015-02-05 23:11:16 +01:00
/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones
/// @param should be string or object
/// @returns options string or object
var getOptions = function (options) {
2015-03-26 15:43:35 +01:00
if (utils.isString(options)) {
2015-02-05 23:11:16 +01:00
return options;
}
2015-01-13 18:28:49 +01:00
2015-02-05 23:11:16 +01:00
options = options || {};
2015-01-13 18:28:49 +01:00
// make sure topics, get converted to hex
2015-03-26 15:43:35 +01:00
options.topics = options.topics || [];
options.topics = options.topics.map(function(topic){
2015-04-22 15:47:29 +02:00
return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
2015-03-26 15:43:35 +01:00
});
2015-01-13 18:28:49 +01:00
2015-03-25 21:46:45 +01:00
// lazy load
return {
topics: options.topics,
to: options.to,
address: options.address,
fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock),
toBlock: formatters.inputBlockNumberFormatter(options.toBlock)
2015-03-25 21:46:45 +01:00
};
2015-01-13 18:28:49 +01:00
};
/**
Adds the callback and sets up the methods, to iterate over the results.
2015-06-08 14:38:16 +02:00
@method getLogsAtStart
@param {Object} self
2015-06-08 14:38:16 +02:00
@param {funciton}
*/
2015-06-08 14:38:16 +02:00
var getLogsAtStart = function(self, callback){
// call getFilterLogs for the first watch callback start
if (!utils.isString(self.options)) {
self.get(function (err, messages) {
// don't send all the responses to all the watches again... just to self one
if (err) {
callback(err);
}
2015-07-20 15:22:01 +02:00
if(utils.isArray(messages)) {
messages.forEach(function (message) {
callback(null, message);
});
}
2015-06-08 14:38:16 +02:00
});
}
};
/**
Adds the callback and sets up the methods, to iterate over the results.
@method pollFilter
@param {Object} self
*/
var pollFilter = function(self) {
var onMessage = function (error, messages) {
if (error) {
return self.callbacks.forEach(function (callback) {
callback(error);
});
}
if(utils.isArray(messages)) {
messages.forEach(function (message) {
message = self.formatter ? self.formatter(message) : message;
self.callbacks.forEach(function (callback) {
callback(null, message);
});
});
}
};
RequestManager.getInstance().startPolling({
method: self.implementation.poll.call,
params: [self.filterId],
}, self.filterId, onMessage, self.stopWatching.bind(self));
2015-06-08 14:38:16 +02:00
};
2015-06-24 11:49:10 +02:00
var Filter = function (options, methods, formatter, callback) {
2015-06-05 10:46:49 +02:00
var self = this;
2015-03-26 18:11:05 +01:00
var implementation = {};
methods.forEach(function (method) {
method.attachToObject(implementation);
});
this.options = getOptions(options);
this.implementation = implementation;
2015-06-26 16:55:33 +02:00
this.filterId = null;
2015-03-26 18:11:05 +01:00
this.callbacks = [];
this.getLogsCallbacks = [];
2015-06-08 14:38:16 +02:00
this.pollFilters = [];
2015-03-26 18:11:05 +01:00
this.formatter = formatter;
2015-06-05 10:46:49 +02:00
this.implementation.newFilter(this.options, function(error, id){
if(error) {
2015-06-24 10:28:12 +02:00
self.callbacks.forEach(function(cb){
cb(error);
});
2015-06-08 14:38:16 +02:00
} else {
2015-06-05 10:46:49 +02:00
self.filterId = id;
2015-06-26 16:55:33 +02:00
// check if there are get pending callbacks as a consequence
// of calling get() with filterId unassigned.
self.getLogsCallbacks.forEach(function (cb){
self.get(cb);
});
self.getLogsCallbacks = [];
2015-06-26 16:55:33 +02:00
// get filter logs for the already existing watch calls
2015-06-24 10:28:12 +02:00
self.callbacks.forEach(function(cb){
getLogsAtStart(self, cb);
});
2015-06-26 16:55:33 +02:00
if(self.callbacks.length > 0)
pollFilter(self);
2015-06-24 10:28:12 +02:00
// start to watch immediately
if(callback) {
return self.watch(callback);
}
}
2015-06-05 10:46:49 +02:00
});
2015-06-24 10:28:12 +02:00
2015-07-22 12:31:37 +02:00
return this;
2015-03-26 18:11:05 +01:00
};
Filter.prototype.watch = function (callback) {
this.callbacks.push(callback);
2015-06-05 10:46:49 +02:00
2015-06-08 14:38:16 +02:00
if(this.filterId) {
getLogsAtStart(this, callback);
pollFilter(this);
}
return this;
2015-03-26 18:11:05 +01:00
};
2015-02-05 23:11:16 +01:00
2015-03-26 18:11:05 +01:00
Filter.prototype.stopWatching = function () {
RequestManager.getInstance().stopPolling(this.filterId);
2015-06-05 11:05:14 +02:00
// remove filter async
2015-06-05 10:46:49 +02:00
this.implementation.uninstallFilter(this.filterId, function(){});
2015-03-26 18:11:05 +01:00
this.callbacks = [];
2015-01-13 18:28:49 +01:00
};
2015-03-31 13:16:07 +02:00
Filter.prototype.get = function (callback) {
2015-03-26 18:11:05 +01:00
var self = this;
2015-04-09 09:15:04 +02:00
if (utils.isFunction(callback)) {
if (this.filterId === null) {
// If filterId is not set yet, call it back
// when newFilter() assigns it.
this.getLogsCallbacks.push(callback);
} else {
this.implementation.getLogs(this.filterId, function(err, res){
if (err) {
callback(err);
} else {
callback(null, res.map(function (log) {
return self.formatter ? self.formatter(log) : log;
}));
}
});
}
2015-03-31 13:16:07 +02:00
} else {
if (this.filterId === null) {
throw new Error('Filter ID Error: filter().get() can\'t be chained synchronous, please provide a callback for the get() method.');
}
2015-03-31 13:16:07 +02:00
var logs = this.implementation.getLogs(this.filterId);
return logs.map(function (log) {
return self.formatter ? self.formatter(log) : log;
});
}
2015-06-08 14:38:16 +02:00
return this;
2015-03-26 18:11:05 +01:00
};
2015-03-26 15:43:35 +01:00
2015-03-26 18:11:05 +01:00
module.exports = Filter;
2015-02-05 23:11:16 +01:00