refactor DAM & add PANIC tests

This commit is contained in:
Mark Nadal 2019-05-26 04:33:00 -07:00
parent a786944ed6
commit 209bdf2b06
3 changed files with 407 additions and 184 deletions

110
axe.js
View File

@ -34,9 +34,8 @@
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
Gun.on('opt', function(at){
if(!at.axe){
at.axe = {};
var opt = at.opt;
var peers = at.opt.peers, tmp;
var axe = at.axe = {}, tmp;
var opt = at.opt, peers = opt.peers;
// 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.
@ -54,7 +53,7 @@
// in case the p2p linear latency is high.
// Or there could be plenty of other better options.
var mesh = opt.mesh = opt.mesh || Gun.Mesh(at);
console.log("axe");
console.log("AXE enabled.");
function verify(dht, msg) {
var puts = Object.keys(msg.put);
@ -83,8 +82,52 @@
// this.to.next(msg);
// console.log('[AXE] out:', msg, a);
// }, at);
function input(msg){
var to = this.to, peer = (msg._||{}).via;
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){
hash = 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]);
}
}*/
}
to.next(msg);
if (opt.rtc && msg.dht) {
Gun.obj.map(msg.dht, function(pids, soul) {
dht(soul, pids);
Gun.obj.map(pids.split(','), 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);
});
});
}
}
if(at.opt.super){
mesh.route = function(msg) {
var rotate = 0;
mesh.way = function(msg) {
if (msg.rtc) {
// console.log('[AXE] MSG WEBRTC: ', msg.rtc);
if (msg.rtc.to) {
@ -94,6 +137,24 @@
return;
}
}
if(msg.get){
var hash = Gun.obj.hash(msg.get);
var routes = axe.routes || (axe.routes = {}); // USE RAD INSTEAD! TMP TESTING!
var peers = routes[hash];
if(!peers){ return mesh.say(msg, opt.peers) }
var ids = Object.keys(peers); // TODO: BUG! THIS IS BAD PERFORMANCE!!!!
var meta = (msg._||yes);
var id, peer, c = 1; // opt. ?redundancy?
//console.log("AXE:", msg.get, ids);
while(c-- && (id = ids[(meta.turn = (meta.turn || 0) + 1) - 1])){ // TODO: This has many flaws to it!
peer = peers[id];
if(false === mesh.say(msg, peer)){ ++c }
}
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);
@ -113,7 +174,7 @@
}
});
};
var connections = 0;
/*var connections = 0; // THIS HAS BEEN MOVED TO CORE NOW!
at.on('hi', function(opt) {
this.to.next(opt);
//console.log('AXE PEER [HI]', new Date(), opt);
@ -130,7 +191,7 @@
}
//location.reload();
}, 500);
}, at);
}, at);*/
}
}
this.to.next(at); // make sure to call the "next" middleware adapter.
@ -147,42 +208,9 @@
dht(soul, tmp);
return tmp;
}
function input(msg){
// console.log('[AXE] input: ', msg);
var at = this.as, to = this.to, peer = (msg._||{}).via;
var opt = at.opt;
var dht = opt.dht;
var get = msg.get, soul, key;
if(peer && get){
if(soul = get['#']){
if(key = get['.']){
} else {
var empty = {}, yes = true, u;
}
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;
at.opt.mesh.say({dht:dht}, opt.peers[peer.id]);
}
}
}
to.next(msg);
if (opt.rtc && msg.dht) {
Gun.obj.map(msg.dht, function(pids, soul) {
dht(soul, pids);
Gun.obj.map(pids.split(','), 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);
});
});
}
}
module.exports = AXE;
})(USE, './axe');
}());

283
gun.js
View File

