Mise à jour de GitLab prévue ce samedi 23 octobre 2021 à partir de 9h00 CET

Commit 111f565a authored by Cédric Moreau's avatar Cédric Moreau
Browse files

Merkle logic externalized to an npm module 'merkle'

parent 44ebdb5f
var sha1 = require('sha1');
var async = require('async');
function Merkle(strings) {
this.leaves = [];
this.depth = 0;
this.levels = [];
// PUBLIC
this.feed = function(anyData) {
if(anyData && anyData.match(/^[\w\d]{40}$/)){
this.leaves.push(anyData.toUpperCase());
}
else{
this.leaves.push(sha1(anyData).toUpperCase());
}
return this;
};
this.process = function(done) {
var obj = this;
async.waterfall([
function(callback){
// Compute tree depth
var pow = 0;
while(Math.pow(2, pow) < obj.leaves.length){
pow++;
}
obj.depth = pow;
callback(null, obj.depth);
},
function(depth, callback){
// Compute the nodes of each level
var levels = [];
for (var i = 0; i < depth; i++) {
levels.push([]);
}
levels[depth] = obj.leaves;
for (var j = depth-1; j >= 0; j--) {
levels[j] = getNodes(levels[j+1]);
}
obj.levels = levels;
callback();
}
], function (err) {
done(err, obj.levels[0]);
});
};
this.getRoot = function() {
return this.levels[0][0];
};
// PRIVATE
function getNodes(leaves) {
var remainder = leaves.length % 2;
var nodes = [];
for (var i = 0; i < leaves.length - 1; i = i + 2) {
nodes[i/2] = sha1(leaves[i] + leaves[i+1]).toUpperCase();
}
if(remainder === 1){
nodes[((leaves.length-remainder)/2)] = leaves[leaves.length - 1];
}
return nodes;
}
// INIT
for (var i = 0; i < strings.length; i++) {
this.feed(strings[i]);
}
}
module.exports = function (strings) {
return new Merkle(strings);
};
\ No newline at end of file
var sha1 = require('sha1');
var async = require('async');
var merkle = require('../lib/merkle');
var merkle = require('merkle');
var mongoose = require('mongoose');
var Amendment = mongoose.model('Amendment');
var Schema = mongoose.Schema;
......@@ -80,14 +80,10 @@ ContractSchema.methods = {
tmpMembers.sort();
// Merkle checking
membersMerkle = merkle(tmpMembers);
membersMerkle.process(function (err) {
if(!err){
if(am.membersRoot !== membersMerkle.getRoot())
err = "Computed members Merkle '" + membersMerkle.getRoot() + "' does not match Amendment '" + am.membersRoot + "'";
}
callback(err);
});
membersMerkle = merkle(tmpMembers, 'sha1').process();
if(am.membersRoot !== membersMerkle.root())
err = "Computed members Merkle '" + membersMerkle.root() + "' does not match Amendment '" + am.membersRoot + "'";
callback(err);
},
function(callback){
......@@ -110,14 +106,10 @@ ContractSchema.methods = {
tmpVoters.sort();
// Merkle checking
votersMerkle = merkle(tmpVoters);
votersMerkle.process(function (err) {
if(!err){
if(am.votersRoot !== votersMerkle.getRoot())
err = "Computed voters Merkle '" + votersMerkle.getRoot() + "' does not match Amendment '" + am.votersRoot + "'";
}
callback(err);
});
votersMerkle = merkle(tmpVoters, 'sha1').process();
if(am.votersRoot !== votersMerkle.root())
err = "Computed voters Merkle '" + votersMerkle.root() + "' does not match Amendment '" + am.votersRoot + "'";
callback(err);
},
function(callback){
......@@ -129,14 +121,10 @@ ContractSchema.methods = {
callback("First amendment requires to have the same keys as members and voters.");
}
else{
var initKeysMerkle = merkle(obj.initKeys);
initKeysMerkle.process(function (err) {
if(!err){
if(am.membersRoot !== initKeysMerkle.getRoot())
err = "Members must be the same keys as the initKeys used for this server.";
}
callback(err);
});
var initKeysMerkle = merkle(obj.initKeys, 'sha1').process();
if(am.membersRoot !== initKeysMerkle.root())
err = "Members must be the same keys as the initKeys used for this server.";
callback(err);
}
}
// Case : Amendment 1+
......
var sha1 = require('sha1');
var async = require('async');
var jpgp = require('../lib/jpgp');
var merkle = require('../lib/merkle');
var merkle = require('merkle');
var mongoose = require('mongoose');
var fs = require('fs');
var PublicKey = mongoose.model('PublicKey');
......
......@@ -69,7 +69,7 @@ Here is an example of members Merkle tree with 5 members (taken from [Tree Hash
Where A,B,C,D,E are already hashed data.
With such a tree structure, NodeCoin consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.
With such a tree structure, NodeCoin considers the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.
Hence it is quite easy for anyone who wants to check if a `Z` member joined the NodeCoin community as it would alter the `E` branch of the tree:
......
......@@ -34,7 +34,7 @@
"ejs": "",
"mongoose": "",
"async": "",
"sha1": "",
"merkle": "",
"underscore": ""
},
"devDependencies": {
......
var should = require('should');
var assert = require('assert');
var fs = require('fs');
var mongoose = require('mongoose');
var async = require('async');
var nodecoin = require('../app/lib/nodecoin');
var merkle = require('../app/lib/merkle');
var merkleM0, merkleM2, merkleV0, merkleV2;
before(function(done) {
var members0 = [];
var members2 = [];
var voters0 = [];
var voters2 = [];
async.waterfall([
function(callback){
members0 = [
"2E69197FAB029D8669EF85E82457A1587CA0ED9C",
"C73882B64B7E72237A2F460CE9CAB76D19A8651E",
"33BBFC0C67078D72AF128B5BA296CC530126F372"
];
members0.sort();
voters0 = members0.slice();
// Merkle
merkleM0 = merkle(members0);
merkleV0 = merkle(voters0);
callback();
},
function(callback){
members2 = members0.slice();
members2.push("31A6302161AC8F5938969E85399EB3415C237F93");
members2.sort();
voters2 = voters0.slice();
voters2.splice(voters2.indexOf('C73882B64B7E72237A2F460CE9CAB76D19A8651E'));
// Merkle
merkleM2 = merkle(members2);
merkleV2 = merkle(voters2);
callback();
}
], function (err, result) {
var merkles = [merkleM0, merkleM2, merkleV0, merkleV2];
async.forEach(merkles, function(merkle, cb) {
merkle.process(cb);
}, done);
});
});
describe('Merkle', function(){
describe('of BB-AM0-OK', function(){
it('voters root should be F5ACFD67FC908D28C0CFDAD886249AC260515C90', function(){
assert.equal(merkleV0.getRoot(), "F5ACFD67FC908D28C0CFDAD886249AC260515C90");
});
it('members root should be F5ACFD67FC908D28C0CFDAD886249AC260515C90', function(){
assert.equal(merkleM0.getRoot(), "F5ACFD67FC908D28C0CFDAD886249AC260515C90");
});
});
describe('of BB-AM2-OK', function(){
it('voters root should be DC7A9229DFDABFB9769789B7BFAE08048BCB856F', function(){
assert.equal(merkleV2.getRoot(), "DC7A9229DFDABFB9769789B7BFAE08048BCB856F");
});
it('members root should be F92B6F81C85200250EE51783F5F9F6ACA57A9AFF', function(){
assert.equal(merkleM2.getRoot(), "F92B6F81C85200250EE51783F5F9F6ACA57A9AFF");
});
});
});
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment