axe.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. ;(function(){
  2. /* UNBUILD */
  3. var root;
  4. if(typeof window !== "undefined"){ root = window }
  5. if(typeof global !== "undefined"){ root = global }
  6. root = root || {};
  7. var console = root.console || {log: function(){}};
  8. function USE(arg, req){
  9. return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
  10. arg(mod = {exports: {}});
  11. USE[R(path)] = mod.exports;
  12. }
  13. function R(p){
  14. return p.split('/').slice(-1).toString().replace('.js','');
  15. }
  16. }
  17. if(typeof module !== "undefined"){ var common = module }
  18. /* UNBUILD */
  19. ;USE(function(module){
  20. if(typeof window !== "undefined"){ module.window = window }
  21. var tmp = module.window || module;
  22. var AXE = tmp.AXE || function(){};
  23. if(AXE.window = module.window){ AXE.window.AXE = AXE }
  24. try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
  25. module.exports = AXE;
  26. })(USE, './root');
  27. ;USE(function(module){
  28. var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
  29. (Gun.AXE = AXE).GUN = AXE.Gun = Gun;
  30. Gun.on('opt', function(at){
  31. start(at);
  32. this.to.next(at); // make sure to call the "next" middleware adapter.
  33. });
  34. function start(at){
  35. if(at.axe){ return }
  36. var opt = at.opt, peers = opt.peers;
  37. if(false === opt.axe){ return }
  38. if((typeof process !== "undefined") && 'false' === ''+(process.env||{}).AXE){ return }
  39. var axe = at.axe = {}, tmp;
  40. // 1. If any remembered peers or from last cache or extension
  41. // 2. Fallback to use hard coded peers from dApp
  42. // 3. Or any offered peers.
  43. //if(Gun.obj.empty(p)){
  44. // Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
  45. // p[url] = {url: url, axe: {}};
  46. // });
  47. //}
  48. // Our current hypothesis is that it is most optimal
  49. // to take peers in a common network, and align
  50. // them in a line, where you only have left and right
  51. // peers, so messages propagate left and right in
  52. // a linear manner with reduced overlap, and
  53. // with one common superpeer (with ready failovers)
  54. // in case the p2p linear latency is high.
  55. // Or there could be plenty of other better options.
  56. var mesh = opt.mesh = opt.mesh || Gun.Mesh(at);
  57. console.log("AXE enabled.");
  58. function verify(dht, msg) {
  59. var puts = Object.keys(msg.put);
  60. var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
  61. var subs = dht(soul);
  62. if (!subs) { return; }
  63. var tmp = [];
  64. Gun.obj.map(subs.split(','), function(pid) {
  65. if (pid in peers) {
  66. tmp.push(pid);
  67. mesh.say(msg, peers[pid]);
  68. }
  69. });
  70. /// Only connected peers in the tmp array.
  71. if (opt.super) {
  72. dht(soul, tmp.join(','));
  73. }
  74. }
  75. function route(get){ var tmp;
  76. if(!get){ return }
  77. if('string' != typeof (tmp = get['#'])){ return }
  78. return tmp;
  79. }
  80. var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
  81. at.opt.dht = Rad();
  82. at.on('in', function input(msg){
  83. var to = this.to, peer = (msg._||{}).via;
  84. var dht = opt.dht;
  85. var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
  86. var get = msg.get, hash, tmp;
  87. //if(get && opt.super && peer){
  88. if(get && opt.super && peer && (tmp = route(get))){
  89. hash = tmp; //Gun.obj.hash(get); // USE RAD INSTEAD!
  90. (routes[hash] || (routes[hash] = {}))[peer.id] = peer;
  91. (peer.routes || (peer.routes = {}))[hash] = routes[hash];
  92. /*if(soul = get['#']){ // SWITCH BACK TO USING DHT!
  93. if(key = get['.']){
  94. } else {
  95. }
  96. if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
  97. var pids = joindht(dht, soul, peer.id);
  98. if (pids) {
  99. var dht = {};
  100. dht[soul] = pids;
  101. mesh.say({dht:dht}, opt.peers[peer.id]);
  102. }
  103. }*/
  104. }
  105. if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){
  106. (tmp = (tmp._||ok)).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
  107. }
  108. to.next(msg);
  109. if (opt.rtc && msg.dht) {
  110. Gun.obj.map(msg.dht, function(pids, soul) {
  111. dht(soul, pids);
  112. Gun.obj.map(pids.split(','), function(pid) {
  113. /// TODO: here we can put an algorithm of who must connect?
  114. if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; }
  115. opt.announce[pid] = true; /// To try only one connection to the same peer.
  116. opt.announce(pid);
  117. });
  118. });
  119. }
  120. });
  121. //try{console.log(req.connection.remoteAddress)}catch(e){};
  122. mesh.hear['opt'] = function(msg, peer){
  123. if(msg.ok){ return opt.log(msg) }
  124. var tmp = msg.opt;
  125. if(!tmp){ return }
  126. tmp = tmp.peers;
  127. if(!tmp || !Gun.text.is(tmp)){ return }
  128. if(axe.up[tmp] || 6 <= Object.keys(axe.up).length){ return }
  129. var o = tmp; //{peers: tmp};
  130. at.$.opt(o);
  131. o = peers[tmp];
  132. if(!o){ return }
  133. o.retry = 9;
  134. mesh.wire(o);
  135. if(peer){ mesh.say({dam: 'opt', ok: 1, '@': msg['#']}, peer) }
  136. }
  137. setInterval(function(tmp){
  138. if(!(tmp = at.stats && at.stats.stay)){ return }
  139. (tmp.axe = tmp.axe || {}).up = Object.keys(axe.up||{});
  140. },1000 * 60)
  141. setTimeout(function(tmp){
  142. if(!(tmp = at.stats && at.stats.stay)){ return }
  143. Gun.obj.map((tmp.axe||{}).up, function(url){ mesh.hear.opt({opt: {peers: url}}) })
  144. },1000);
  145. if(at.opt.super){
  146. var rotate = 0;
  147. mesh.way = function(msg) {
  148. if (msg.rtc) {
  149. if (msg.rtc.to) {
  150. /// Send announce to one peer only if the msg have 'to' attr
  151. var peer = (peers) ? peers[msg.rtc.to] : null;
  152. if (peer) { mesh.say(msg, peer); }
  153. return;
  154. }
  155. }
  156. if(msg.get){ mesh.say(msg, axe.up) } // always send gets up!
  157. if(msg.get && (tmp = route(msg.get))){
  158. var hash = tmp; //Gun.obj.hash(msg.get);
  159. var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
  160. var peers = routes[hash];
  161. function chat(peers, old){ // what about optimizing for directed peers?
  162. if(!peers){ return chat(opt.peers) }
  163. var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
  164. var meta = (msg._||yes);
  165. clearTimeout(meta.lack);
  166. var id, peer, c = 1; // opt. ?redundancy?
  167. while((id = ids[meta.turn || 0]) && c--){ // TODO: This hits peers in order, not necessarily best for load balancing. And what about optimizing for directed peers?
  168. peer = peers[id];
  169. meta.turn = (meta.turn || 0) + 1;
  170. if((old && old[id]) || false === mesh.say(msg, peer)){ ++c }
  171. }
  172. //console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers);
  173. if(0 < c){
  174. if(peers === opt.peers){ return } // prevent infinite lack loop.
  175. return meta.turn = 0, chat(opt.peers, peers)
  176. }
  177. var hash = msg['##'], ack = meta.ack;
  178. meta.lack = setTimeout(function(){
  179. if(ack && hash && hash === msg['##']){ return }
  180. if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit.
  181. //console.log(msg['#'], "CONTINUE:", ack, hash, msg['##']);
  182. chat(peers, old); // keep asking for data if there is mismatching hashes.
  183. }, 25);
  184. }
  185. return chat(peers);
  186. }
  187. // TODO: PUTs need to only go to subs!
  188. if(msg.put){
  189. var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
  190. var peers = {};
  191. Gun.obj.map(msg.put, function(node, soul){
  192. var hash = soul; //Gun.obj.hash({'#': soul});
  193. var to = routes[hash];
  194. if(!to){ return }
  195. Gun.obj.to(to, peers);
  196. });
  197. mesh.say(msg, peers);
  198. return;
  199. }
  200. mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT!
  201. if (!msg.put) { mesh.say(msg); return; }
  202. //console.log('AXE HOOK!! ', msg);
  203. verify(opt.dht, msg);
  204. };
  205. } else {
  206. mesh.route = function(msg) {
  207. if (msg.rtc) {
  208. }
  209. if (!msg.put) { mesh.say(msg); return; }
  210. verify(opt.dht, msg);
  211. /// Always send to superpeers?
  212. Gun.obj.map(peers, function(peer) {
  213. if (peer.url) {
  214. mesh.say(msg, peer);
  215. }
  216. });
  217. };
  218. /*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW!
  219. at.on('hi', function(opt) {
  220. this.to.next(opt);
  221. //console.log('AXE PEER [HI]', new Date(), opt);
  222. connections++;
  223. /// The first connection don't need to resubscribe the nodes.
  224. if (connections === 1) { return; }
  225. /// Resubscribe all nodes.
  226. setTimeout(function() {
  227. var souls = Object.keys(at.graph);
  228. for (var i=0; i < souls.length; ++i) {
  229. //at.gun.get(souls[i]).off();
  230. at.next[souls[i]].ack = 0;
  231. at.gun.get(souls[i]).once(function(){});
  232. }
  233. //location.reload();
  234. }, 500);
  235. }, at);*/
  236. }
  237. axe.up = {};
  238. at.on('hi', function(peer){
  239. this.to.next(peer);
  240. if(!peer.url){ return }
  241. axe.up[peer.id] = peer;
  242. })
  243. at.on('bye', function(peer){ this.to.next(peer);
  244. if(peer.url){ delete axe.up[peer.id] }
  245. Gun.obj.map(peer.routes, function(route, hash){
  246. delete route[peer.id];
  247. if(Gun.obj.empty(route)){
  248. delete axe.routes[hash];
  249. }
  250. });
  251. });
  252. }
  253. function joindht(dht, soul, pids) {
  254. if (!pids || !soul || !dht) { return; }
  255. var subs = dht(soul);
  256. var tmp = subs ? subs.split(',') : [];
  257. Gun.obj.map(pids.split(','), function(pid) {
  258. if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); }
  259. });
  260. tmp = tmp.join(',');
  261. dht(soul, tmp);
  262. return tmp;
  263. }
  264. var empty = {}, yes = true, u;
  265. module.exports = AXE;
  266. })(USE, './axe');
  267. }());