@ -833,7 +833,7 @@
if(text_is(tmp)){ tmp = [tmp] }
if(list_is(tmp)){
tmp = obj_map(tmp, function(url, i, map){
map(url, {url: url});
i = {}; i.id = i.url = url; map(url, i);
});
if(!obj_is(at.opt.peers)){ at.opt.peers = {}}
at.opt.peers = obj_to(tmp, at.opt.peers);
@ -1459,11 +1459,11 @@
var cat = (as.$.back(-1)._), ask = cat.ask(function(ack){
cat.root.on('ack', ack);
if(ack.err){ Gun.log(ack) }
if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this.
if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default.
if(!as.ack){ return }
as.ack(ack, this);
//--C;
}, as.opt);
}, as.opt), acks = 0;
//C++;
// NOW is a hack to get synchronous replies to correctly call.
// and STOP is a hack to get async behavior to correctly call.
@ -1937,66 +1937,43 @@
})(USE, './adapters/localStorage');
;USE(function(module){
var Gun = USE('../index');
var Type = USE('../type');
function Mesh(ctx){
function Mesh(root){
var mesh = function(){};
var opt = ctx.opt || {};
var opt = root.opt || {};
opt.log = opt.log || console.log;
opt.gap = opt.gap || opt.wait || 1;
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
mesh.out = function(msg){ var tmp;
if(this.to){ this.to.next(msg) }
//if(mesh.last != msg['#']){ return mesh.last = msg['#'], this.to.next(msg) }
if((tmp = msg['@'])
&& (tmp = ctx.dup.s[tmp])
&& (tmp = tmp.it)
&& tmp._){
mesh.say(msg, (tmp._).via, 1);
tmp['##'] = msg['##'];
return;
}
if(mesh.route){ mesh.route(msg); return }
mesh.say(msg);
}
ctx.on('create', function(root){
root.opt.pid = root.opt.pid || Type.text.random(9);
this.to.next(root);
ctx.on('out', mesh.out);
});
var dup = root.dup;
mesh.hear = function(raw, peer){
if(!raw){ return }
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
var msg, id, hash, tmp = raw[0];
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
if('{' === tmp){
try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
try{msg = JSON.parse(raw);
}catch(e){return opt.log('DAM JSON parse error', e)}
if(!msg){ return }
mesh.hear.d += raw.length; ++mesh.hear.c; // STATS!
if(dup.check(id = msg['#'])){ return }
dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed.
if((tmp = msg['@']) && msg.put){
hash = msg['##'] || (msg['##'] = mesh.hash(msg));
if((tmp = tmp + hash) != id){
if(dup.check(tmp)){ return }
(tmp = dup.s)[hash] = tmp[id];
}
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(dup.check(id)){ return }
dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(hash && (tmp = msg['@'])){
if(dup.check(tmp+hash)){ return }
dup.track(tmp+hash, true).it = msg; // GUN core also dedups, so `true` is needed. // Does GUN core need to dedup anymore?
}
(msg._ = function(){}).via = peer;
if((tmp = msg['><'])){
(msg._).to = Type.obj.map(tmp.split(','), tomap);
}
if(tmp = msg['><']){ (msg._).to = Type.obj.map(tmp.split(','), tomap) }
if(msg.dam){
if(tmp = mesh.hear[msg.dam]){
tmp(msg, peer, ctx);
tmp(msg, peer, root);
}
return;
}
ctx.on('in', msg);
root.on('in', msg);
return;
} else
if('[' === tmp){
@ -2006,147 +1983,125 @@
while(m = msg[i++]){
mesh.hear(m, peer);
}
return;
}
}
mesh.hear.c = mesh.hear.d = 0;
var tomap = function(k,i,m){m(k,true)};
mesh.hear.c = mesh.hear.d = 0;
;(function(){
var message;
function each(peer){ mesh.say(message, peer) }
mesh.say = function(msg, peer, o){ var tmp;
/*
TODO: Plenty of performance optimizations
that can be made just based off of ordering,
and reducing function calls for cached writes.
*/
var meta = (msg._) || empty, raw;
if(!(raw = meta.raw)){ raw = mesh.raw(msg) }
if((tmp = msg['@'])
&& (tmp = ctx.dup.s[tmp])
&& (tmp = tmp.it)){
if(tmp.get && tmp['##'] && tmp['##'] === msg['##']){ // PERF: move this condition outside say?
return; // TODO: this still needs to be tested in the browser!
mesh.say = function(msg, peer){
if(this.to){ this.to.next(msg) } // compatible with middleware adapters.
if(!msg){ return false }
var id, hash, tmp, raw;
var meta = msg._||(msg._=function(){});
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
if(!(hash = msg['##']) && u !== msg.put){ hash = msg['##'] = Type.obj.hash(msg.put) }
if(!(raw = meta.raw)){
raw = meta.raw = mesh.raw(msg);
if(hash && (tmp = msg['@'])){
dup.track(tmp+hash).it = msg;
if(tmp = (dup.s[tmp]||ok).it){
if(hash === tmp['##']){ return false }
tmp['##'] = hash;
}
}
}
if(!peer){ message = msg;
Type.obj.map(opt.peers, each);
dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more!
if(!peer){ peer = (tmp = dup.s[msg['@']]) && (tmp = tmp.it) && (tmp = tmp._) && (tmp = tmp.via) }
if(!peer && mesh.way){ return mesh.way(msg) }
if(!peer || !peer.id){ message = msg;
if(!Type.obj.is(peer || opt.peers)){ return false }
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
return;
}
var wire = peer.wire || ((opt.wire) && opt.wire(peer));// || open(peer, ctx); // TODO: Reopen!
if(!wire){ return }
if(peer === meta.via){ return }
if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
if(peer === meta.via){ return false }
if((tmp = meta.to) && (tmp[peer.url] || tmp[peer.pid] || tmp[peer.id]) /*&& !o*/){ return false }
if(peer.batch){
peer.tail = (peer.tail || 0) + raw.length;
peer.tail = (tmp = peer.tail || 0) + raw.length;
if(peer.tail <= opt.pack){
peer.batch.push(raw);
peer.batch.push(raw); // peer.batch += (tmp?'':',')+raw; // TODO: Prevent double JSON! // FOR v1.0 !?
return;
}
flush(peer);
}
peer.batch = [];
peer.batch = []; // peer.batch = '['; // TODO: Prevent double JSON!
setTimeout(function(){flush(peer)}, opt.gap);
send(raw, peer);
}
function flush(peer){
var tmp = peer.batch;
if(!tmp){ return }
var tmp = peer.batch; // var tmp = peer.batch + ']'; // TODO: Prevent double JSON!
peer.batch = peer.tail = null;
if(!tmp.length){ return }
try{send(JSON.stringify(tmp), peer);
}catch(e){opt.log('DAM JSON stringify error', e)}
}
function send(raw, peer){
var wire = peer.wire;
try{
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
mesh.say.d += raw.length; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
}
if(!tmp){ return }
if(!tmp.length){ return } // if(3 > tmp.length){ return } // TODO: ^
try{tmp = (1 === tmp.length? tmp[0] : JSON.stringify(tmp));
}catch(e){return opt.log('DAM JSON stringify error', e)}
if(!tmp){ return }
send(tmp, peer);
}
mesh.say.c = mesh.say.d = 0;
}());
// for now - find better place later.
function send(raw, peer){ try{
var wire = peer.wire;
if(peer.say){
peer.say(raw);
} else
if(wire.send){
wire.send(raw);
}
mesh.say.d += raw.length; ++mesh.say.c; // STATS!
}catch(e){
(peer.queue = peer.queue || []).push(raw);
}}
;(function(){
mesh.raw = function(msg){
mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out!
if(!msg){ return '' }
var dup = ctx.dup, meta = (msg._) || {}, put, hash, tmp;
var meta = (msg._) || {}, put, hash, tmp;
if(tmp = meta.raw){ return tmp }
if(typeof msg === 'string'){ return msg }
if(msg['@'] && (tmp = msg.put)){
if(!(hash = msg['##'])){
put = $(tmp, sort) || '';
hash = mesh.hash(msg, put);
msg['##'] = hash;
}
(tmp = dup.s)[hash = msg['@']+hash] = tmp[msg['#']];
msg['#'] = hash || msg['#'];
if(put){ (msg = Type.obj.to(msg)).put = _ }
}
if(!msg.dam){
var i = 0, to = []; Type.obj.map(opt.peers, function(p){
to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later!
to.push(p.url || p.pid || p.id); if(++i > 9){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids.
}); msg['><'] = to.join();
}
var raw = $(msg);
if(u !== put){
var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash?
/*if(u !== put){
tmp = raw.indexOf(_, raw.indexOf('put'));
raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1);
//raw = raw.replace('"'+ _ +'"', put); // https://github.com/amark/gun/wiki/@$$ Heisenbug
}
if(meta){
meta.raw = raw;
}
//raw = raw.replace('"'+ _ +'"', put); // NEVER USE THIS! ALSO NEVER DELETE IT TO NOT MAKE SAME MISTAKE! https://github.com/amark/gun/wiki/@$$ Heisenbug
}*/
if(meta){ meta.raw = raw }
return raw;
}
mesh.hash = function(msg, hash){
return Mesh.hash(hash || $(msg.put, sort) || '') || msg['#'] || Type.text.random(9);
}
function sort(k, v){ var tmp;
if(!(v instanceof Object)){ return v }
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
return tmp;
}
function map(k){
this.to[k] = this.on[k];
}
var $ = JSON.stringify, _ = ':])([:';
}());
mesh.hi = function(peer){
var tmp = peer.wire || {};
if(peer.id || peer.url){
if(peer.id){
opt.peers[peer.url || peer.id] = peer;
} else {
tmp = peer.id = peer.id || Type.text.random(9);
mesh.say({dam: '?'}, opt.peers[tmp] = peer);
}
peer.met = peer.met || +(new Date);
if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) }
if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) }
// @rogowski I need this here by default for now to fix go1dfish's bug
tmp = peer.queue; peer.queue = [];
Type.obj.map(tmp, function(msg){
mesh.say(msg, peer);
send(msg, peer);
});
}
mesh.bye = function(peer){
Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect
ctx.on('bye', peer);
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
}
@ -2155,31 +2110,73 @@
if(!msg.pid){
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
// @rogowski I want to re-enable this AXE logic with some fix/merge later.
// var tmp = peer.queue; peer.queue = [];
// Type.obj.map(tmp, function(msg){
// mesh.say(msg, peer);
// });
/* var tmp = peer.queue; peer.queue = [];
Type.obj.map(tmp, function(msg){
mesh.say(msg, peer);
}); */
// @rogowski 2: I think with my PID fix we can delete this and use the original.
return;
}
if(peer.pid){ return }
peer.pid = msg.pid;
}
root.on('create', function(root){
root.opt.pid = root.opt.pid || Type.text.random(9);
this.to.next(root);
root.on('out', mesh.say);
});
if(!opt.super){
var gets = {};
root.on('bye', function(peer, tmp){
if(!(tmp = peer.url)){ return } gets[tmp] = true;
setTimeout(function(){ delete gets[tmp] },opt.lack || 9000)
});
root.on('hi', function(peer, tmp){
if(!(tmp = peer.url) || !gets[tmp]){ return } delete gets[tmp];
Type.obj.map(root.graph, function(node, soul){ tmp = {}; tmp[soul] = node;
mesh.say({'##': Type.obj.hash(tmp), get: {'#': soul}}, peer);
})
});
}
return mesh;
}
Mesh.hash = function(s){ // via SO
if(typeof s !== 'string'){ return {err: 1} }
var c = 0;
if(!s.length){ return c }
for(var i=0,l=s.length,n; i<l; ++i){
n = s.charCodeAt(i);
c = ((c<<5)-c)+n;
c |= 0;
}
return c; // Math.abs(c);
}
;(function(){
Type.text.hash = function(s){ // via SO
if(typeof s !== 'string'){ return {err: 1} }
var c = 0;
if(!s.length){ return c }
for(var i=0,l=s.length,n; i<l; ++i){
n = s.charCodeAt(i);
c = ((c<<5)-c)+n;
c |= 0;
}
return c; // Math.abs(c);
}
var $ = JSON.stringify, u;
var empty = {}, u;
Type.obj.hash = function(obj, hash){
if(!hash && u === (obj = $(obj, sort))){ return }
return Type.text.hash(hash || obj || '');
}
function sort(k, v){ var tmp;
if(!(v instanceof Object)){ return v }
Type.obj.map(Object.keys(v).sort(), map, {to: tmp = {}, on: v});
return tmp;
}
Type.obj.hash.sort = sort;
function map(k){
this.to[k] = this.on[k];
}
}());
var empty = {}, ok = true, u;
Object.keys = Object.keys || function(o){ return map(o, function(v,k,t){t(k)}) }
try{ module.exports = Mesh }catch(e){}
@ -2207,8 +2204,8 @@
var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
var wire = opt.wire;
opt.wire = open;
var wire = mesh.wire || opt.wire;
mesh.wire = opt.wire = open;
function open(peer){ try{
if(!peer || !peer.url){ return wire && wire(peer) }
var url = peer.url.replace('http', 'ws');

198
test/panic/1putackget.js Normal file
View File

@ -0,0 +1,198 @@
/*
This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
1. (this file) Is a browser write is confirmed as save by multiple peers even if by daisy chain.
2.
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 2,
browsers: 2,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var bob = servers.pluck(1);
var carl = servers.excluding(bob).pluck(1);
var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var dave = browsers.excluding(alice).pluck(1);
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
console.log("PLEASE OPEN http://"+ config.IP +":"+ config.port +" IN "+ config.browsers +" BROWSER(S)!");
return browsers.atLeast(config.browsers);
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(client, id){
tests.push(client.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.ref = gun.get('a');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Put", function(){
return alice.run(function(test){
console.log("I AM ALICE");
test.async();
var c = test.props.acks, acks = {};
c = c < 2? 2 : c;
ref.put({hello: 'world'}, function(ack){
//console.log("acks:", ack, c);
acks[ack['#']] = 1;
if(Object.keys(acks).length == c){
wire();
return test.done();
}
}, {acks: c});
function wire(){
var hear = ref._.root.opt.mesh.hear;
ref._.root.opt.mesh.hear = function(raw, peer){
var msg = Gun.obj.ify(raw);
console.log('hear:', msg);
hear(raw, peer);
(ref.hear || (ref.hear = [])).push(msg);
}
var say = ref._.root.opt.mesh.say;
ref._.root.opt.mesh.say = function(raw, peer){
var yes = say(raw, peer);
if(yes === false){ return }
console.log("say:", msg, yes);
(ref.say || (ref.say = [])).push(Gun.obj.ify(msg));
}
}
}, {acks: config.servers});
});
it("Get", function(){
/*
Here is the recursive rule for GET, keep replying while hashes mismatch.
1. Receive a GET message.
2. If it has a hash, and if you have a thing matching the GET, then see if the hashes are the same, if they are then don't ACK, don't relay, end.
3. If you would have the thing but do not, then ACK that YOU have nothing.
4. If you have a thing matching the GET or an ACK for the GET's message, add the hash to the GET message, and ACK with the thing or ideally the remaining difference.
5. Pick ?3? OTHER peers preferably by priority that they have got the thing, send them the GET, plus all "up" peers.
6. If no ACKs you are done, end.
7. If you get ACKs back to the GET with things and different hashes, optionally merge into the thing you have GOT and update the hash.
8. Go to 4.
*/
return dave.run(function(test){
console.log("I AM DAVE");
test.async();
var c = 0, to;
var hear = ref._.root.opt.mesh.hear;
ref._.root.opt.mesh.hear = function(raw, peer){
var msg = Gun.obj.ify(raw);
console.log('hear:', msg);
hear(raw, peer);
(ref.hear || (ref.hear = [])).push(msg);
if(msg.put){ ++c }
}
ref.get(function(ack){
if(!ack.put || ack.put.hello !== 'world'){ return }
if(c > 1){ too_many_acks }
clearTimeout(to);
to = setTimeout(test.done, 1000);
});
}, {acks: config.servers});
});
it("DAM", function(){
return alice.run(function(test){
test.async();
if(ref.say){ said_too_much }
if(ref.hear.length > 1){ heard_to_much }
test.done()
}, {acks: config.servers});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
browsers.run(function(){
//location.reload();
//setTimeout(function(){
//}, 15 * 1000);
});
return servers.run(function(){
process.exit();
});
});
});