axe.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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(false === process.env.NO_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. if(at.opt.super){
  122. var rotate = 0;
  123. mesh.way = function(msg) {
  124. if (msg.rtc) {
  125. if (msg.rtc.to) {
  126. /// Send announce to one peer only if the msg have 'to' attr
  127. var peer = (peers) ? peers[msg.rtc.to] : null;
  128. if (peer) { mesh.say(msg, peer); }
  129. return;
  130. }
  131. }
  132. if(msg.get && (tmp = route(msg.get))){
  133. var hash = tmp; //Gun.obj.hash(msg.get);
  134. var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
  135. var peers = routes[hash];
  136. function chat(peers, old){ // what about optimizing for directed peers?
  137. if(!peers){ return chat(opt.peers) }
  138. var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
  139. var meta = (msg._||yes);
  140. clearTimeout(meta.lack);
  141. var id, peer, c = 1; // opt. ?redundancy?
  142. 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?
  143. peer = peers[id];
  144. meta.turn = (meta.turn || 0) + 1;
  145. if((old && old[id]) || false === mesh.say(msg, peer)){ ++c }
  146. }
  147. //console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers);
  148. if(0 < c){
  149. if(peers === opt.peers){ return } // prevent infinite lack loop.
  150. return meta.turn = 0, chat(opt.peers, peers)
  151. }
  152. var hash = msg['##'], ack = meta.ack;
  153. meta.lack = setTimeout(function(){
  154. if(ack && hash && hash === msg['##']){ return }
  155. if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit.
  156. //console.log(msg['#'], "CONTINUE:", ack, hash, msg['##']);
  157. chat(peers, old); // keep asking for data if there is mismatching hashes.
  158. }, 25);
  159. }
  160. return chat(peers);
  161. }
  162. // TODO: PUTs need to only go to subs!
  163. if(msg.put){
  164. var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
  165. var peers = {};
  166. Gun.obj.map(msg.put, function(node, soul){
  167. var hash = soul; //Gun.obj.hash({'#': soul});
  168. var to = routes[hash];
  169. if(!to){ return }
  170. Gun.obj.to(to, peers);
  171. });
  172. mesh.say(msg, peers);
  173. return;
  174. }
  175. mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT!
  176. if (!msg.put) { mesh.say(msg); return; }
  177. //console.log('AXE HOOK!! ', msg);
  178. verify(opt.dht, msg);
  179. };
  180. } else {
  181. mesh.route = function(msg) {
  182. if (msg.rtc) {
  183. }
  184. if (!msg.put) { mesh.say(msg); return; }
  185. verify(opt.dht, msg);
  186. /// Always send to superpeers?
  187. Gun.obj.map(peers, function(peer) {
  188. if (peer.url) {
  189. mesh.say(msg, peer);
  190. }
  191. });
  192. };
  193. /*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW!
  194. at.on('hi', function(opt) {
  195. this.to.next(opt);
  196. //console.log('AXE PEER [HI]', new Date(), opt);
  197. connections++;
  198. /// The first connection don't need to resubscribe the nodes.
  199. if (connections === 1) { return; }
  200. /// Resubscribe all nodes.
  201. setTimeout(function() {
  202. var souls = Object.keys(at.graph);
  203. for (var i=0; i < souls.length; ++i) {
  204. //at.gun.get(souls[i]).off();
  205. at.next[souls[i]].ack = 0;
  206. at.gun.get(souls[i]).once(function(){});
  207. }
  208. //location.reload();
  209. }, 500);
  210. }, at);*/
  211. }
  212. at.on('bye', function(peer){ this.to.next(peer);
  213. Gun.obj.map(peer.routes, function(route, hash){
  214. delete route[peer.id];
  215. if(Gun.obj.empty(route)){
  216. delete axe.routes[hash];
  217. }
  218. });
  219. });
  220. }
  221. function joindht(dht, soul, pids) {
  222. if (!pids || !soul || !dht) { return; }
  223. var subs = dht(soul);
  224. var tmp = subs ? subs.split(',') : [];
  225. Gun.obj.map(pids.split(','), function(pid) {
  226. if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); }
  227. });
  228. tmp = tmp.join(',');
  229. dht(soul, tmp);
  230. return tmp;
  231. }
  232. var empty = {}, yes = true, u;
  233. module.exports = AXE;
  234. })(USE, './axe');
  235. }());