Skip to content

Commit

Permalink
feat: structured value, safe set/get
Browse files Browse the repository at this point in the history
  • Loading branch information
nredko committed Apr 9, 2020
1 parent 04e8e80 commit 0c14fbc
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 1,707 deletions.
16 changes: 16 additions & 0 deletions client/rootCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const fs = require('fs');

class RootCache {
constructor() {
this.ROOT_FN = ".root";
}
get() {
let rootBinary = fs.readFileSync(this.ROOT_FN);
return JSON.parse(rootBinary);
}
set(root) {
fs.writeFileSync(this.ROOT_FN, JSON.stringify(root));
}
}

module.exports = RootCache;
41 changes: 41 additions & 0 deletions client/rootService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const RootCache = require('./rootCache');

class RootService {
constructor(immuClient) {
this.immuClient = immuClient;
this.cache = new RootCache();
}
init(onComplete) {
this.immuClient.currentRoot({}, (err, response) => {
if (err) {
throw "Root not found";
}

this.cache.set(response);

if (onComplete)
onComplete(response);
});
}
getRoot(onComplete) {
try {
let root = this.cache.get();
onComplete(root);
} catch (_) {
this.immuClient.currentRoot({}, (err, response) => {
if (err) {
throw "Root not found";
}

this.cache.set(response);
onComplete(response);
});
}
}

setRoot(value) {
this.cache.set(value);
}
};

module.exports = RootService;
43 changes: 43 additions & 0 deletions handler/safeGet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const itemUtils = require("./../utils/item");
const Proofs = require("./../utils/proofs");

const call = (immuClient, rootService, request, callback) => {
rootService.getRoot((root) => {
let index = { index: root.index };
let protoReq = {
key: { key: Buffer.from(request.key) },
rootIndex: index
};

immuClient._safeGet(protoReq, (err, msg) => {
if (err) {
callback(err, null);
return;
}

let verified = Proofs.verify(msg.proof, itemUtils.hash(msg.item), root);

if (verified) {
let toCache = {
index: msg.proof.at,
root: msg.proof.root
};

rootService.setRoot(toCache);
}

let i = msg.item;
let valueRaw = Buffer.from(i.value);

callback(null, {
index: i.index,
key: i.key,
value: valueRaw.subarray(8),
timestamp: Number(valueRaw.slice(0, 8).readBigUInt64BE()),
verified
});
});
});
}

module.exports.call = call;
59 changes: 59 additions & 0 deletions handler/safeSet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const itemUtils = require("./../utils/item");
const Proofs = require("./../utils/proofs");

const call = (immuClient, rootService, request, callback) => {
rootService.getRoot((root) => {
let index = { index: root.index };
var valueB = Buffer.alloc(8 + request.kv.value.length);

var buffTimestamp = Buffer.alloc(8);
buffTimestamp.writeBigUInt64BE(BigInt(Date.now()));
buffTimestamp.copy(valueB, 0);

var buffValue = Buffer.from(request.kv.value);
buffValue.copy(valueB, 8);

let protoReq = {
kv: {
key: Buffer.from(request.kv.key),
value: valueB
},
rootIndex: index
};

immuClient._safeSet(protoReq, (err, msg) => {
if (err) {
callback(err, null);
return;
}

let item = {
index: msg.index,
key: protoReq.kv.key,
value: protoReq.kv.value
};

if (Buffer.compare(itemUtils.hash(item), msg.leaf) != 0)
throw "Proof does not match the given item";

let verified = Proofs.verify(msg, msg.leaf, root);

if (verified) {
let toCache = { index: msg.index, root: msg.root };
rootService.setRoot(toCache);
}

callback(null, {
index: msg.index,
leaf: msg.leaf,
root: msg.root,
at: msg.at,
inclusionPath: msg.inclusionPath,
consistencyPath: msg.consistencyPath,
verified
});
});
});
}

module.exports.call = call;
175 changes: 36 additions & 139 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,160 +1,57 @@
crypto = require ("crypto");
const proofs = require("./utils/proofs");
const digest = require("./utils/digest");

