mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
Merge branch 'master' of https://github.com/amark/gun into amark-master
This commit is contained in:
commit
fd26e39acc
35
README.md
35
README.md
@ -9,7 +9,7 @@
|
||||
|
||||
Currently, [Internet Archive](https://news.ycombinator.com/item?id=17685682) and HackerNoon run GUN in production.
|
||||
|
||||
Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
|
||||
Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. have already pushed terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@ -117,7 +117,8 @@ Thanks to:<br/>
|
||||
<a href="http://github.com/velua">John Williamson</a>,
|
||||
<a href="http://github.com/finwo">Robin Bron</a>,
|
||||
<a href="http://github.com/ElieMakhoul">Elie Makhoul</a>,
|
||||
<a href="http://github.com/mikestaub">Mike Staub</a>
|
||||
<a href="http://github.com/mikestaub">Mike Staub</a>,
|
||||
<a href="http://github.com/bmatusiak">Bradley Matusiak</a>
|
||||
</p>
|
||||
|
||||
- Join others in sponsoring code: https://www.patreon.com/gunDB !
|
||||
@ -185,28 +186,24 @@ rm -rf *data*
|
||||
|
||||
### Additional Cryptography Libraries
|
||||
|
||||
To install with npm, first install `npm install gun -S`.
|
||||
For just the networking layer, import Gun:
|
||||
> These are only needed for NodeJS, they shim the native Browser WebCrypto API.
|
||||
|
||||
If you want to use [SEA](https://gun.eco/docs/SEA) for `User` auth and security, you will need to install:
|
||||
|
||||
`npm install text-encoding isomorphic-webcrypto --save`
|
||||
|
||||
Then you can require [SEA](https://gun.eco/docs/SEA) without an error:
|
||||
|
||||
```javascript
|
||||
var Gun = require('gun/gun');
|
||||
var GUN = require('gun/gun');
|
||||
var SEA = require('gun/sea');
|
||||
```
|
||||
|
||||
If you also need to install SEA for user auth and crypto, also install some of its dependencies like this:
|
||||
|
||||
`npm install text-encoding @peculiar/webcrypto --save`
|
||||
|
||||
You will need to require it too (it will be automatically added to the Gun object):
|
||||
|
||||
```javascript
|
||||
var Gun = require('gun/gun');
|
||||
var Sea = require('gun/sea');
|
||||
```
|
||||
|
||||
|
||||
## Deploy
|
||||
|
||||
To quickly spin up a Gun test server for your development team, utilize either [Heroku](http://heroku.com) or [Docker](http://docker.com) or any variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), [Flynn.io](http://flynn.io), [now.sh](https://zeit.co/now), etc. !
|
||||
> Note: The default examples that get auto-deployed on `npm start` CDN-ify all GUN files, modules, & storage.
|
||||
|
||||
To quickly spin up a GUN relay peer for your development team, utilize either [Heroku](http://heroku.com), [Docker](http://docker.com), any variant thereof [Dokku](http://dokku.viewdocs.io/dokku/), [Flynn.io](http://flynn.io), [now.sh](https://zeit.co/now), etc. ! Or use all of them so your relays are decentralized too!
|
||||
|
||||
### [Heroku](https://www.heroku.com/)
|
||||
|
||||
@ -243,6 +240,8 @@ Then visit the deployed app by following the 'view app' button, in your browser.
|
||||
|
||||
### [Docker](https://www.docker.com/)
|
||||
|
||||
> Warning: Docker image is community contributed and may be old with missing security updates, please check version numbers to compare.
|
||||
|
||||
[](https://hub.docker.com/r/gundb/gun/) [](https://microbadger.com/images/gundb/gun "Get your own image badge on microbadger.com") [](https://hub.docker.com/r/gundb/gun/) [](https://hub.docker.com/r/gundb/gun/)
|
||||
|
||||
Pull from the [Docker Hub](https://hub.docker.com/r/gundb/gun/) [](https://microbadger.com/images/gundb/gun). Or:
|
||||
|
2
axe.js
2
axe.js
@ -129,7 +129,7 @@
|
||||
}*/
|
||||
}
|
||||
if((tmp = msg['@']) && (tmp = at.dup.s[tmp]) && (tmp = tmp.it)){
|
||||
(tmp = (tmp._||ok)).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
|
||||
(tmp = (tmp._||{})).ack = (tmp.ack || 0) + 1; // count remote ACKs to GET.
|
||||
}
|
||||
to.next(msg);
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765;
|
||||
|
||||
var Gun = require('../');
|
||||
|
||||
// have to do this before instancing gun(?)
|
||||
Gun.on('out', function(msg){
|
||||
this.to.next( msg );
|
||||
msg = JSON.stringify(msg);
|
||||
gunPeers.forEach( function(peer){ peer.send( msg ) })
|
||||
})
|
||||
|
||||
var gun = Gun({
|
||||
file: 'data.json'
|
||||
});
|
||||
|
||||
var server = require('http').createServer(function(req, res){
|
||||
var insert = "";
|
||||
if( req.url.endsWith( "gun.js" ) )
|
||||
insert = "../";
|
||||
|
||||
require('fs').createReadStream(require('path').join(__dirname, insert, req.url)).on('error',function(){ // static files!
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end(require('fs').readFileSync(require('path').join(__dirname, 'index.html'))); // or default to index
|
||||
}).pipe(res); // stream
|
||||
});
|
||||
|
||||
// do not do this to attach server... instead pull websocket provider and use that.
|
||||
// gun.wsp(server);
|
||||
|
||||
var ws = require( 'ws' ); // default websocket provider gun used...
|
||||
var WebSocketServer = ws.Server;
|
||||
|
||||
var wss = new WebSocketServer( {
|
||||
server: server, // 'ws' npm
|
||||
autoAcceptConnections : false // want to handle the request (websocket npm?)
|
||||
});
|
||||
|
||||
wss.on('connection',acceptConnection )
|
||||
|
||||
var gunPeers = []; // used as a list of connected clients.
|
||||
|
||||
function acceptConnection( connection ) {
|
||||
// connection.upgradeReq.headers['sec-websocket-protocol'] === (if present) protocol requested by client
|
||||
// connection.upgradeReq.url === url request
|
||||
console.log( "connect?", connection.upgradeReq.headers, connection.upgradeReq.url )
|
||||
gunPeers.push( connection );
|
||||
connection.on( 'error',function(error){console.log( "WebSocket Error:", error) } );
|
||||
|
||||
connection.on('message', function (msg) {
|
||||
msg = JSON.parse(msg)
|
||||
if ("forEach" in msg) msg.forEach(m => gun.on('in', JSON.parse(m)));
|
||||
else gun.on('in', msg)
|
||||
})
|
||||
|
||||
connection.on( 'close', function(reason,desc){
|
||||
// gunpeers gone.
|
||||
var i = gunPeers.findIndex( function(p){return p===connection} );
|
||||
if( i >= 0 )
|
||||
gunPeers.splice( i, 1 );
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
server.listen(port);
|
||||
|
||||
console.log('Server started on port ' + port + ' with ');
|
@ -6,6 +6,7 @@
|
||||
# Copy paste and run each line into your terminal.
|
||||
# If you are on Windows, http://nodejs.org/download/ has
|
||||
# an installer that will automatically do it for you.
|
||||
# curl -o- https://raw.githubusercontent.com/amark/gun/master/examples/install.sh | bash
|
||||
|
||||
#debian/ubuntu
|
||||
su -
|
||||
|
73
gun.js
73
gun.js
@ -619,7 +619,7 @@
|
||||
var Type = USE('./type');
|
||||
function Dup(opt){
|
||||
var dup = {s:{}};
|
||||
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
|
||||
opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3};
|
||||
dup.check = function(id){ var tmp;
|
||||
if(!(tmp = dup.s[id])){ return false }
|
||||
if(tmp.pass){ return tmp.pass = false }
|
||||
@ -629,18 +629,17 @@
|
||||
var it = dup.s[id] || (dup.s[id] = {});
|
||||
it.was = time_is();
|
||||
if(pass){ it.pass = true }
|
||||
if(!dup.to){
|
||||
dup.to = setTimeout(function(){
|
||||
var now = time_is();
|
||||
Type.obj.map(dup.s, function(it, id){
|
||||
if(it && opt.age > (now - it.was)){ return }
|
||||
Type.obj.del(dup.s, id);
|
||||
});
|
||||
dup.to = null;
|
||||
}, opt.age + 9);
|
||||
}
|
||||
if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) }
|
||||
return it;
|
||||
}
|
||||
dup.drop = function(age){
|
||||
var now = time_is();
|
||||
Type.obj.map(dup.s, function(it, id){
|
||||
if(it && (age || opt.age) > (now - it.was)){ return }
|
||||
Type.obj.del(dup.s, id);
|
||||
});
|
||||
dup.to = null;
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
var time_is = Type.time.is;
|
||||
@ -702,7 +701,8 @@
|
||||
return;
|
||||
}
|
||||
dup.track(tmp);
|
||||
if(!at.ask(msg['@'], msg)){
|
||||
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness.
|
||||
if(!at.ask(tmp, msg)){
|
||||
if(msg.get){
|
||||
Gun.on.get(msg, gun); //at.on('get', get(msg));
|
||||
}
|
||||
@ -721,18 +721,21 @@
|
||||
;(function(){
|
||||
Gun.on.put = function(msg, gun){
|
||||
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
|
||||
if(!Gun.obj.map(msg.put, perf, ctx)){ return } // HNPERF: performance test, not core code, do not port.
|
||||
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
|
||||
obj_map(ctx.put, merge, ctx);
|
||||
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
|
||||
if(u !== ctx.defer){
|
||||
var to = ctx.defer - ctx.machine;
|
||||
setTimeout(function(){
|
||||
Gun.on.put(msg, gun);
|
||||
}, ctx.defer - ctx.machine);
|
||||
}, to > MD? MD : to ); // setTimeout Max Defer 32bit :(
|
||||
}
|
||||
if(!ctx.diff){ return }
|
||||
at.on('put', obj_to(msg, {put: ctx.diff}));
|
||||
};
|
||||
var MD = 2147483647;
|
||||
function verify(val, key, node, soul){ var ctx = this;
|
||||
var state = Gun.state.is(node, key), tmp;
|
||||
if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" }
|
||||
@ -798,6 +801,7 @@
|
||||
(msg.$._).on('in', msg);
|
||||
this.cat.stop = null; // temporary fix till a better solution?
|
||||
}
|
||||
function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port!
|
||||
|
||||
Gun.on.get = function(msg, gun){
|
||||
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
||||
@ -811,15 +815,17 @@
|
||||
// Maybe... in case the in-memory key we have is a local write
|
||||
// we still need to trigger a pull/merge from peers.
|
||||
} else {
|
||||
node = Gun.obj.copy(node);
|
||||
node = Gun.window? Gun.obj.copy(node) : node; // HNPERF: If !browser bump Performance? Is this too dangerous to reference root graph? Copy / shallow copy too expensive for big nodes. Gun.obj.to(node); // 1 layer deep copy // Gun.obj.copy(node); // too slow on big nodes
|
||||
}
|
||||
node = Gun.graph.node(node);
|
||||
tmp = (at||empty).ack;
|
||||
var faith = function(){}; faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
|
||||
root.on('in', {
|
||||
'@': msg['#'],
|
||||
how: 'mem',
|
||||
put: node,
|
||||
$: gun
|
||||
$: gun,
|
||||
_: faith
|
||||
});
|
||||
//if(0 < tmp){ return }
|
||||
root.on('get', msg);
|
||||
@ -1962,6 +1968,7 @@
|
||||
|
||||
var dup = root.dup;
|
||||
|
||||
// TODO: somewhere in here caused a out-of-memory crash! How? It is inbound!
|
||||
mesh.hear = function(raw, peer){
|
||||
if(!raw){ return }
|
||||
var msg, id, hash, tmp = raw[0];
|
||||
@ -1990,11 +1997,11 @@
|
||||
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
|
||||
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
|
||||
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?
|
||||
dup.track(id, true).it = 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['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same.
|
||||
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?
|
||||
dup.track(tmp+hash, true).it = 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) }
|
||||
@ -2004,9 +2011,9 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
var S; LOG && (S = +new Date);
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
root.on('in', msg);
|
||||
LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']);
|
||||
LOG && !msg.nts && (ST = +new Date - S) > 9 && opt.log(S, ST, 'msg', msg['#']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2014,6 +2021,7 @@
|
||||
mesh.hear.c = mesh.hear.d = 0;
|
||||
|
||||
;(function(){
|
||||
var SMIA = 0;
|
||||
var message;
|
||||
function each(peer){ mesh.say(message, peer) }
|
||||
mesh.say = function(msg, peer){
|
||||
@ -2025,24 +2033,28 @@
|
||||
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);
|
||||
raw = mesh.raw(msg);
|
||||
if(hash && (tmp = msg['@'])){
|
||||
dup.track(tmp+hash).it = msg;
|
||||
dup.track(tmp+hash).it = it(msg);
|
||||
if(tmp = (dup.s[tmp]||ok).it){
|
||||
if(hash === tmp['##']){ return false }
|
||||
tmp['##'] = hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'say prep');
|
||||
dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more!
|
||||
//LOG && opt.log(S, +new Date - S, 'say prep');
|
||||
dup.track(id).it = 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 && msg['@']){
|
||||
LOG && opt.log(+new Date, ++SMIA, 'total no peer to ack to');
|
||||
return false;
|
||||
} // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior.
|
||||
if(!peer && mesh.way){ return mesh.way(msg) }
|
||||
if(!peer || !peer.id){ message = msg;
|
||||
if(!Type.obj.is(peer || opt.peers)){ return false }
|
||||
var S; LOG && (S = +new Date);
|
||||
//var S; LOG && (S = +new Date);
|
||||
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
|
||||
LOG && opt.log(S, +new Date - S, 'say loop');
|
||||
//LOG && opt.log(S, +new Date - S, 'say loop');
|
||||
return;
|
||||
}
|
||||
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
|
||||
@ -2079,20 +2091,21 @@
|
||||
// for now - find better place later.
|
||||
function send(raw, peer){ try{
|
||||
var wire = peer.wire;
|
||||
var S; LOG && (S = +new Date);
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
if(peer.say){
|
||||
peer.say(raw);
|
||||
} else
|
||||
if(wire.send){
|
||||
wire.send(raw);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'wire send', raw.length);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'wire send', raw.length);
|
||||
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
|
||||
}catch(e){
|
||||
(peer.queue = peer.queue || []).push(raw);
|
||||
}}
|
||||
|
||||
;(function(){
|
||||
// TODO: this caused a out-of-memory crash!
|
||||
mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out!
|
||||
if(!msg){ return '' }
|
||||
var meta = (msg._) || {}, put, hash, tmp;
|
||||
@ -2100,7 +2113,7 @@
|
||||
if(typeof msg === 'string'){ return msg }
|
||||
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! // For "tower" peer, MUST include 6 surrounding ids.
|
||||
to.push(p.url || p.pid || p.id); if(++i > 3){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. // REDUCED THIS TO 3 for temporary relay peer performance, towers still should list neighbors.
|
||||
}); if(i > 1){ msg['><'] = to.join() }
|
||||
}
|
||||
var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash?
|
||||
@ -2109,7 +2122,7 @@
|
||||
raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1);
|
||||
//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 }
|
||||
if(meta && (raw||'').length < (1000 * 100)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory.
|
||||
return raw;
|
||||
}
|
||||
var $ = JSON.stringify, _ = ':])([:';
|
||||
@ -2211,6 +2224,8 @@
|
||||
}
|
||||
}());
|
||||
|
||||
function it(msg){ return msg || {_: msg._, '##': msg['##']} } // HNPERF: Only need some meta data, not full reference (took up too much memory). // HNPERF: Garrrgh! We add meta data to msg over time, copying the object happens to early.
|
||||
|
||||
var empty = {}, ok = true, u;
|
||||
var LOG = console.LOG;
|
||||
|
||||
|
2
gun.min.js
vendored
2
gun.min.js
vendored
File diff suppressed because one or more lines are too long
11
lib/doll.js
11
lib/doll.js
@ -41,4 +41,15 @@
|
||||
})
|
||||
})}
|
||||
$.fn.appendTo = function(html){ return this.append(html, 0, 1) }
|
||||
$.fn.parents = function(q, c){
|
||||
var I = $(), l = I.tags, p = 'parentElement';
|
||||
this.each(function(i, tag){
|
||||
if(c){ (c = {})[p] = tag ; tag = c }
|
||||
while(tag){ if((tag = tag[p]) && $(tag).is(q)){
|
||||
l.push(tag); if(c){ return }
|
||||
}}
|
||||
});
|
||||
return I;
|
||||
}
|
||||
$.fn.closest = function(q, c){ return this.parents(q, 1) }
|
||||
}());
|
17
lib/evict.js
17
lib/evict.js
@ -5,16 +5,21 @@
|
||||
this.to.next(root);
|
||||
if(root.once){ return }
|
||||
if(typeof process == 'undefined'){ return }
|
||||
var util = process.memoryUsage;
|
||||
var util = process.memoryUsage, heap;
|
||||
if(!util){ return }
|
||||
|
||||
ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though.
|
||||
try{ heap = require('v8').getHeapStatistics }catch(e){}
|
||||
if(!heap){ return }
|
||||
|
||||
ev.max = parseFloat(root.opt.memory || (heap().heap_size_limit / 1024 / 1024) || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though.
|
||||
|
||||
setInterval(check, 1000);
|
||||
function check(){
|
||||
var used = ev.used = util().rss / 1024 / 1024;
|
||||
var used = util().rss / 1024 / 1024;
|
||||
var hused = heap().used_heap_size / 1024 / 1024;
|
||||
//if(hused < ev.max && used < ev.max){ return }
|
||||
if(used < ev.max){ return }
|
||||
setTimeout(GC, 1);
|
||||
console.LOG && Gun.log('evict memory:', hused.toFixed(), used.toFixed(), ev.max.toFixed());
|
||||
GC();//setTimeout(GC, 1);
|
||||
}
|
||||
function GC(){
|
||||
var souls = Object.keys(root.graph||empty);
|
||||
@ -24,7 +29,7 @@
|
||||
if(--toss < 0){ return }
|
||||
root.gun.get(soul).off();
|
||||
});
|
||||
//console.log(+new Date - S, 'gc');
|
||||
root.dup.drop(1000 * 9); // clean up message tracker
|
||||
}
|
||||
/*
|
||||
root.on('in', function(msg){
|
||||
|
@ -22,11 +22,10 @@ Gun.on('create', function(root){
|
||||
socket.bind({port: udp.port, exclusive: true}, function(){
|
||||
socket.setBroadcast(true);
|
||||
socket.setMulticastTTL(128);
|
||||
try{ socket.addMembership(udp.address); }catch(e){}
|
||||
});
|
||||
|
||||
socket.on("listening", function(){
|
||||
try { socket.addMembership(udp.address) }catch(e){ return }
|
||||
try { socket.addMembership(udp.address) }catch(e){ console.error(e); return; }
|
||||
udp.peer = {id: udp.address + ':' + udp.port, wire: socket};
|
||||
|
||||
udp.peer.say = function(raw){
|
||||
@ -60,7 +59,7 @@ Gun.on('create', function(root){
|
||||
|
||||
var url = 'http://' + info.address + ':' + (port || (opt.web && opt.web.address()||{}).port) + '/gun';
|
||||
if(root.opt.peers[url]){ return }
|
||||
|
||||
|
||||
//console.log('discovered', url, message, info);
|
||||
root.$.opt(url);
|
||||
|
||||
|
190
lib/radisk.js
190
lib/radisk.js
@ -108,42 +108,84 @@
|
||||
6. Merge and write all of those to the in-memory file and back to disk.
|
||||
7. If file too large, split. More details needed here.
|
||||
*/
|
||||
/* NEW APPROACH:
|
||||
1. For each item in radix memory
|
||||
2. Add it to a radix bucket corresponding to directory of files
|
||||
3. Iterate over each bucket
|
||||
4. Resume old approach.
|
||||
*/
|
||||
r.save = function(rad, cb){
|
||||
var s = function Span(){};
|
||||
s.find = function(tree, key){
|
||||
if(key < s.start){ return }
|
||||
s.start = key;
|
||||
r.list(s.lex);
|
||||
return true;
|
||||
if(r.save.ing){
|
||||
r.save.ing.push({rad: rad, ack: cb});
|
||||
return;
|
||||
}
|
||||
s.lex = function(file){
|
||||
file = (u === file)? u : decodeURIComponent(file);
|
||||
if(!file || file > s.start){
|
||||
s.mix(s.file || opt.code.from, s.start, s.end = file);
|
||||
//console.only(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID);
|
||||
r.save.ing = [];
|
||||
var ack = cb;
|
||||
var s = function Span(err, ok){
|
||||
var tmp = r.save.ing;
|
||||
//console.only(99); var TMP; console.log("]]]]]]]]", ID, (TMP = +new Date) - S, 'more?', !!tmp);
|
||||
r.save.ing = null;
|
||||
map(tmp, function(q){ // if many, not the most efficient to requeue, but works for now.
|
||||
if(!q || !q.rad || !q.ack){ return }
|
||||
r.save(q.rad, q.ack);
|
||||
})
|
||||
ack(err, ok);
|
||||
};
|
||||
cb = s;
|
||||
s.files = {};
|
||||
s.i = 0; // TODO: revise? Using counter for critical path not my favorite.
|
||||
s.place = function(tree, key){
|
||||
var go = function(file, last){
|
||||
file = decodeURIComponent(file || last || opt.code.from);
|
||||
(s.files[file] || (s.files[file] = Radix()))(key, tree);
|
||||
if(!(--s.i)){ s.go() } // TODO: See above, revise?
|
||||
return true;
|
||||
}
|
||||
s.file = file;
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
++s.i; // TODO: See above, revise?
|
||||
}
|
||||
s.mix = function(file, start, end){
|
||||
s.start = s.end = s.file = u;
|
||||
s.go = function(){
|
||||
if(s.gone){ return } s.gone = true;
|
||||
s.seq = [];
|
||||
map(s.files, function(mem, file){ s.seq.push({file: file, mem: mem}) });
|
||||
s.files = null;
|
||||
s.c = 0;
|
||||
s.merge(s.c);
|
||||
}
|
||||
s.merge = function(i){
|
||||
i = i || 0;
|
||||
//var at = s.seq[i];
|
||||
var at = s.seq.shift();
|
||||
if(!at){
|
||||
if(s.ok){ return cb(null, s.ok) }
|
||||
return cb("No file to save data to.");
|
||||
}
|
||||
var file = at.file, mem = at.mem;
|
||||
r.parse(file, function(err, disk){
|
||||
if(err){ return cb(err) }
|
||||
if(!disk && file !== opt.code.from){ // corrupt file?
|
||||
r.list.bad(file); // remove from dir list
|
||||
r.save(rad, cb); // try again
|
||||
return;
|
||||
}
|
||||
disk = disk || Radix();
|
||||
Radix.map(rad, function(val, key){
|
||||
if(key < start){ return }
|
||||
if(end && end < key){ return s.start = key }
|
||||
Radix.map(mem, function(val, key){
|
||||
// PLUGIN: consider adding HAM as an extra layer of protection
|
||||
disk(key, val); // merge batch[key] -> disk[key]
|
||||
});
|
||||
r.write(file, disk, s.next);
|
||||
});
|
||||
r.write(file, disk, s.pop);
|
||||
})
|
||||
}
|
||||
s.next = function(err, ok){
|
||||
if(s.err = err){ return cb(err) }
|
||||
if(s.start){ return Radix.map(rad, s.find) }
|
||||
cb(err, ok);
|
||||
s.pop = function(err, ok){
|
||||
if(s.err = err || s.err){ return cb(err) }
|
||||
s.ok = ok || s.ok || 1;
|
||||
s.merge(++s.c);
|
||||
}
|
||||
Radix.map(rad, s.find);
|
||||
Radix.map(rad, s.place);
|
||||
if(!s.i){ s.go() }; // TODO: See above, revise?
|
||||
}
|
||||
|
||||
/*
|
||||
@ -168,7 +210,8 @@
|
||||
f.limit = Math.ceil(f.count/2);
|
||||
f.count = 0;
|
||||
f.sub = Radix();
|
||||
Radix.map(rad, f.slice);
|
||||
// IMPORTANT: DO THIS IN REVERSE, SO LAST HALF OF DATA MOVED TO NEW FILE BEFORE DROPPING FROM CURRENT FILE.
|
||||
Radix.map(rad, f.slice, {reverse: true});
|
||||
return true;
|
||||
}
|
||||
f.text += enc;
|
||||
@ -176,29 +219,28 @@
|
||||
f.write = function(){
|
||||
var tmp = ename(file);
|
||||
var S; LOG && (S = +new Date);
|
||||
opt.store.put(tmp, f.text, function(err){
|
||||
r.list.add(tmp, function(err){
|
||||
LOG && opt.log(S, +new Date - S, "wrote disk", tmp);
|
||||
if(err){ return cb(err) }
|
||||
r.list.add(tmp, cb);
|
||||
opt.store.put(tmp, f.text, cb);
|
||||
});
|
||||
}
|
||||
f.slice = function(val, key){
|
||||
if(key < f.file){ return }
|
||||
if(f.limit < (++f.count)){
|
||||
var name = f.file;
|
||||
f.file = key;
|
||||
f.count = 0;
|
||||
r.write(name, f.sub, f.next, o);
|
||||
f.sub(f.end = key, val);
|
||||
if(f.limit <= (++f.count)){
|
||||
r.write(key, f.sub, f.swap, o);
|
||||
return true;
|
||||
}
|
||||
f.sub(key, val);
|
||||
}
|
||||
f.next = function(err){
|
||||
f.swap = function(err){
|
||||
if(err){ return cb(err) }
|
||||
f.sub = Radix();
|
||||
if(!Radix.map(rad, f.slice)){
|
||||
r.write(f.file, f.sub, cb, o);
|
||||
}
|
||||
Radix.map(rad, f.stop);
|
||||
r.write(f.file, f.sub, cb, o);
|
||||
}
|
||||
f.stop = function(val, key){
|
||||
if(key >= f.end){ return true }
|
||||
f.sub(key, val);
|
||||
}
|
||||
if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
|
||||
if(!Radix.map(rad, f.each, true)){ f.write() }
|
||||
@ -244,7 +286,7 @@
|
||||
}
|
||||
o.span = (u !== o.start) || (u !== o.end); // is there a start or end?
|
||||
var g = function Get(){};
|
||||
g.lex = function(file){ var tmp;
|
||||
g.lex = function(file){ var tmp; // // TODO: this had a out-of-memory crash!
|
||||
file = (u === file)? u : decodeURIComponent(file);
|
||||
tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
|
||||
if(!file || (o.reverse? file < tmp : file > tmp)){
|
||||
@ -259,19 +301,25 @@
|
||||
g.it(null, u, {});
|
||||
return true;
|
||||
}
|
||||
r.parse(g.file, g.it);
|
||||
r.parse(g.file, g.check);
|
||||
return true;
|
||||
}
|
||||
g.file = file;
|
||||
}
|
||||
g.it = function(err, disk, info){
|
||||
if(g.err = err){ opt.log('err', err) }
|
||||
if(!disk && g.file){ // corrupt file?
|
||||
r.list.bad(g.file); // remove from dir list
|
||||
r.read(key, cb, o); // look again
|
||||
return;
|
||||
}
|
||||
g.info = info;
|
||||
if(disk){ RAD = g.disk = disk }
|
||||
disk = Q[g.file]; delete Q[g.file];
|
||||
LOG && opt.log(S, +new Date - S, 'rad read in, ack', disk.length); S = +new Date;
|
||||
var STMP = disk.length; // TMP STATS! DELETE!
|
||||
map(disk, g.ack);
|
||||
LOG && opt.log(S, +new Date - S, 'rad read acked');
|
||||
LOG && opt.log(S, +new Date - S, 'rad read acked', STMP, JSON.stringify(g.file));
|
||||
}
|
||||
g.ack = function(as){
|
||||
if(!as.ack){ return }
|
||||
@ -288,12 +336,57 @@
|
||||
return
|
||||
}
|
||||
if(u !== data){
|
||||
var S = +new Date;
|
||||
as.ack(g.err, data, o); // more might be coming!
|
||||
LOG && opt.log(S, +new Date - S, 'rad range ack.'); // 1.4s
|
||||
if(o.parsed >= o.limit){ return } // even if more, we've hit our limit, asking peer will need to make a new ask with a new starting point.
|
||||
}
|
||||
o.next = as.file;
|
||||
r.read(key, as.ack, o);
|
||||
}
|
||||
g.check = function(err, disk, info){
|
||||
g.it(err, disk, info);
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){
|
||||
good = false
|
||||
}
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return }
|
||||
var id = Gun.text.random(3); console.log("MISLOCATED DATA", id);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ return r.save(disk, ack) } // ad infinitum???
|
||||
console.log("MISLOCATED CORRECTED", id);
|
||||
});
|
||||
}
|
||||
/*g.check2 = function(err, disk, info){
|
||||
if(err || !disk){ return g.it(err, disk, info) }
|
||||
var good = true;
|
||||
Radix.map(disk, function(val, key){
|
||||
// assume in memory for now, since both write/read already call r.list which will init it.
|
||||
var go = function(file){
|
||||
if(info.file !== file){ good = false }
|
||||
return true;
|
||||
}
|
||||
go.reverse = 1;
|
||||
go.end = key;
|
||||
r.list(go);
|
||||
});
|
||||
if(good){ return g.it(err, disk, info) }
|
||||
var id = Gun.text.random(3); console.log("MISLOCATED DATA", id);
|
||||
r.save(disk, function ack(err, ok){
|
||||
if(err){ return r.save(disk, ack) } // ad infinitum???
|
||||
console.log("MISLOCATED CORRECTED", id);
|
||||
r.read(key, cb, o);
|
||||
});
|
||||
}*/
|
||||
if(o.reverse){ g.lex.reverse = true }
|
||||
LOG && (S = +new Date);
|
||||
r.list(g.lex);
|
||||
@ -313,7 +406,7 @@
|
||||
var Q = {}, s = String.fromCharCode(31);
|
||||
r.parse = function(file, cb, raw){ var q;
|
||||
if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
|
||||
var p = function Parse(){}, info = {};
|
||||
var p = function Parse(){}, info = {file: ename(file)};
|
||||
p.disk = Radix();
|
||||
p.read = function(err, data){ var tmp;
|
||||
LOG && opt.log(S, +new Date - S, 'read disk', ename(file));
|
||||
@ -336,7 +429,7 @@
|
||||
LOG && (S = +new Date);
|
||||
if(opt.jsonify || '{' === data[0]){ // temporary testing idea
|
||||
try{
|
||||
var json = JSON.parse(data);
|
||||
var json = JSON.parse(data); // TODO: this caused a out-of-memory crash!
|
||||
p.disk.$ = json;
|
||||
LOG && opt.log(S, +new Date - S, 'rad parsed JSON');
|
||||
map(q, p.ack);
|
||||
@ -400,10 +493,11 @@
|
||||
var dir, q, f = String.fromCharCode(28), ef = ename(f);
|
||||
r.list = function(cb){
|
||||
if(dir){
|
||||
var tmp = {reverse: (cb.reverse)? 1 : 0};
|
||||
var last, tmp = {reverse: (cb.reverse)? 1 : 0, start: cb.start, end: cb.end};
|
||||
Radix.map(dir, function(val, key){
|
||||
return cb(key);
|
||||
}, tmp) || cb();
|
||||
if(!val){ return }
|
||||
return cb(last = key);
|
||||
}, tmp) || cb(u, last);
|
||||
return;
|
||||
}
|
||||
if(q){ return q.push(cb) } q = [cb];
|
||||
@ -414,7 +508,7 @@
|
||||
if(has || file === ef){
|
||||
return cb(u, 1);
|
||||
}
|
||||
dir(file, true);
|
||||
dir(file, 1);
|
||||
cb.listed = (cb.listed || 0) + 1;
|
||||
r.write(f, dir, function(err, ok){
|
||||
if(err){ return cb(err) }
|
||||
@ -423,6 +517,10 @@
|
||||
cb(u, 1);
|
||||
}, true);
|
||||
}
|
||||
r.list.bad = function(file, cb){
|
||||
dir(ename(file), 0);
|
||||
r.write(f, dir, cb||noop);
|
||||
}
|
||||
r.list.init = function(err, disk){
|
||||
if(err){
|
||||
opt.log('list', err);
|
||||
|
13
lib/radix.js
13
lib/radix.js
@ -58,21 +58,26 @@
|
||||
//var keys = Object.keys(t).sort();
|
||||
opt = (true === opt)? {branch: true} : (opt || {});
|
||||
if(rev = opt.reverse){ keys = keys.slice().reverse() }
|
||||
var start = opt.start, end = opt.end;
|
||||
var start = opt.start, end = opt.end, END = '\uffff';
|
||||
var i = 0, l = keys.length;
|
||||
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
|
||||
if(!tree || '' === key || _ === key){ continue }
|
||||
p = pre.slice(); p.push(key);
|
||||
pt = p.join('');
|
||||
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
|
||||
if(u !== end && (end || '\uffff') < pt){ continue }
|
||||
if(u !== end && (end || END) < pt){ continue }
|
||||
if(rev){ // children must be checked first when going in reverse.
|
||||
tmp = map(tree, cb, opt, p);
|
||||
if(u !== tmp){ return tmp }
|
||||
}
|
||||
if(u !== (tmp = tree[''])){
|
||||
tmp = cb(tmp, pt, key, pre);
|
||||
if(u !== tmp){ return tmp }
|
||||
var yes = 1;
|
||||
if(u !== start && pt < (start||'')){ yes = 0 }
|
||||
if(u !== end && pt > (end || END)){ yes = 0 }
|
||||
if(yes){
|
||||
tmp = cb(tmp, pt, key, pre);
|
||||
if(u !== tmp){ return tmp }
|
||||
}
|
||||
} else
|
||||
if(opt.branch){
|
||||
tmp = cb(u, pt, key, pre);
|
||||
|
@ -56,6 +56,14 @@ function Store(opt){
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
store.list = function(cb, match, params, cbs){
|
||||
var dir = fs.readdirSync(opt.file);
|
||||
dir.forEach(function(file){
|
||||
cb(file);
|
||||
})
|
||||
cb();
|
||||
};
|
||||
|
||||
return store;
|
||||
}
|
||||
|
@ -2,11 +2,10 @@
|
||||
var Gun = require('../gun'), u;
|
||||
Gun.serve = require('./serve');
|
||||
//process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
|
||||
//console.LOG = true; // only temporarily, REVERT THIS IN FUTURE!
|
||||
console.LOG = true; // only do this for dev.
|
||||
Gun.on('opt', function(root){
|
||||
if(u === root.opt.super){
|
||||
root.opt.super = true;
|
||||
}
|
||||
if(u === root.opt.super){ root.opt.super = true }
|
||||
if(u === root.opt.faith){ root.opt.faith = true } // HNPERF: This should probably be off, but we're testing performance improvements, please audit.
|
||||
root.opt.log = root.opt.log || Gun.log;
|
||||
this.to.next(root);
|
||||
})
|
||||
|
24
lib/store.js
24
lib/store.js
@ -31,7 +31,7 @@ Gun.on('create', function(root){
|
||||
val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
|
||||
rad(soul+esc+key, val, (track? ack : u));
|
||||
});
|
||||
LOG && Gun.log(S, +new Date - S, 'put loop');
|
||||
//LOG && Gun.log(S, +new Date - S, 'put loop');
|
||||
function ack(err, ok){
|
||||
acks--;
|
||||
if(ack.err){ return }
|
||||
@ -79,22 +79,27 @@ Gun.on('create', function(root){
|
||||
}
|
||||
if(has['-'] || (soul||{})['-']){ o.reverse = true }
|
||||
if((tmp = (root.next||empty)[soul]) && tmp.put){
|
||||
var SPUT = tmp.put;
|
||||
if(o.atom){
|
||||
tmp = (tmp.next||empty)[o.atom] ;
|
||||
if(tmp && tmp.rad){ return }
|
||||
if(tmp && tmp.rad){
|
||||
LOG && Gun.log("still cached atom", JSON.stringify(get), Object.keys(SPUT||{}).length);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
if(tmp && tmp.rad){
|
||||
LOG && Gun.log("still cached", JSON.stringify(get), Object.keys(SPUT||{}).length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
var S = (+new Date); // STATS!
|
||||
var S = (+new Date), C = 0, CC = 0; // STATS!
|
||||
rad(key||'', function(err, data, o){
|
||||
try{opt.store.stats.get.time[statg % 50] = (+new Date) - S; ++statg;
|
||||
opt.store.stats.get.count++;
|
||||
if(err){ opt.store.stats.get.err = err }
|
||||
}catch(e){} // STATS!
|
||||
//if(u === data && o.chunks > 1){ return } // if we already sent a chunk, ignore ending empty responses. // this causes tests to fail.
|
||||
LOG && Gun.log(S, +new Date - S, 'got'); S = +new Date; // MARK RETURN HERE!!!! Gun.log will always log unless off :/ switch to something like LOG && whatever?
|
||||
LOG && Gun.log(S, +new Date - S, 'got'); S = +new Date;
|
||||
if(data){
|
||||
if(typeof data !== 'string'){
|
||||
if(o.atom){
|
||||
@ -105,11 +110,15 @@ Gun.on('create', function(root){
|
||||
}
|
||||
if(!graph && data){ each(data, '') }
|
||||
}
|
||||
LOG && Gun.log(S, +new Date - S, 'got prep');
|
||||
root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: each});
|
||||
LOG && Gun.log(S, +new Date - S, 'got prep count', CC, C); S = +new Date; CC = 0;
|
||||
var faith = function(){}; faith.faith = true; faith.rad = get; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
|
||||
root.on('in', {'@': id, put: graph, '%': o.more? 1 : u, err: err? err : u, _: faith});
|
||||
LOG && Gun.log(S, +new Date - S, 'got sent nodes in graph', Object.keys(graph||{}).length);
|
||||
graph = u; // each is outside our scope, we have to reset graph to nothing!
|
||||
}, o);
|
||||
LOG && Gun.log(S, +new Date - S, 'get call');
|
||||
function each(val, has, a,b){
|
||||
function each(val, has, a,b){ // TODO: THIS CODE NEEDS TO BE FASTER!!!!
|
||||
C++; ++CC;
|
||||
if(!val){ return }
|
||||
has = (key+has).split(esc);
|
||||
var soul = has.slice(0,1)[0];
|
||||
@ -121,7 +130,6 @@ Gun.on('create', function(root){
|
||||
(graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul);
|
||||
if(o.limit && o.limit <= o.count){ return true }
|
||||
}
|
||||
each.rad = get;
|
||||
LOG = console.LOG;
|
||||
});
|
||||
opt.store.stats = {get:{time:{}, count:0}, put: {time:{}, count:0}}; // STATS!
|
||||
|
@ -21,13 +21,16 @@
|
||||
opt.RTCSessionDescription = rtcsd;
|
||||
opt.RTCIceCandidate = rtcic;
|
||||
opt.rtc = opt.rtc || {'iceServers': [
|
||||
{url: 'stun:stun.l.google.com:19302'},
|
||||
{url: "stun:stun.sipgate.net:3478"},
|
||||
{url: "stun:stun.stunprotocol.org"},
|
||||
{url: "stun:stun.sipgate.net:10000"},
|
||||
{url: "stun:217.10.68.152:10000"},
|
||||
{url: 'stun:stun.services.mozilla.com'}
|
||||
{urls: 'stun:stun.l.google.com:19302'},
|
||||
{urls: "stun:stun.sipgate.net:3478"}/*,
|
||||
{urls: "stun:stun.stunprotocol.org"},
|
||||
{urls: "stun:stun.sipgate.net:10000"},
|
||||
{urls: "stun:217.10.68.152:10000"},
|
||||
{urls: 'stun:stun.services.mozilla.com'}*/
|
||||
]};
|
||||
// TODO: Select the most appropriate stuns.
|
||||
// FIXME: Find the wire throwing ICE Failed
|
||||
// The above change corrects at least firefox RTC Peer handler where it **throws** on over 6 ice servers, and updates url: to urls: removing deprecation warning
|
||||
opt.rtc.dataChannel = opt.rtc.dataChannel || {ordered: false, maxRetransmits: 2};
|
||||
opt.rtc.sdp = opt.rtc.sdp || {mandatory: {OfferToReceiveAudio: false, OfferToReceiveVideo: false}};
|
||||
opt.announce = function(to){
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.2019.1228",
|
||||
"version": "0.2020.116",
|
||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||
"main": "index.js",
|
||||
"browser": "browser.js",
|
||||
|
24
sea.js
24
sea.js
@ -325,7 +325,7 @@
|
||||
|
||||
var ecdhSubtle = shim.ossl || shim.subtle;
|
||||
// First: ECDSA keys for signing/verifying...
|
||||
var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ])
|
||||
var sa = await shim.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, [ 'sign', 'verify' ])
|
||||
.then(async (keys) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
//const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey)
|
||||
@ -345,7 +345,7 @@
|
||||
// Next: ECDH keys for encryption/decryption...
|
||||
|
||||
try{
|
||||
var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey'])
|
||||
var dh = await ecdhSubtle.generateKey({name: 'ECDH', namedCurve: 'P-256'}, true, ['deriveKey'])
|
||||
.then(async (keys) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
var key = {};
|
||||
@ -404,8 +404,8 @@
|
||||
var priv = pair.priv;
|
||||
var jwk = S.jwk(pub, priv);
|
||||
var hash = await sha(json);
|
||||
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}
|
||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
||||
|
||||
@ -439,12 +439,12 @@
|
||||
opt = opt || {};
|
||||
// SEA.I // verify is free! Requires no user permission.
|
||||
var pub = pair.pub || pair;
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']);
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var hash = await sha(json.m);
|
||||
var buf, sig, check, tmp; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf);
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash));
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash));
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
if(SEA.opt.fallback){
|
||||
@ -470,7 +470,7 @@
|
||||
var keyForPair = SEA.opt.slow_leak = pair => {
|
||||
if (knownKeys[pair]) return knownKeys[pair];
|
||||
var jwk = S.jwk(pair);
|
||||
knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]);
|
||||
knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ["verify"]);
|
||||
return knownKeys[pair];
|
||||
};
|
||||
|
||||
@ -482,12 +482,12 @@
|
||||
var buf; var sig; var check; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
var r = check? S.parse(json.m) : u;
|
||||
@ -612,7 +612,7 @@
|
||||
var epriv = pair.epriv;
|
||||
var ecdhSubtle = shim.ossl || shim.subtle;
|
||||
var pubKeyData = keysToEcdhJwk(pub);
|
||||
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },S.ecdh); // Thanks to @sirpy !
|
||||
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },{name: 'ECDH', namedCurve: 'P-256'}); // Thanks to @sirpy !
|
||||
var privKeyData = keysToEcdhJwk(epub, epriv);
|
||||
var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveBits']).then(async (privKey) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
@ -643,7 +643,7 @@
|
||||
jwk,
|
||||
{ x: x, y: y, kty: 'EC', crv: 'P-256', ext: true }
|
||||
), // ??? refactor
|
||||
S.ecdh
|
||||
{name: 'ECDH', namedCurve: 'P-256'}
|
||||
]
|
||||
}
|
||||
|
||||
@ -659,7 +659,7 @@
|
||||
SEA.verify = USE('./verify');
|
||||
SEA.encrypt = USE('./encrypt');
|
||||
SEA.decrypt = USE('./decrypt');
|
||||
SEA.aeskey = USE('./aeskey');
|
||||
SEA.opt.aeskey = USE('./aeskey'); // not official!
|
||||
|
||||
SEA.random = SEA.random || shim.random;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
var ecdhSubtle = shim.ossl || shim.subtle;
|
||||
// First: ECDSA keys for signing/verifying...
|
||||
var sa = await shim.subtle.generateKey(S.ecdsa.pair, true, [ 'sign', 'verify' ])
|
||||
var sa = await shim.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, [ 'sign', 'verify' ])
|
||||
.then(async (keys) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
//const { d: priv } = await shim.subtle.exportKey('jwk', keys.privateKey)
|
||||
@ -39,7 +39,7 @@
|
||||
// Next: ECDH keys for encryption/decryption...
|
||||
|
||||
try{
|
||||
var dh = await ecdhSubtle.generateKey(S.ecdh, true, ['deriveKey'])
|
||||
var dh = await ecdhSubtle.generateKey({name: 'ECDH', namedCurve: 'P-256'}, true, ['deriveKey'])
|
||||
.then(async (keys) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
var key = {};
|
||||
|
@ -13,7 +13,7 @@
|
||||
var epriv = pair.epriv;
|
||||
var ecdhSubtle = shim.ossl || shim.subtle;
|
||||
var pubKeyData = keysToEcdhJwk(pub);
|
||||
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },S.ecdh); // Thanks to @sirpy !
|
||||
var props = Object.assign({ public: await ecdhSubtle.importKey(...pubKeyData, true, []) },{name: 'ECDH', namedCurve: 'P-256'}); // Thanks to @sirpy !
|
||||
var privKeyData = keysToEcdhJwk(epub, epriv);
|
||||
var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveBits']).then(async (privKey) => {
|
||||
// privateKey scope doesn't leak out from here!
|
||||
@ -44,7 +44,7 @@
|
||||
jwk,
|
||||
{ x: x, y: y, kty: 'EC', crv: 'P-256', ext: true }
|
||||
), // ??? refactor
|
||||
S.ecdh
|
||||
{name: 'ECDH', namedCurve: 'P-256'}
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
||||
var priv = pair.priv;
|
||||
var jwk = S.jwk(pub, priv);
|
||||
var hash = await sha(json);
|
||||
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
var sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['sign'])
|
||||
.then((key) => (shim.ossl || shim.subtle).sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||
var r = {m: json, s: shim.Buffer.from(sig, 'binary').toString(opt.encode || 'base64')}
|
||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
||||
|
||||
|
@ -15,12 +15,12 @@
|
||||
opt = opt || {};
|
||||
// SEA.I // verify is free! Requires no user permission.
|
||||
var pub = pair.pub || pair;
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify']);
|
||||
var key = SEA.opt.slow_leak? await SEA.opt.slow_leak(pub) : await (shim.ossl || shim.subtle).importKey('jwk', jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ['verify']);
|
||||
var hash = await sha(json.m);
|
||||
var buf, sig, check, tmp; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64'); // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf);
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash));
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash));
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
if(SEA.opt.fallback){
|
||||
@ -46,7 +46,7 @@
|
||||
var keyForPair = SEA.opt.slow_leak = pair => {
|
||||
if (knownKeys[pair]) return knownKeys[pair];
|
||||
var jwk = S.jwk(pair);
|
||||
knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, S.ecdsa.pair, false, ["verify"]);
|
||||
knownKeys[pair] = (shim.ossl || shim.subtle).importKey("jwk", jwk, {name: 'ECDSA', namedCurve: 'P-256'}, false, ["verify"]);
|
||||
return knownKeys[pair];
|
||||
};
|
||||
|
||||
@ -58,12 +58,12 @@
|
||||
var buf; var sig; var check; try{
|
||||
buf = shim.Buffer.from(json.s, opt.encode || 'base64') // NEW DEFAULT!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify(S.ecdsa.sign, key, sig, new Uint8Array(hash))
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
var r = check? S.parse(json.m) : u;
|
||||
|
@ -11,6 +11,7 @@ function Mesh(root){
|
||||
|
||||
var dup = root.dup;
|
||||
|
||||
// TODO: somewhere in here caused a out-of-memory crash! How? It is inbound!
|
||||
mesh.hear = function(raw, peer){
|
||||
if(!raw){ return }
|
||||
var msg, id, hash, tmp = raw[0];
|
||||
@ -39,11 +40,11 @@ function Mesh(root){
|
||||
if(!(id = msg['#'])){ id = msg['#'] = Type.text.random(9) }
|
||||
if(msg.DBG_s){ opt.log(+new Date - msg.DBG_s, 'to hear', id) }
|
||||
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?
|
||||
dup.track(id, true).it = 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['@'] || (msg.get && id))){ // Reduces backward daisy in case varying hashes at different daisy depths are the same.
|
||||
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?
|
||||
dup.track(tmp+hash, true).it = 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) }
|
||||
@ -53,9 +54,9 @@ function Mesh(root){
|
||||
}
|
||||
return;
|
||||
}
|
||||
var S; LOG && (S = +new Date);
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
root.on('in', msg);
|
||||
LOG && !msg.nts && opt.log(S, +new Date - S, 'msg', msg['#']);
|
||||
LOG && !msg.nts && (ST = +new Date - S) > 9 && opt.log(S, ST, 'msg', msg['#']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -63,6 +64,7 @@ function Mesh(root){
|
||||
mesh.hear.c = mesh.hear.d = 0;
|
||||
|
||||
;(function(){
|
||||
var SMIA = 0;
|
||||
var message;
|
||||
function each(peer){ mesh.say(message, peer) }
|
||||
mesh.say = function(msg, peer){
|
||||
@ -74,24 +76,28 @@ function Mesh(root){
|
||||
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);
|
||||
raw = mesh.raw(msg);
|
||||
if(hash && (tmp = msg['@'])){
|
||||
dup.track(tmp+hash).it = msg;
|
||||
dup.track(tmp+hash).it = it(msg);
|
||||
if(tmp = (dup.s[tmp]||ok).it){
|
||||
if(hash === tmp['##']){ return false }
|
||||
tmp['##'] = hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'say prep');
|
||||
dup.track(id).it = msg; // track for 9 seconds, default. Earth<->Mars would need more!
|
||||
//LOG && opt.log(S, +new Date - S, 'say prep');
|
||||
dup.track(id).it = 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 && msg['@']){
|
||||
LOG && opt.log(+new Date, ++SMIA, 'total no peer to ack to');
|
||||
return false;
|
||||
} // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior.
|
||||
if(!peer && mesh.way){ return mesh.way(msg) }
|
||||
if(!peer || !peer.id){ message = msg;
|
||||
if(!Type.obj.is(peer || opt.peers)){ return false }
|
||||
var S; LOG && (S = +new Date);
|
||||
//var S; LOG && (S = +new Date);
|
||||
Type.obj.map(peer || opt.peers, each); // in case peer is a peer list.
|
||||
LOG && opt.log(S, +new Date - S, 'say loop');
|
||||
//LOG && opt.log(S, +new Date - S, 'say loop');
|
||||
return;
|
||||
}
|
||||
if(!peer.wire && mesh.wire){ mesh.wire(peer) }
|
||||
@ -128,20 +134,21 @@ function Mesh(root){
|
||||
// for now - find better place later.
|
||||
function send(raw, peer){ try{
|
||||
var wire = peer.wire;
|
||||
var S; LOG && (S = +new Date);
|
||||
var S, ST; LOG && (S = +new Date);
|
||||
if(peer.say){
|
||||
peer.say(raw);
|
||||
} else
|
||||
if(wire.send){
|
||||
wire.send(raw);
|
||||
}
|
||||
LOG && opt.log(S, +new Date - S, 'wire send', raw.length);
|
||||
LOG && (ST = +new Date - S) > 9 && opt.log(S, ST, 'wire send', raw.length);
|
||||
mesh.say.d += raw.length||0; ++mesh.say.c; // STATS!
|
||||
}catch(e){
|
||||
(peer.queue = peer.queue || []).push(raw);
|
||||
}}
|
||||
|
||||
;(function(){
|
||||
// TODO: this caused a out-of-memory crash!
|
||||
mesh.raw = function(msg){ // TODO: Clean this up / delete it / move logic out!
|
||||
if(!msg){ return '' }
|
||||
var meta = (msg._) || {}, put, hash, tmp;
|
||||
@ -149,7 +156,7 @@ function Mesh(root){
|
||||
if(typeof msg === 'string'){ return msg }
|
||||
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! // For "tower" peer, MUST include 6 surrounding ids.
|
||||
to.push(p.url || p.pid || p.id); if(++i > 3){ return true } // limit server, fast fix, improve later! // For "tower" peer, MUST include 6 surrounding ids. // REDUCED THIS TO 3 for temporary relay peer performance, towers still should list neighbors.
|
||||
}); if(i > 1){ msg['><'] = to.join() }
|
||||
}
|
||||
var raw = $(msg); // optimize by reusing put = the JSON.stringify from .hash?
|
||||
@ -158,7 +165,7 @@ function Mesh(root){
|
||||
raw = raw.slice(0, tmp-1) + put + raw.slice(tmp + _.length + 1);
|
||||
//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 }
|
||||
if(meta && (raw||'').length < (1000 * 100)){ meta.raw = raw } // HNPERF: If string too big, don't keep in memory.
|
||||
return raw;
|
||||
}
|
||||
var $ = JSON.stringify, _ = ':])([:';
|
||||
@ -260,6 +267,8 @@ function Mesh(root){
|
||||
}
|
||||
}());
|
||||
|
||||
function it(msg){ return msg || {_: msg._, '##': msg['##']} } // HNPERF: Only need some meta data, not full reference (took up too much memory). // HNPERF: Garrrgh! We add meta data to msg over time, copying the object happens to early.
|
||||
|
||||
var empty = {}, ok = true, u;
|
||||
var LOG = console.LOG;
|
||||
|
||||
|
21
src/dup.js
21
src/dup.js
@ -2,7 +2,7 @@
|
||||
var Type = require('./type');
|
||||
function Dup(opt){
|
||||
var dup = {s:{}};
|
||||
opt = opt || {max: 1000, age: 1000 * 9};//1000 * 60 * 2};
|
||||
opt = opt || {max: 1000, age: /*1000 * 9};//*/ 1000 * 9 * 3};
|
||||
dup.check = function(id){ var tmp;
|
||||
if(!(tmp = dup.s[id])){ return false }
|
||||
if(tmp.pass){ return tmp.pass = false }
|
||||
@ -12,18 +12,17 @@ function Dup(opt){
|
||||
var it = dup.s[id] || (dup.s[id] = {});
|
||||
it.was = time_is();
|
||||
if(pass){ it.pass = true }
|
||||
if(!dup.to){
|
||||
dup.to = setTimeout(function(){
|
||||
var now = time_is();
|
||||
Type.obj.map(dup.s, function(it, id){
|
||||
if(it && opt.age > (now - it.was)){ return }
|
||||
Type.obj.del(dup.s, id);
|
||||
});
|
||||
dup.to = null;
|
||||
}, opt.age + 9);
|
||||
}
|
||||
if(!dup.to){ dup.to = setTimeout(dup.drop, opt.age + 9) }
|
||||
return it;
|
||||
}
|
||||
dup.drop = function(age){
|
||||
var now = time_is();
|
||||
Type.obj.map(dup.s, function(it, id){
|
||||
if(it && (age || opt.age) > (now - it.was)){ return }
|
||||
Type.obj.del(dup.s, id);
|
||||
});
|
||||
dup.to = null;
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
var time_is = Type.time.is;
|
||||
|
15
src/root.js
15
src/root.js
@ -53,7 +53,8 @@ Gun.dup = require('./dup');
|
||||
return;
|
||||
}
|
||||
dup.track(tmp);
|
||||
if(!at.ask(msg['@'], msg)){
|
||||
if(tmp = msg['@']){ dup.track(tmp) } // HNPERF: Bump original request's liveliness.
|
||||
if(!at.ask(tmp, msg)){
|
||||
if(msg.get){
|
||||
Gun.on.get(msg, gun); //at.on('get', get(msg));
|
||||
}
|
||||
@ -72,18 +73,21 @@ Gun.dup = require('./dup');
|
||||
;(function(){
|
||||
Gun.on.put = function(msg, gun){
|
||||
var at = gun._, ctx = {$: gun, graph: at.graph, put: {}, map: {}, souls: {}, machine: Gun.state(), ack: msg['@'], cat: at, stop: {}};
|
||||
if(!Gun.obj.map(msg.put, perf, ctx)){ return } // HNPERF: performance test, not core code, do not port.
|
||||
if(!Gun.graph.is(msg.put, null, verify, ctx)){ ctx.err = "Error: Invalid graph!" }
|
||||
if(ctx.err){ return at.on('in', {'@': msg['#'], err: Gun.log(ctx.err) }) }
|
||||
obj_map(ctx.put, merge, ctx);
|
||||
if(!ctx.async){ obj_map(ctx.map, map, ctx) }
|
||||
if(u !== ctx.defer){
|
||||
var to = ctx.defer - ctx.machine;
|
||||
setTimeout(function(){
|
||||
Gun.on.put(msg, gun);
|
||||
}, ctx.defer - ctx.machine);
|
||||
}, to > MD? MD : to ); // setTimeout Max Defer 32bit :(
|
||||
}
|
||||
if(!ctx.diff){ return }
|
||||
at.on('put', obj_to(msg, {put: ctx.diff}));
|
||||
};
|
||||
var MD = 2147483647;
|
||||
function verify(val, key, node, soul){ var ctx = this;
|
||||
var state = Gun.state.is(node, key), tmp;
|
||||
if(!state){ return ctx.err = "Error: No state on '"+key+"' in node '"+soul+"'!" }
|
||||
@ -149,6 +153,7 @@ Gun.dup = require('./dup');
|
||||
(msg.$._).on('in', msg);
|
||||
this.cat.stop = null; // temporary fix till a better solution?
|
||||
}
|
||||
function perf(node, soul){ if(node !== this.graph[soul]){ return true } } // HNPERF: do not port!
|
||||
|
||||
Gun.on.get = function(msg, gun){
|
||||
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
||||
@ -162,15 +167,17 @@ Gun.dup = require('./dup');
|
||||
// Maybe... in case the in-memory key we have is a local write
|
||||
// we still need to trigger a pull/merge from peers.
|
||||
} else {
|
||||
node = Gun.obj.copy(node);
|
||||
node = Gun.window? Gun.obj.copy(node) : node; // HNPERF: If !browser bump Performance? Is this too dangerous to reference root graph? Copy / shallow copy too expensive for big nodes. Gun.obj.to(node); // 1 layer deep copy // Gun.obj.copy(node); // too slow on big nodes
|
||||
}
|
||||
node = Gun.graph.node(node);
|
||||
tmp = (at||empty).ack;
|
||||
var faith = function(){}; faith.faith = true; // HNPERF: We're testing performance improvement by skipping going through security again, but this should be audited.
|
||||
root.on('in', {
|
||||
'@': msg['#'],
|
||||
how: 'mem',
|
||||
put: node,
|
||||
$: gun
|
||||
$: gun,
|
||||
_: faith
|
||||
});
|
||||
//if(0 < tmp){ return }
|
||||
root.on('get', msg);
|
||||
|
92
test/rad/crash.js
Normal file
92
test/rad/crash.js
Normal file
@ -0,0 +1,92 @@
|
||||
var root;
|
||||
var Gun;
|
||||
(function(){
|
||||
var env;
|
||||
if(typeof global !== 'undefined'){ env = global }
|
||||
if(typeof window !== 'undefined'){ env = window }
|
||||
root = env.window? env.window : global;
|
||||
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
|
||||
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
|
||||
if(root.Gun){
|
||||
root.Gun = root.Gun;
|
||||
root.Gun.TESTING = true;
|
||||
} else {
|
||||
try{ require('fs').unlinkSync('data.json') }catch(e){}
|
||||
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
|
||||
root.Gun = require('../../gun');
|
||||
root.Gun.TESTING = true;
|
||||
//require('../lib/file');
|
||||
require('../../lib/store');
|
||||
require('../../lib/rfs');
|
||||
}
|
||||
|
||||
try{ var expect = global.expect = require("../expect") }catch(e){}
|
||||
|
||||
}(this));
|
||||
|
||||
;(function(){
|
||||
Gun = root.Gun
|
||||
|
||||
if(Gun.window && !Gun.window.RindexedDB){ return }
|
||||
|
||||
var opt = {};
|
||||
opt.file = 'radatatest';
|
||||
var Radisk = (Gun.window && Gun.window.Radisk) || require('../../lib/radisk');
|
||||
opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(opt);
|
||||
opt.chunk = 170;
|
||||
var Radix = Radisk.Radix;
|
||||
var rad = Radisk(opt), esc = String.fromCharCode(27);
|
||||
|
||||
describe('RAD Crashes', function(){
|
||||
|
||||
describe('If Some of Split Fails, Keep Original Data', function(){
|
||||
var gun = Gun({chunk: opt.chunk});
|
||||
|
||||
it('write initial', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
var names = ['al', 'alex', 'alexander', 'alice'];
|
||||
names.forEach(function(v,i){
|
||||
all[++i] = true;
|
||||
tmp = v.toLowerCase();
|
||||
gun.get('names').get(tmp).put(i, function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
delete all[i];
|
||||
if(!Gun.obj.empty(all)){ return }
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('write alan', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
var names = ['alan'];
|
||||
console.log("DID YOU ADD `Gun.CRASH` to Radisk f.swap?");
|
||||
Gun.CRASH = true; // add check for this in f.swap!
|
||||
names.forEach(function(v,i){
|
||||
all[++i] = true;
|
||||
tmp = v.toLowerCase();
|
||||
gun.get('names').get(tmp).put(i);
|
||||
});
|
||||
setTimeout(function(){
|
||||
Gun.CRASH = false;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it('read names', function(done){
|
||||
console.log("Better to .skip 1st run, .only 2nd run & prevent clearing radatatest.");
|
||||
var g = Gun();
|
||||
var all = {al: 1, alex: 2, alexander: 3, alice: 4};
|
||||
g.get('names').map().on(function(v,k){
|
||||
//console.log("DATA:", k, v);
|
||||
if(all[k] === v){ delete all[k] }
|
||||
if(!Gun.obj.empty(all)){ return }
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
@ -19,16 +19,16 @@ var Gun;
|
||||
require('../../lib/store');
|
||||
require('../../lib/rfs');
|
||||
}
|
||||
|
||||
|
||||
try{ var expect = global.expect = require("../expect") }catch(e){}
|
||||
|
||||
|
||||
}(this));
|
||||
|
||||
|
||||
;(function(){
|
||||
Gun = root.Gun
|
||||
|
||||
if(Gun.window && !Gun.window.RindexedDB){ return }
|
||||
|
||||
|
||||
var opt = {};
|
||||
opt.file = 'radatatest';
|
||||
var Radisk = (Gun.window && Gun.window.Radisk) || require('../../lib/radisk');
|
||||
@ -36,11 +36,11 @@ opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(
|
||||
opt.chunk = 1000;
|
||||
var Radix = Radisk.Radix;
|
||||
var rad = Radisk(opt), esc = String.fromCharCode(27);
|
||||
|
||||
|
||||
describe('RAD', function(){
|
||||
|
||||
|
||||
var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammamaria","Andy","Anselme","Ardeen","Armand","Ashelman","Aube","Averyl","Baker","Barger","Baten","Bee","Benia","Bernat","Bevers","Bittner","Bobbe","Bonny","Boyce","Breech","Brittaney","Bryn","Burkitt","Cadmann","Campagna","Carlee","Carver","Cavallaro","Chainey","Chaunce","Ching","Cianca","Claudina","Clyve","Colon","Cooke","Corrina","Crawley","Cullie","Dacy","Daniela","Daryn","Deedee","Denie","Devland","Dimitri","Dolphin","Dorinda","Dream","Dunham","Eachelle","Edina","Eisenstark","Elish","Elvis","Eng","Erland","Ethan","Evelyn","Fairman","Faus","Fenner","Fillander","Flip","Foskett","Fredette","Fullerton","Gamali","Gaspar","Gemina","Germana","Gilberto","Giuditta","Goer","Gotcher","Greenstein","Grosvenor","Guthrey","Haldane","Hankins","Harriette","Hayman","Heise","Hepsiba","Hewie","Hiroshi","Holtorf","Howlond","Hurless","Ieso","Ingold","Isidora","Jacoba","Janelle","Jaye","Jennee","Jillana","Johnson","Josy","Justinian","Kannan","Kast","Keeley","Kennett","Kho","Kiran","Knowles","Koser","Kroll","LaMori","Lanctot","Lasky","Laverna","Leff","Leonanie","Lewert","Lilybel","Lissak","Longerich","Lou","Ludeman","Lyman","Madai","Maia","Malvina","Marcy","Maris","Martens","Mathilda","Maye","McLain","Melamie","Meras","Micco","Millburn","Mittel","Montfort","Moth","Mutz","Nananne","Nazler","Nesta","Nicolina","Noellyn","Nuli","Ody","Olympie","Orlena","Other","Pain","Parry","Paynter","Pentheas","Pettifer","Phyllida","Plath","Posehn","Proulx","Quinlan","Raimes","Ras","Redmer","Renelle","Ricard","Rior","Rocky","Ron","Rosetta","Rubia","Ruttger","Salbu","Sandy","Saw","Scholz","Secor","September","Shanleigh","Shenan","Sholes","Sig","Sisely","Soble","Spanos","Stanwinn","Stevie","Stu","Suzanne","Tacy","Tanney","Tekla","Thackeray","Thomasin","Tilla","Tomas","Tracay","Tristis","Ty","Urana","Valdis","Vasta","Vezza","Vitoria","Wait","Warring","Weissmann","Whetstone","Williamson","Wittenburg","Wymore","Yoho","Zamir","Zimmermann"];
|
||||
|
||||
|
||||
//console.log("HYPER TEST");var z = 10000; while(--z){ names.push(Gun.text.random(7)) }this.timeout(9000);
|
||||
|
||||
describe('Radix', function(){
|
||||
@ -59,7 +59,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('radix write read again', function(done){
|
||||
var all = {};
|
||||
names.forEach(function(v,i){
|
||||
@ -74,7 +74,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('radix read start end', function(done){
|
||||
var all = {}, start = 'Warring'.toLowerCase(), end = 'Zamir'.toLowerCase();
|
||||
names.forEach(function(v,i){
|
||||
@ -92,7 +92,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('radix read start- end+', function(done){
|
||||
var all = {}, start = 'Warrinf'.toLowerCase(), end = 'Zamis'.toLowerCase();
|
||||
names.forEach(function(v,i){
|
||||
@ -134,7 +134,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
});
|
||||
|
||||
describe('Radisk', function(){
|
||||
|
||||
|
||||
/*it('parse', function(done){
|
||||
this.timeout(60000);
|
||||
if(Gun.window){ return done() }
|
||||
@ -144,8 +144,8 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
}, raw);
|
||||
return;
|
||||
});*/
|
||||
|
||||
|
||||
|
||||
|
||||
it('write contacts', function(done){
|
||||
var all = {}, to, start;
|
||||
names.forEach(function(v,i){
|
||||
@ -178,7 +178,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
}, opt);
|
||||
});
|
||||
console.log("UNDO THIS RETURN!!!");return;*/
|
||||
|
||||
|
||||
it('read contacts start end', function(done){
|
||||
var opt = {};
|
||||
opt.start = 'Warring'.toLowerCase();
|
||||
@ -200,7 +200,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
done();
|
||||
}, opt);
|
||||
});
|
||||
|
||||
|
||||
it('read contacts', function(done){
|
||||
var all = {}, find = 'a';
|
||||
names.forEach(function(v){
|
||||
@ -217,7 +217,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('read again', function(done){
|
||||
var all = {}, find = 'm';
|
||||
names.forEach(function(v){
|
||||
@ -252,12 +252,12 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
var ntmp = names;
|
||||
describe('RAD + GUN', function(){
|
||||
var ochunk = 1000;
|
||||
var gun = Gun({chunk: ochunk});
|
||||
|
||||
|
||||
it('write same', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
var names = [], c = 285;
|
||||
@ -273,7 +273,7 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('write contacts', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
names.forEach(function(v,i){
|
||||
@ -342,9 +342,46 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
|
||||
},100);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it.skip('read contacts smaller than cursor', function(done){ // TODO!!!
|
||||
var all = {}, cursor = 'm', to;
|
||||
names.forEach(function(v){
|
||||
v = v.toLowerCase();
|
||||
if(v < cursor){ all[v] = true }
|
||||
});
|
||||
gun.get('names').get({'.': {'<': cursor}, '%': 1000 * 100}).once().map().once(function(data, key){
|
||||
expect(data.name).to.be.ok();
|
||||
expect(data.age).to.be.ok();
|
||||
//if(!all.hasOwnProperty(key)){console.error(key);}
|
||||
expect(all.hasOwnProperty(key)).to.be.ok();
|
||||
delete all[key];
|
||||
clearTimeout(to);
|
||||
to = setTimeout(function(){
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
done();
|
||||
},100);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('read contacts in descending order', function(done){ // TODO!!!
|
||||
var all = {}, cursor = 'm', to;
|
||||
names.forEach(function(v){
|
||||
all[v] = true;
|
||||
});
|
||||
gun.get('names').get({'.': {'-': true}, '%': 1000 * 100}).once().map().once(function(data, key){
|
||||
expect(data.name).to.be.ok();
|
||||
expect(data.age).to.be.ok();
|
||||
delete all[key];
|
||||
clearTimeout(to);
|
||||
to = setTimeout(function(){
|
||||
expect(Gun.obj.empty(all)).to.be.ok();
|
||||
done();
|
||||
},100);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
}());
|
||||
|
100
test/rad/recover.js
Normal file
100
test/rad/recover.js
Normal file
@ -0,0 +1,100 @@
|
||||
var root;
|
||||
var Gun;
|
||||
(function(){
|
||||
var env;
|
||||
if(typeof global !== 'undefined'){ env = global }
|
||||
if(typeof window !== 'undefined'){ env = window }
|
||||
root = env.window? env.window : global;
|
||||
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
|
||||
try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
|
||||
if(root.Gun){
|
||||
root.Gun = root.Gun;
|
||||
root.Gun.TESTING = true;
|
||||
} else {
|
||||
try{ require('fs').unlinkSync('data.json') }catch(e){}
|
||||
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
|
||||
root.Gun = require('../../gun');
|
||||
root.Gun.TESTING = true;
|
||||
//require('../lib/file');
|
||||
require('../../lib/store');
|
||||
require('../../lib/rfs');
|
||||
}
|
||||
|
||||
try{ var expect = global.expect = require("../expect") }catch(e){}
|
||||
|
||||
}(this));
|
||||
|
||||
;(function(){
|
||||
Gun = root.Gun
|
||||
|
||||
if(Gun.window && !Gun.window.RindexedDB){ return }
|
||||
|
||||
var opt = {};
|
||||
opt.file = 'radatatest';
|
||||
var Radisk = (Gun.window && Gun.window.Radisk) || require('../../lib/radisk');
|
||||
opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(opt);
|
||||
opt.chunk = 170;
|
||||
var Radix = Radisk.Radix;
|
||||
var rad = Radisk(opt), esc = String.fromCharCode(27);
|
||||
|
||||
describe('RAD Crashes', function(){
|
||||
|
||||
describe('If No File Added to Index, Recover', function(){
|
||||
var gun = Gun({chunk: opt.chunk});
|
||||
|
||||
it('write initial', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
var names = ['al', 'alex', 'alexander', 'alice'];
|
||||
names.forEach(function(v,i){
|
||||
all[++i] = true;
|
||||
tmp = v.toLowerCase();
|
||||
gun.get('names').get(tmp).put(i, function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
delete all[i];
|
||||
if(!Gun.obj.empty(all)){ return }
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('write alan', function(done){
|
||||
var all = {}, to, start, tmp;
|
||||
var names = ['alan'];
|
||||
console.log("DID YOU ADD `Gun.CRASH` != 1%C to Radisk f.write list.add callback?");
|
||||
Gun.CRASH = true; // add check for this in f.swap!
|
||||
names.forEach(function(v,i){
|
||||
all[++i] = true;
|
||||
tmp = v.toLowerCase();
|
||||
gun.get('names').get(tmp).put(i);
|
||||
});
|
||||
setTimeout(function(){
|
||||
Gun.CRASH = false;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it('write zach', function(done){
|
||||
gun.get('names').get('zach').put(9, function(ack){
|
||||
expect(ack.err).to.not.be.ok();
|
||||
done() ;
|
||||
});
|
||||
});
|
||||
|
||||
it('read names', function(done){
|
||||
console.log("Better to .skip 1st run, .only 2nd run & prevent clearing radatatest.");
|
||||
var g = Gun();
|
||||
var all = {al: 1, alex: 2, alexander: 3, alice: 4, zach: 9};
|
||||
//g.get('names').get('zach').put(9, function(ack){ done() }); return;
|
||||
g.get('names').map().on(function(v,k){
|
||||
//console.log("DATA:", k, v);
|
||||
if(all[k] === v){ delete all[k] }
|
||||
if(!Gun.obj.empty(all)){ return }
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
@ -38,7 +38,9 @@ describe('SEA', function(){
|
||||
var pub;
|
||||
describe('Utility', function(){
|
||||
it('generates aeskey from jwk', function(done) {
|
||||
SEA.aeskey('x','x').then(k => {
|
||||
console.log("WARNING: THIS DOES NOT WORK IN BROWSER!!!! NEEDS FIX");
|
||||
SEA.opt.aeskey('x','x').then(k => {
|
||||
//console.log("DATA", k.data);
|
||||
expect(k.data.toString('base64')).to.be('Xd6JaIf2dUybFb/jpEGuSAbfL96UABMR4IvxEGIuC74=')
|
||||
done()
|
||||
})
|
||||
@ -139,6 +141,16 @@ describe('SEA', function(){
|
||||
});});});
|
||||
})
|
||||
|
||||
/*it('DOESNT DECRYPT SCIENTIFIC NOTATION', function(done){
|
||||
var pair, s, v;
|
||||
SEA.pair(function(pair){
|
||||
SEA.encrypt('4e2', pair, function(s){
|
||||
SEA.decrypt(s, pair, function(v){
|
||||
expect(400).to.be(v);
|
||||
done();
|
||||
});});});
|
||||
})*/
|
||||
|
||||
it('legacy', function(done){ (async function(){
|
||||
var pw = 'test123';
|
||||
// https://cdn.jsdelivr.net/npm/gun@0.9.99999/sea.js !
|
||||
|
Loading…
x
Reference in New Issue
Block a user