From c42d2bc23ef09c9bbc00910f7579a6f5bea68028 Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Fri, 2 Jul 2021 13:00:07 -0700 Subject: [PATCH] axe polyfill for now --- axe.js | 301 +++------------------------------------------------------ gun.js | 5 +- 2 files changed, 14 insertions(+), 292 deletions(-) diff --git a/axe.js b/axe.js index bfd2d4d4..43318bcf 100644 --- a/axe.js +++ b/axe.js @@ -36,297 +36,18 @@ var opt = at.opt, peers = opt.peers; if(false === opt.axe){ return } if((typeof process !== "undefined") && 'false' === ''+(process.env||'').AXE){ return } - var axe = at.axe = {}, tmp; - // 1. If any remembered peers or from last cache or extension - // 2. Fallback to use hard coded peers from dApp - // 3. Or any offered peers. - //if(Gun.obj.empty(p)){ - // Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){ - // p[url] = {url: url, axe: {}}; - // }); - //} - // Our current hypothesis is that it is most optimal - // to take peers in a common network, and align - // them in a line, where you only have left and right - // peers, so messages propagate left and right in - // a linear manner with reduced overlap, and - // with one common superpeer (with ready failovers) - // in case the p2p linear latency is high. - // Or there could be plenty of other better options. - - /* - AXE should have a couple of threshold items... - let's pretend there is a variable max peers connected - mob = 10000 - if we get more peers than that... - we should start sending those peers a remote command - that they should connect to this or that other peer - and then once they (or before they do?) drop them from us. - sake of the test... gonna set that peer number to 1. - The mob threshold might be determined by other factors, - like how much RAM or CPU stress we have. - */ - opt.mob = opt.mob || 9876 || Infinity; - var mesh = opt.mesh = opt.mesh || Gun.Mesh(at); - console.log("AXE enabled."); - - function verify(dht, msg){ - var S = (+new Date); - var puts = Object.keys(msg.put); - var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls? - var subs = dht(soul); - if (!subs) { return; } - var tmp = []; - (subs.split(',')||[]).forEach(function(pid){ - if (pid in peers) { - tmp.push(pid); - mesh.say(msg, peers[pid]); - } - }); - /// Only connected peers in the tmp array. - if (opt.super) { - dht(soul, tmp.join(',')); - } - console.STAT && console.STAT(S, +new Date - S, 'axe verify'); - } - function route(get){ var tmp; - if(!get){ return } - if('string' != typeof (tmp = get['#'])){ return } - return tmp; - } - // TODO: AXE NEEDS TO BE CHECKED FOR NEW CODE SYSTEM!!!!!!!!!! - - var Rad = (Gun.window||'').Radix || USE('./lib/radix', 1); - at.opt.dht = Rad(); - at.on('in', input); - function input(msg){ - var to = this.to, peer = (msg._||'').via; // warning! mesh.leap could be buggy! - var dht = opt.dht; - var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! - var get = msg.get, hash, tmp; - //if(get && opt.super && peer){ - if(get && opt.super && peer && (tmp = route(get))){ - hash = tmp; //Gun.obj.hash(get); // USE RAD INSTEAD! - (routes[hash] || (routes[hash] = {}))[peer.id] = peer; - (peer.routes || (peer.routes = {}))[hash] = routes[hash]; - - /*if(soul = get['#']){ // SWITCH BACK TO USING DHT! - if(key = get['.']){ - - } else { - - } - if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);} - var pids = joindht(dht, soul, peer.id); - if (pids) { - var dht = {}; - dht[soul] = pids; - mesh.say({dht:dht}, opt.peers[peer.id]); - } - }*/ - } - if((tmp = msg['@']) && (tmp = at.dup.s[tmp])){ - tmp.ack = (tmp.ack || 0) + 1; // count remote ACKs to GET. - } - to.next(msg); - - if (opt.rtc && msg.dht) { - Object.keys(msg.dht).forEach(function(soul, pids) { pids = msg.dht[soul]; - dht(soul, pids); - (pids.split(',')||[]).forEach(function(pid) { - /// TODO: here we can put an algorithm of who must connect? - if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; } - opt.announce[pid] = true; /// To try only one connection to the same peer. - opt.announce(pid); - }); - }); - } - }; - - //try{console.log(req.connection.remoteAddress)}catch(e){}; - mesh.hear['opt'] = function(msg, peer){ - if(msg.ok){ return opt.log(msg) } - var tmp = msg.opt; - if(!tmp){ return } - tmp = tmp.peers; - if(!tmp || 'string' != typeof tmp){ return } - if(axe.up[tmp] || 6 <= Object.keys(axe.up).length){ return } - var o = tmp; //{peers: tmp}; - at.$.opt(o); - o = peers[tmp]; - if(!o){ return } - o.retry = 9; - mesh.wire(o); - if(peer){ mesh.say({dam: 'opt', ok: 1, '@': msg['#']}, peer) } - } - setInterval(function(tmp){ - if(!(tmp = at.stats && at.stats.stay)){ return } - (tmp.axe = tmp.axe || {}).up = Object.keys(axe.up||{}); - },1000 * 60) - setTimeout(function(tmp){ - if(!(tmp = at.stats && at.stats.stay)){ return } - ((tmp.axe||{}).up||[]).forEach(function(url){ mesh.hear.opt({opt: {peers: url}}) }) - },1000); - - if(at.opt.super){ - var rotate = 0; - mesh.way = function(msg) { - if (msg.rtc) { - if (msg.rtc.to) { - /// Send announce to one peer only if the msg have 'to' attr - var peer = (peers) ? peers[msg.rtc.to] : null; - if (peer) { mesh.say(msg, peer); } - return; - } - } - if(msg.get){ mesh.say(msg, axe.up) } // always send gets up! // send gets up to only rad's up - if(msg.get && (tmp = route(msg.get))){ - var hash = tmp; //Gun.obj.hash(msg.get); - var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! - var peers = routes[hash]; - function chat(peers, old){ // what about optimizing for directed peers? - if(!peers){ return chat(opt.peers) } - var S = (+new Date); // STATS! - var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!! - console.STAT && console.STAT(S, +new Date - S, 'axe peer keys'); - var meta = (msg._||yes); - clearTimeout(meta.lack); - var id, peer, c = 1; // opt. ?redundancy? - 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? - peer = peers[id]; - meta.turn = (meta.turn || 0) + 1; - if((old && old[id]) || false === mesh.say(msg, peer)){ ++c } - } - console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'axe chat'); - //console.log("AXE:", Gun.obj.copy(msg), meta.turn, c, ids, opt.peers === peers); - if(0 < c){ - if(peers === opt.peers){ return } // prevent infinite lack loop. - return meta.turn = 0, chat(opt.peers, peers) - } - var hash = msg['##'], ack = meta.ack || at.dup.s[msg['#']]; - meta.lack = setTimeout(function(){ - if(ack && hash && hash === msg['##']){ return } - if(meta.turn >= (axe.turns || 3)){ return } // variable for later! Also consider ACK based turn limit. - //console.log(msg['#'], "CONTINUE:", ack, hash, msg['##']); - chat(peers, old); // keep asking for data if there is mismatching hashes. - }, 25); - } - return chat(peers); - } - // TODO: PUTs need to only go to subs! - if(msg.put){ - mesh.say(msg, axe.up); // always send gets up! Hope that mesh.say below dedups via DAM's check. - var S = (+new Date); // STATS! - var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING! - var peers = {}; - //Object.keys(msg.put).forEach(function(soul, node){ node = msg.put[soul]; - setTimeout.each(Object.keys(msg.put), function(soul, node){ node = msg.put[soul]; - var hash = soul; //Gun.obj.hash({'#': soul}); - var to = routes[hash]; - if(!to){ return } - setTimeout.each(Object.keys(to), function(k){ - mesh.say(msg, to[k]); //peers[k] = to[k] - }); - }); - console.STAT && (ST = +new Date - S) > 9 && console.STAT(S, ST, 'axe put'); - //mesh.say(msg, peers); - return; - } - mesh.say(msg, opt.peers); return; // TODO: DISABLE THIS!!! USE DHT! - - - if (!msg.put) { mesh.say(msg); return; } - //console.log('AXE HOOK!! ', msg); - verify(opt.dht, msg); - }; - } else { - mesh.route = function(msg) { - if (msg.rtc) { - } - if (!msg.put) { mesh.say(msg); return; } - verify(opt.dht, msg); - /// Always send to superpeers? - Gun.obj.map(peers, function(peer) { - if (peer.url) { - mesh.say(msg, peer); - } - }); - }; - } - axe.up = {}; - var C = 0; - at.on('hi', function(peer){ C++; - this.to.next(peer); - if(!peer.url){ return } - axe.up[peer.id] = peer; + var axe = at.axe = {}, tmp, id; + tmp = peers[id = 'http://localhost:8765/gun'] = peers[id] || {}; + tmp.id = tmp.url = id; + tmp.retry = tmp.retry || 2; // BUG: Check 0? + console.log("Attempting to discover network via (1) local peer (2) last used peers (3) hardcoded peers!"); + var last = JSON.parse((localStorage||'')[(opt.file||'')+'axe/']||null) || {}; + Object.keys(last.peers||'').forEach(function(key){ + tmp = peers[id = key] = peers[id] || {}; + tmp.id = tmp.url = id; }); - at.on('bye', function(peer){ C--; this.to.next(peer); - if(peer.url){ delete axe.up[peer.id] } - var S = +new Date; - setTimeout.each(Object.keys(peer.routes||''), function(hash, route){ - route = peer.routes[hash]; - delete route[peer.id]; - if(Object.empty(route)){ - delete axe.routes[hash]; - } - }); - console.STAT && console.STAT(S, +new Date - S, 'axe bye'); - }); - - // handle rebalancing a mob of peers: - at.on('hi', function(peer){ - this.to.next(peer); - if(peer.url){ return } // I am assuming that if we are wanting to make an outbound connection to them, that we don't ever want to drop them unless our actual config settings change. - var count = C; - if(opt.mob >= count){ return } // TODO: Make dynamic based on RAM/CPU also. Or possibly even weird stuff like opt.mob / axe.up length? - var peers = Object.keys(axe.up); - if(!peers.length){ return } - mesh.say({dam: 'mob', mob: count, peers: peers}, peer); - //setTimeout(function(){ mesh.bye(peer) }, 9); // something with better perf? // UNCOMMENT WHEN WE ACTIVATE THIS FEATURE - }); - at.on('bye', function(peer){ - this.to.next(peer); - }); - - at.on('hi', function(peer){ - this.to.next(peer); - // this code handles disconnecting from self & duplicates - setTimeout(function(){ // must wait - if(peer.pid !== opt.pid){ - // this extra logic checks for duplicate connections between 2 peers. - if(!obj_map(axe.up, function(p){ - if(peer.pid === p.pid && peer !== p){ - return yes = true; - } - })){ return } - } - mesh.say({dam: '-'}, peer); - delete at.dup.s[peer.last]; - }, Math.random() * 100); - }); - mesh.hear['-'] = function(msg, peer){ - mesh.bye(peer); - peer.url = ''; - } - } - - var obj_map = function(o, f, r){ - for(var k in o){ - if(!o.hasOwnProperty(k)){ continue } - if((r = f(o[k], k)) !== u){ return r } - } - } - - function joindht(dht, soul, pids) { - if (!pids || !soul || !dht) { return; } - var subs = dht(soul); - var tmp = subs ? subs.split(',') : []; - Gun.obj.map(pids.split(','), function(pid) { - if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); } - }); - tmp = tmp.join(','); - dht(soul, tmp); - return tmp; + tmp = peers[id = 'https://gun-manhattan.herokuapp.com/gun'] = peers[id] || {}; + tmp.id = tmp.url = id; } var empty = {}, yes = true, u; diff --git a/gun.js b/gun.js index 3b974f2f..5d8b5eb7 100644 --- a/gun.js +++ b/gun.js @@ -343,7 +343,7 @@ ++ni; kl = null; pop(o); }()); } Gun.on.put = put; - console.log("TODO: Mark: HAM is unfinished, new acks, RAD, etc."); + console.log("BEWARE: BETA VERSION OF NEW GUN! NOT ALL FEATURES FINISHED!"); function ham(val, key, soul, state, msg){ var ctx = msg._||'', root = ctx.root, graph = root.graph, lot, tmp; var vertex = graph[soul] || empty, was = state_is(vertex, key, 1), known = vertex[key]; @@ -419,6 +419,7 @@ // TODO: consider tagging original message into dup for DAM. var ctx = msg._||{}, DBG = ctx.DBG = msg.DBG; DBG && (DBG.g = +new Date); + console.log("GET:", get, node, has); if(!node){ return root.on('get', msg) } if(has){ if('string' != typeof has || u === node[has]){ return root.on('get', msg) } @@ -1641,7 +1642,7 @@ var lex = msg.get, soul, data, tmp, u; if(!lex || !(soul = lex['#'])){ return } data = disk[soul] || u; - if(data && (tmp = lex['.'])){ // pluck! + if(data && (tmp = lex['.']) && !Object.plain(tmp)){ // pluck! data = Gun.state.ify({}, tmp, Gun.state.is(data, tmp), data[tmp], soul); } if(data){ (tmp = {})[soul] = data } // back into a graph.