Use orbit-common. Make test fully independent and not require orbit-server to be running.

This commit is contained in:
haad 2016-02-03 14:22:58 +07:00
parent 2f1c13ee42
commit f74ec4b104
10 changed files with 82 additions and 282 deletions

View File

@ -1,20 +1,24 @@
# orbit-client
# OrbitDB
## Introduction
Key-Value Store and Event Store on IPFS.
Distributed, peer-to-peer* Key-Value Store and Event Log on IPFS.
Requires `orbit-server` to connect to. Get from https://github.com/haadcode/orbit-server.
_* Currently requires a centralized server. This will change in the future as required p2p features land in IPFS_
## Features
- Distributed kv-store and event log database
- Stores all data in IPFS
- Data encrypted on the wire and at rest
- Per channel access rights
_Channel maps to "table", "keyspace", "topic" or "feed" in similar systems_
_Channel is similar to "table", "keyspace", "topic", "feed" or "collection" in other systems_
## API
_See Usage below_
connect(host, username, password)
channel(name, password)
@ -36,7 +40,7 @@ _Channel maps to "table", "keyspace", "topic" or "feed" in similar systems_
.get(key) // Retrieve value
.remove({ key: <key>, hash: <event's ipfs-hash> }) // Remove entry (use one option)
.remove({ key: <key or hash> }) // Remove entry
.setMode(modes) // Set channel modes, can be an object or an array of objects
@ -62,11 +66,9 @@ async(() => {
const channelName = 'hello-world';
// Send an event
const head = orbit.channel(channelName).add('hello'); // <ipfs-hash>
// Delete an event
orbit.channel(channelName).remove(head);
/* Event Log */
const hash = orbit.channel(channelName).add('hello'); // <ipfs-hash>
orbit.channel(channelName).remove({ key: hash });
// Iterator options
const options = { limit: -1 }; // fetch all messages
@ -82,11 +84,12 @@ async(() => {
// for(let i of iter)
// console.log(i.hash, i.item);
// KV-store
/* KV Store */
orbit.channel(channelName).put("key1", "hello world");
orbit.channel(channelName).get("key1"); // returns "hello world"
orbit.channel(channelName).remove("key1");
// Modes
/* Modes */
const password = 'hello';
let channelModes;
channelModes = orbit.channel(channel).setMode({ mode: "+r", params: { password: password } }); // { modes: { r: { password: 'hello' } } }
@ -94,7 +97,7 @@ async(() => {
channelModes = orbit.channel(channel, password).setMode({ mode: "-r" }); // { modes: { ... } }
channelModes = orbit.channel(channel, '').setMode({ mode: "-w" }); // { modes: {} }
// Delete channel
/* Delete channel */
const result = orbit.channel(channelName, channelPwd).delete(); // true | false
})();
```

View File

@ -1,22 +1,23 @@
{
"name": "orbit-client",
"name": "orbit-db",
"version": "0.1.1",
"description": "Event Log and KeyValue Store on IPFS",
"description": "Key-Value Store and Event Log on IPFS",
"author": "Haad",
"license": "MIT",
"engines" : {
"node" : "^4.x.x"
"engines": {
"node": "^4.x.x"
},
"main": "src/OrbitClient.js",
"dependencies": {
"asyncawait": "^1.0.1",
"bluebird": "^3.1.1",
"bs58": "^3.0.0",
"ipfsd-ctl": "^0.7.1",
"orbit-common": "^0.1.0",
"unirest": "^0.4.2"
},
"devDependencies": {
"mocha": "^2.3.4"
"mocha": "^2.3.4",
"orbit-server": "^0.1.2"
},
"scripts": {
"test": "mocha"

View File

@ -2,12 +2,12 @@
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var ipfsAPI = require('./ipfs-api-promised');
var ipfsAPI = require('orbit-common/lib/ipfs-api-promised');
var Keystore = require('orbit-common/lib/Keystore');
var Encryption = require('orbit-common/lib/Encryption');
var HashCache = require('./HashCacheClient');
var HashCacheItem = require('./HashCacheItem').EncryptedHashCacheItem;
var HashCacheOps = require('./HashCacheItem').HashCacheOps;
var Keystore = require('./Keystore');
var Encryption = require('./Encryption');
var pubkey = Keystore.getKeys().publicKey;
var privkey = Keystore.getKeys().privateKey;
@ -29,7 +29,7 @@ class Aggregator {
if(item) {
if((item.op === HashCacheOps.Put || item.op === HashCacheOps.Add) && !this._contains(handledItems, item.key)) {
if(!opts.key || opts.key && opts.key === item.key) {
if(!opts.key || (opts.key && opts.key === item.key)) {
res.push({ hash: hash, item: item });
currentDepth ++;
handledItems.push(item.target);

View File

@ -1,61 +0,0 @@
'use strict';
var fs = require('fs');
var crypto = require('crypto');
var algorithm = 'aes-256-ecb';
class Encryption {
static encrypt(text, privkey, pubkey) {
var encrypted;
try {
var cipher = crypto.createCipher(algorithm, privkey)
encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex');
} catch(e) {
console.log("Error while encrypting:", e, e.stack);
}
return encrypted;
}
static decrypt(text, privkey, pubkey) {
var decrypted;
try {
var cipher = crypto.createDecipher(algorithm, privkey)
decrypted = cipher.update(text, 'hex', 'utf8')
decrypted += cipher.final('utf8');
} catch(e) {
console.log("Error while decrypting:", e, e.stack);
}
return decrypted;
}
static hashWithSHA512(data, salt) {
if(!salt) salt = "null";
var hash = crypto.createHmac('sha512', salt);
hash.update(data);
var value = hash.digest('hex');
return value;
}
static sign(data, privkey, seq, salt) {
if(!salt) salt = "null";
var sign = crypto.createSign('RSA-SHA256');
var hash = Encryption.hashWithSHA512(data, "" + salt)
sign.update("" + seq + hash);
var sig = sign.sign(privkey, 'hex');
return sig;
}
static verify(data, pubkey, sig, seq, salt) {
if(!salt) salt = "null";
var verify = crypto.createVerify('RSA-SHA256');
var hash = Encryption.hashWithSHA512(data, salt);
verify.update("" + seq + hash);
var verified = verify.verify(pubkey, sig, 'hex');
return verified;
}
}
module.exports = Encryption;

View File

@ -1,6 +1,6 @@
'use strict';
const Encryption = require('./Encryption');
const Encryption = require('orbit-common/lib/Encryption');
const HashCacheOps = {
Add: "ADD",

View File

@ -2,8 +2,10 @@
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var ipfsDaemon = require('./ipfs-daemon');
var ipfsAPI = require('./ipfs-api-promised');
var Keystore = require('orbit-common/lib/Keystore');
var Encryption = require('orbit-common/lib/Encryption');
var ipfsDaemon = require('orbit-common/lib/ipfs-daemon');
var ipfsAPI = require('orbit-common/lib/ipfs-api-promised');
var HashCache = require('./HashCacheClient');
var HashCacheItem = require('./HashCacheItem').EncryptedHashCacheItem;
var HashCacheOps = require('./HashCacheItem').HashCacheOps;
@ -11,8 +13,6 @@ var ItemTypes = require('./ItemTypes');
var MetaInfo = require('./MetaInfo');
var Post = require('./Post');
var Aggregator = require('./Aggregator');
var Keystore = require('./Keystore');
var Encryption = require('./Encryption');
var pubkey = Keystore.getKeys().publicKey;
var privkey = Keystore.getKeys().privateKey;

View File

@ -1,6 +1,6 @@
'use strict';
var encryption = require('./Encryption');
var Encryption = require('orbit-common/lib/Encryption');
class Post {
constructor(content) {
@ -9,7 +9,7 @@ class Post {
}
encrypt(privkey, pubkey) {
this.content = encryption.encrypt(this.content, privkey, pubkey);
this.content = Encryption.encrypt(this.content, privkey, pubkey);
}
}

View File

@ -1,81 +0,0 @@
'use strict';
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var ipfsAPI = {
cat: (ipfs, hash, cb) => {
var ipfscat = Promise.promisify(ipfs.cat);
return ipfscat(hash);
},
ls: async ((ipfs, hash) => {
var ipfsls = Promise.promisify(ipfs.ls);
return ipfsls(hash);
}),
add: async ((ipfs, filePath) => {
var addFiles = Promise.promisify((filePath, cb) => {
ipfs.add(filePath, { recursive: true }, cb);
});
return addFiles(filePath);
}),
getObject: async ((ipfs, hash) => {
var getObject = Promise.promisify(ipfs.object.get);
return getObject(hash);
}),
putObject: async ((ipfs, payload) => {
var putObject = Promise.promisify((payload, cb) => {
ipfs.object.put(new Buffer(JSON.stringify({ Data: payload })), "json", cb);
});
return putObject(payload);
}),
patchObject: async ((ipfs, root, target) => {
var patchObject = Promise.promisify((root, target, cb) => {
ipfs.object.patch(root, ["add-link", "next", target], cb);
});
return patchObject(root, target);
}),
statObject: async ((ipfs, hash) => {
var getObject = Promise.promisify(ipfs.object.stat);
return getObject(hash);
}),
pinObject: async ((ipfs, hash) => {
var pinObject = Promise.promisify(ipfs.pin.add);
return pinObject(hash);
}),
getPinned: async ((ipfs) => {
var getPinned = Promise.promisify(ipfs.pin.list);
var list = await (getPinned());
return Object.keys(list.Keys);
}),
swarmPeers: async ((ipfs) => {
var getPeers = Promise.promisify(ipfs.swarm.peers);
return getPeers();
}),
swarmConnect: async ((ipfs, hash) => {
var connect = Promise.promisify(ipfs.swarm.connect);
return await (connect(hash));
}),
dhtPut: async ((ipfs, key, value) => {
var put = Promise.promisify(ipfs.dht.put);
return put(key, value);
}),
dhtGet: async ((ipfs, key) => {
var get = Promise.promisify(ipfs.dht.get);
return get(key);
}),
dhtQuery: async ((ipfs, peerID) => {
var query = Promise.promisify(ipfs.dht.query);
return query(peerID);
}),
dhtFindProviders: async ((ipfs, hash) => {
var findprov = Promise.promisify(ipfs.dht.findprovs);
return findprov(hash);
}),
dhtFindPeer: async ((ipfs, peerID) => {
var findpeer = Promise.promisify(ipfs.dht.findpeer);
return findpeer(peerID);
})
}
module.exports = ipfsAPI;

View File

@ -1,39 +0,0 @@
'use strict';
var fs = require('fs');
var path = require('path');
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var ipfsdCtl = require('ipfsd-ctl');
const getUserHome = () => {
return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
};
const ipfsPath = path.resolve(getUserHome() + '/.ipfs');
if(!fs.existsSync(ipfsPath))
fs.mkdirSync(ipfsPath);
const startIpfs = async (() => {
let ipfs, nodeInfo;
try {
const ipfsNode = Promise.promisify(ipfsdCtl.local.bind(ipfsPath))
const ipfsd = await (ipfsNode());
const start = Promise.promisify(ipfsd.startDaemon.bind(ipfsd));
ipfs = await (start());
const getId = Promise.promisify(ipfs.id);
nodeInfo = await (getId())
} catch(e) {
console.log("Error initializing ipfs daemon:", e);
return null;
}
return { daemon: ipfs, nodeInfo: nodeInfo };
});
module.exports = async(() => {
return await(startIpfs());
});

View File

@ -1,78 +1,69 @@
'use strict';
var fs = require('fs');
var path = require('path');
var assert = require('assert');
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var encryption = require('../src/Encryption');
var ipfsDaemon = require('orbit-common/lib/ipfs-daemon');
var logger = require('orbit-common/lib/logger');
var Server = require('orbit-server/src/server');
var OrbitClient = require('../src/OrbitClient');
// var mockServerAddresses = [
// "localhost",
// "127.0.0.1"
// ];
// var serverConfig = {
// networkId: "anon-test",
// networkName: "Anonymous Networks TEST",
// salt: "thisisthenetworksalt",
// userDataPath: path.resolve("/tmp/anon-server-tests"),
// verifyMessages: true
// }
// Create the userDataPath in case it doesn't exist
// if(!fs.existsSync(serverConfig.userDataPath))
// fs.mkdirSync(serverConfig.userDataPath);
var serverConfig = {
networkId: "orbitdb-test",
networkName: "OrbitDB Test Network",
salt: "hellothisisdog",
userDataPath: "/tmp/orbitdb-tests",
verifyMessages: true
}
// Orbit
var host = 'localhost:3006';
var username = 'testrunner';
var password = '';
const host = 'localhost';
const port = 3006;
const username = 'testrunner';
const password = '';
const startServer = async (() => {
// TODO: this should be handled by orbit-server
if(!fs.existsSync(serverConfig.userDataPath))
fs.mkdirSync(serverConfig.userDataPath);
return new Promise(async((resolve, reject) => {
logger.setLevel('ERROR');
const ipfsd = await(ipfsDaemon());
const server = Server(ipfsd.daemon, ipfsd.nodeInfo, serverConfig);
server.app.listen(port, () => {
resolve(server);
});
}));
});
describe('Orbit Client', () => {
// let ipfs, server, orbit, httpServer;
let orbit;
let server, orbit;
let head = '';
let second = '';
let items = [];
let head = '';
let items = [];
let channel = 'abcdefgh';
before(function(done) {
// logger.setLevel('ERROR');
// Start ipfs daemon
// if(!ipfs && !server) {
// var startIpfsDaemon = new Promise(async((resolve, reject) => {
// ipfs = await(ipfsd());
// ipfs.nodeInfo.Addresses = mockServerAddresses;
// // Start hash-cache server
// server = require('../../ipfs-backend/server/server')(ipfs.daemon, ipfs.nodeInfo, serverConfig);
// httpServer = server.app.listen(3006, async(() => {
// logger.info('network server listening at http://localhost:%s', 3006);
// orbit = await(OrbitClient.connect(host, username, password, ipfs.daemon));
// resolve();
// }));
// }));
// startIpfsDaemon.then(done);
// } else {
// done();
// }
var start = () => new Promise(async((resolve, reject) => {
orbit = OrbitClient.connect(host, username, password);
before(async((done) => {
var initialize = () => new Promise(async((resolve, reject) => {
orbit = OrbitClient.connect(`${host}:${port}`, username, password);
orbit.channel(channel, '').delete();
resolve();
}));
start().then(done);
});
server = await(startServer());
await(initialize());
done();
}));
after(function(done) {
var deleteChannel = () => new Promise(async((resolve, reject) => {
orbit.channel(channel, '').delete();
if(orbit) orbit.channel(channel, '').delete();
resolve();
}));
deleteChannel().then(done);
// server.shutdown();
// httpServer.close();
// rmDir(serverConfig.userDataPath);
server.shutdown();
});
/* TESTS */
@ -80,9 +71,9 @@ describe('Orbit Client', () => {
it('connects to hash-cache-server', async((done) => {
assert.notEqual(orbit, null);
assert.notEqual(orbit.client, null);
assert.equal(orbit.user.id, 'QmcLzfQBKuvBYLsmgt4nkaUM7i7LNL37dPtnBZWgGpjPRW');
assert.equal(orbit.network.id, 'anon-test');
assert.equal(orbit.network.name, 'Anonymous Networks TEST');
assert.equal(orbit.user.id, 'Qmf5A5RSTQmcfvigT3j29Fqh2fAHRANk5ooBYKdWsPtr8U');
assert.equal(orbit.network.id, serverConfig.networkId);
assert.equal(orbit.network.name, serverConfig.networkName);
assert.notEqual(orbit.network.config.SupernodeRouting, null);
assert.notEqual(orbit.network.config.Bootstrap.length, 0);
done();
@ -158,7 +149,7 @@ describe('Orbit Client', () => {
it('adds an item to an empty channel', async((done) => {
try {
orbit.channel(channel, '').delete();
head = orbit.channel(channel, '').add('hello');
const head = orbit.channel(channel, '').add('hello');
assert.notEqual(head, null);
assert.equal(head.startsWith('Qm'), true);
assert.equal(head.length, 46);
@ -170,8 +161,8 @@ describe('Orbit Client', () => {
it('adds a new item to a channel with one item', async((done) => {
try {
var v = orbit.channel(channel, '').iterator().collect();
second = orbit.channel(channel, '').add('hello');
const head = orbit.channel(channel, '').iterator().collect()[0];
const second = orbit.channel(channel, '').add('hello');
assert.notEqual(second, null);
assert.notEqual(second, head);
assert.equal(second.startsWith('Qm'), true);
@ -585,17 +576,3 @@ describe('Orbit Client', () => {
});
});
// let rmDir = function(dirPath) {
// try { var files = fs.readdirSync(dirPath); }
// catch(e) { return; }
// if (files.length > 0)
// for (var i = 0; i < files.length; i++) {
// var filePath = dirPath + '/' + files[i];
// if (fs.statSync(filePath).isFile())
// fs.unlinkSync(filePath);
// else
// rmDir(filePath);
// }
// fs.rmdirSync(dirPath);
// };