Skip to content

Commit

Permalink
refactor: bls lib usage (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
antouhou authored Dec 25, 2020
1 parent 85443e6 commit 942368c
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 73 deletions.
105 changes: 79 additions & 26 deletions lib/crypto/bls.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,48 @@
const EventEmitter = require('events');
const BlsSignatures = require('bls-signatures');

const eventNames = {
LOADED_EVENT: 'LOADED',
};

const events = new EventEmitter();
let isLoading = false;
let instance = null;

function compileWasmModule() {
isLoading = true;
return BlsSignatures()
.then((loadedInstance) => {
instance = loadedInstance;
isLoading = false;
events.emit(eventNames.LOADED_EVENT);
});
}

const bls = {
isLoading: false,
instance: null,
events: new EventEmitter(),
LOADED: 'LOADED',
load() {
this.isLoading = true;
return BlsSignatures()
.then((instance) => {
this.instance = instance;
this.isLoading = false;
this.events.emit(this.LOADED);
});
},
/**
* Compiles BlsSignature instance if it wasn't compiled yet
* and returns module instance
* @return {Promise<BlsSignatures>}
*/
getInstance() {
return new Promise((resolve) => {
if (this.instance) {
resolve(this.instance);
if (instance) {
resolve(instance);
}

if (this.isLoading) {
this.events.once(this.LOADED, () => {
resolve(this.instance);
if (isLoading) {
events.once(eventNames.LOADED_EVENT, () => {
resolve(instance);
});
} else {
this.load().then(() => {
resolve(this.instance);
compileWasmModule().then(() => {
resolve(instance);
});
}
});
},

/**
* Validate bls signature
* @param {string} signatureHex
Expand All @@ -40,17 +51,17 @@ const bls = {
* @return {Promise<boolean>}
*/
async verifySignature(signatureHex, messageHash, publicKeyHex) {
const blsInstance = await this.getInstance();
const BLS = await this.getInstance();
let result = false;

let thresholdSignature;
let quorumPubKey;
let publicKey;
let aggregationInfo;

try {
thresholdSignature = blsInstance.Signature.fromBytes(Uint8Array.from(Buffer.from(signatureHex, 'hex')));
quorumPubKey = blsInstance.PublicKey.fromBytes(Uint8Array.from(Buffer.from(publicKeyHex, 'hex')));
aggregationInfo = blsInstance.AggregationInfo.fromMsgHash(quorumPubKey, messageHash);
thresholdSignature = BLS.Signature.fromBytes(Uint8Array.from(Buffer.from(signatureHex, 'hex')));
publicKey = BLS.PublicKey.fromBytes(Uint8Array.from(Buffer.from(publicKeyHex, 'hex')));
aggregationInfo = BLS.AggregationInfo.fromMsgHash(publicKey, messageHash);

thresholdSignature.setAggregationInfo(aggregationInfo);

Expand All @@ -63,12 +74,54 @@ const bls = {
// Values from emscripten compiled code can't be garbage collected in JS,
// so they have to be released first using .delete method
if (thresholdSignature) { thresholdSignature.delete(); }
if (quorumPubKey) { quorumPubKey.delete(); }
if (publicKey) { publicKey.delete(); }
if (aggregationInfo) { aggregationInfo.delete(); }
}

return result;
},

/**
* @param publicKeys
* @param signersBits
* @return {Promise<string>} - hex string with the aggregated public key
*/
async aggregatePublicKey(publicKeys, signersBits) {
const BLS = await this.getInstance();
const pks = [];
let i = 0;

publicKeys.forEach((publicKey) => {
if (signersBits[i]) {
pks.push(BLS.PublicKey.fromBytes(
Uint8Array.from(Buffer.from(publicKey, 'hex')),
));
}
i += 1;
});

const aggregatedKey = BLS.PublicKey.aggregate(pks);
const aggregatedKeyHex = Buffer.from(aggregatedKey.serialize()).toString('hex');

// Freeing memory, looks at the comment in finally {} of verifySignature for more info
pks.forEach(pk => pk.delete());
aggregatedKey.delete();

return aggregatedKeyHex;
},
/**
*
* @param {string} signatureHex
* @param {Uint8Array} messageHash
* @param {string[]} publicKeys
* @param {boolean[]} signersBits
* @return {Promise<boolean>}
*/
async verifyAggregatedSignature(signatureHex, messageHash, publicKeys, signersBits) {
const publicKey = await this.aggregatePublicKey(publicKeys, signersBits);

return this.verifySignature(signatureHex, messageHash, publicKey);
},
};

module.exports = bls;
60 changes: 19 additions & 41 deletions lib/deterministicmnlist/QuorumEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,50 +317,28 @@ QuorumEntry.prototype.isValidQuorumSig = async function isValidQuorumSig() {
* @param {SimplifiedMNList} mnList - MNList for the block (quorumHash)
* @return {Promise<boolean>}
*/
QuorumEntry.prototype.isValidMemberSig = function isValidMemberSig(mnList) {
return new Promise((resolve, reject) => {
if (mnList.blockHash !== this.quorumHash) {
return reject(new Error(`Wrong Masternode List for quorum: blockHash
${mnList.blockHash} doesn't correspond with quorumHash ${this.quorumHash}`));
}
if (this.isOutdatedRPC) {
return reject(new Error('Quorum cannot be verified: node running on outdated DashCore version (< 0.16)'));
}

return bls.getInstance().then((blsInstance) => {
const quorumMembers = this.getAllQuorumMembers(mnList);
const aggregatedSignature = blsInstance.Signature.fromBytes(
Uint8Array.from(Buffer.from(this.membersSig, 'hex')),
);
const signersBits = BitArray.uint8ArrayToBitArray(
Uint8Array.from(Buffer.from(this.signers, 'hex')),
);

// aggregate all pubKeyOperators of signing members
const pks = [];
let i = 0;

quorumMembers.forEach((member) => {
if (signersBits[i]) {
pks.push(blsInstance.PublicKey.fromBytes(
Uint8Array.from(Buffer.from(member.pubKeyOperator, 'hex')),
));
}
i += 1;
});
QuorumEntry.prototype.isValidMemberSig = async function isValidMemberSig(mnList) {
if (mnList.blockHash !== this.quorumHash) {
throw new Error(`Wrong Masternode List for quorum: blockHash
${mnList.blockHash} doesn't correspond with quorumHash ${this.quorumHash}`);
}
if (this.isOutdatedRPC) {
throw new Error('Quorum cannot be verified: node running on outdated DashCore version (< 0.16)');
}

const aggregationInfo = blsInstance.AggregationInfo.fromMsgHash(
blsInstance.PublicKey.aggregate(pks), Uint8Array.from(this.getCommitmentHash()),
);
aggregatedSignature.setAggregationInfo(aggregationInfo);
const quorumMembers = this.getAllQuorumMembers(mnList);
const publicKeyStrings = quorumMembers.map(quorumMember => quorumMember.pubKeyOperator);

const result = aggregatedSignature.verify();
aggregatedSignature.delete();
aggregationInfo.delete();
const signersBits = BitArray.uint8ArrayToBitArray(
Uint8Array.from(Buffer.from(this.signers, 'hex')),
);

resolve(result);
});
});
return bls.verifyAggregatedSignature(
this.membersSig,
Uint8Array.from(this.getCommitmentHash()),
publicKeyStrings,
signersBits,
);
};

/**
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dashevo/dashcore-lib",
"version": "0.19.16",
"version": "0.19.17",
"description": "A pure and powerful JavaScript Dash library.",
"author": "Dash Core Group, Inc. <dev@dash.org>",
"main": "index.js",
Expand Down
8 changes: 4 additions & 4 deletions test/bloomfilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var BloomFilter = require('../lib/bloomfilter');
var PrivateKey = require('../lib/privatekey');

function getPayloadBuffer(messageBuffer) {
return new Buffer(messageBuffer.slice(48), 'hex');
return Buffer.from(messageBuffer.slice(48), 'hex');
}

// convert a hex string to a bytes buffer
Expand All @@ -24,7 +24,7 @@ function ParseHex(str) {
result.push(parseInt(str.substring(0, 2), 16));
str = str.substring(2, str.length);
}
var buf = new Buffer(result, 16);
var buf = Buffer.from(result, 16);
return buf;
}

Expand Down Expand Up @@ -63,13 +63,13 @@ describe('BloomFilter', function() {
assert(filter.contains(ParseHex('b9300670b4c5366e95b2699e8b18bc75e5f729c5')));

var actual = filter.toBuffer();
var expected = new Buffer('03614e9b050000000000000001', 'hex');
var expected = Buffer.from('03614e9b050000000000000001', 'hex');

actual.should.deep.equal(expected);
});

it('deserialize a buffer', function() {
var buffer = new Buffer('03614e9b050000000000000001', 'hex');
var buffer = Buffer.from('03614e9b050000000000000001', 'hex');
var filter = BloomFilter.fromBuffer(buffer);

assert(filter.contains(ParseHex('99108ad8ed9bb6274d3980bab5a85c048f0950c8')));
Expand Down

0 comments on commit 942368c

Please sign in to comment.