module.exports.inclusionVerify = function (path, at, i, root, leaf) {
return _inclusionVerify(path, at, i, root, leaf)
module.exports.inclusionVerify = function (path, at, i, root, leaf) {
return proofs.inclusionVerify(path, at, i, root, leaf)
};

module.exports.consistencyVerify = function (path, at, i, root, leaf) {
return _consistencyVerify(path, at, i, root, leaf)
module.exports.consistencyVerify = function (path, at, i, root, leaf) {
return proofs.consistencyVerify(path, at, i, root, leaf)
};

module.exports.digest = function (index, key, value) {
return _digest(index, key, value)
module.exports.digest = function (index, key, value) {
return digest(index, key, value);
};

module.exports.client = function (immudbUrl) {
module.exports.client = function (immudbUrl) {
return _client(immudbUrl)
};

const LeafPrefix = 0;
const NodePrefix = 1;

const SHA256_SIZE = 32

const BigInt0 = BigInt("0")
const BigInt1 = BigInt("1")
const Bigint2 = BigInt("2")

function _consistencyVerify(path, second, first, secondHash, firstHash) {
first = BigInt(first)
second = BigInt(second)
let l = path.length
if (first == second && firstHash.compare(secondHash) == 0 && l == 0){
return true
}
if (first < second || l == 0){
return false
}
let pp = path.slice()
if( _isPowerOfTwo(first + BigInt1)){
pp.push(firstHash)
}
let fn = first
let sn = second

while ((fn%Bigint2) == BigInt1){
fn >>= BigInt1
sn >>= BigInt1
}
let fr = pp[0]
let sr = pp[0]

for (let k = 1; k < p.length; k++){
if (sn == BigInt0){
return false
}
if ((fn%Bigint2) == BigInt1 || fn == sn){
let tmp = Buffer.from([NodePrefix])
tmp.copy( p[k] , 1)

tmp.copy( fr , 1+SHA256_SIZE)
fr = Buffer.from( crypto.createHash('sha256').update(tmp).digest())

tmp.copy( sr , 1+SHA256_SIZE)
sr = Buffer.from( crypto.createHash('sha256').update(tmp).digest())

while ((fn%Bigint2) == BigInt0 && fn != BigInt0){
fn >>= BigInt1
sn >>= BigInt1
}
}else{
let tmp = Buffer.from([NodePrefix])
tmp.copy( sr , 1)

tmp.copy( p[k] , 1+SHA256_SIZE)
sr = Buffer.from( crypto.createHash('sha256').update(tmp).digest())
}
fn >>= BigInt1
sn >>= BigInt1
}

return fr.compare(firstHash) == 0 && sr.compare(secondHash) == 0 && sn == BigInt0
}

function _isPowerOfTwo(x) {
return (x!=BigInt0 && (x & (x-BigInt1)) == BigInt0)
}

function _inclusionVerify(p, at, i, root, leaf) {
at = BigInt(at)
i = BigInt(i)

if (i > at || (at > 0 && p.length == 0)) {
return false
}

let h = leaf

for (let k = 0; k < p.length; k++){
let t = Buffer.from([NodePrefix])

if (i % BigInt("2") == 0 && i != at){
t.copy(h, 1)
t.copy(p[k], 1+SHA256_SIZE)
}else{
t.copy(p[k], 1)
t.copy(h, 1+SHA256_SIZE)
}
const hash = crypto.createHash('sha256');
h = Buffer.from(hash.update(t).digest())
i = i/BigInt("2")
at = at/BigInt("2")
}
return at == i && h.compare(root) == 0
function _client(immudbUrl) {
if (!immudbUrl)
return null;

}
const grpc = require("grpc");
const protoLoader = require("@grpc/proto-loader");
const path = require("path");

function _digest( index , key, value ) {
index = index.toString()
k = Buffer.from(key)
v = Buffer.from(value)
c = Buffer.alloc(1+8+8+k.length+v.length)
c[0] = LeafPrefix
buf_index = Buffer.alloc(8)
buf_index.writeBigUInt64BE(BigInt(index))
buf_index.copy(c, 1)
buf_key = Buffer.alloc(8)
buf_key.writeBigUInt64BE(BigInt(k.length))
buf_key.copy(c, 1+8)
k.copy(c, 1+8+8)
v.copy(c, 1+8+8+k.length)
const hash = crypto.createHash('sha256');
d = hash.update(c).digest()
return Buffer.from(d)
}
const safeGet = require('./handler/safeGet');
const safeSet = require('./handler/safeSet');
const rootService = require('./client/rootService');

function _client(immudbUrl) {
if (immudbUrl) {
grpc = require('grpc');
protoLoader = require('@grpc/proto-loader');

const PROTO_PATH = require('path').resolve(__dirname + '/schema.proto');

const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
const PROTO_PATH = path.resolve(__dirname + "/schema.proto");
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});

const immudb_schema = grpc.loadPackageDefinition(packageDefinition).immudb.schema;
const client = new immudb_schema.ImmuService(immudbUrl, grpc.credentials.createInsecure());

return client;
} else {
return null;
}

const immudbSchema = grpc.loadPackageDefinition(packageDefinition).immudb.schema;
const client = new immudbSchema.ImmuService(immudbUrl, grpc.credentials.createInsecure());
const rs = new rootService(client);

rs.init();

client._safeGet = client.safeGet;
client._safeSet = client.safeSet;

client.safeGet = (request, callback) => safeGet.call(client, rs, request, callback);
client.safeSet = (request, callback) => safeSet.call(client, rs, request, callback);

return client;
}
Loading

0 comments on commit 0c14fbc

Please sign in to comment.