commit
d91f04930c
@ -1,67 +1,104 @@
|
|||||||
const hash = require('js-sha512');
|
const hash = require('js-sha512');
|
||||||
const bb = require('bytebuffer');
|
const bb = require('bytebuffer');
|
||||||
|
const BigInteger = require('jsbn').BigInteger;
|
||||||
|
|
||||||
|
const NONCE_LEN = 8;
|
||||||
|
// Modify this value for difficulty scaling
|
||||||
|
const NONCE_TRIALS = 1000;
|
||||||
|
|
||||||
// Increment Uint8Array nonce by 1 with carrying
|
// Increment Uint8Array nonce by 1 with carrying
|
||||||
function incrementNonce(nonce) {
|
function incrementNonce(nonce) {
|
||||||
let idx = nonce.length - 1;
|
let idx = NONCE_LEN - 1;
|
||||||
nonce[idx] += 1;
|
const newNonce = new Uint8Array(nonce);
|
||||||
// Nonce will just reset to 0 if all values are 255 causing infinite loop, should never happen
|
newNonce[idx] += 1;
|
||||||
while (nonce[idx] == 0 && idx >= 0) {
|
// Nonce will just reset to 0 if all values are 255 causing infinite loop
|
||||||
nonce[--idx] += 1;
|
while (newNonce[idx] === 0 && idx > 0) {
|
||||||
|
idx -= 1;
|
||||||
|
newNonce[idx] += 1;
|
||||||
}
|
}
|
||||||
return nonce;
|
return newNonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a Uint8Array to a base64 string
|
// Convert a Uint8Array to a base64 string
|
||||||
function bufferToBase64(buf) {
|
function bufferToBase64(buf) {
|
||||||
let binstr = Array.prototype.map.call(buf, function (ch) {
|
function mapFn(ch) {
|
||||||
return String.fromCharCode(ch);
|
return String.fromCharCode(ch);
|
||||||
}).join('');
|
};
|
||||||
return bb.btoa(binstr);
|
const binaryString = Array.prototype.map.call(buf, mapFn).join('');
|
||||||
|
return bb.btoa(binaryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert javascript number to Uint8Array of length 8
|
// Convert BigInteger to Uint8Array of length NONCE_LEN
|
||||||
function numberToUintArr(numberVal) {
|
function bigIntToUint8Array(bigInt) {
|
||||||
let arr = new Uint8Array(8);
|
const arr = new Uint8Array(NONCE_LEN);
|
||||||
for (let idx = 7; idx >= 0; idx--) {
|
let n;
|
||||||
let n = 8 - (idx + 1);
|
for (let idx = NONCE_LEN - 1; idx >= 0; idx -= 1) {
|
||||||
|
n = NONCE_LEN - (idx + 1);
|
||||||
// 256 ** n is the value of one bit in arr[idx], modulus to carry over
|
// 256 ** n is the value of one bit in arr[idx], modulus to carry over
|
||||||
arr[idx] = (numberVal / 256**n) % 256;
|
// (bigInt / 256**n) % 256;
|
||||||
|
const uint8Val = (bigInt.divide((new BigInteger('256')).pow(n))).mod(new BigInteger('256'));
|
||||||
|
arr[idx] = uint8Val.intValue();
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare two Uint8Arrays, return true if arr1 is > arr2
|
||||||
|
function greaterThan(arr1, arr2) {
|
||||||
|
// Early exit if lengths are not equal. Should never happen
|
||||||
|
if (arr1.length !== arr2.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (let i = 0, len = arr1.length; i < len; i += 1) {
|
||||||
|
if (arr1[i] > arr2[i])
|
||||||
|
return true;
|
||||||
|
if (arr1[i] < arr2[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Return nonce that hashes together with payload lower than the target
|
// Return nonce that hashes together with payload lower than the target
|
||||||
function calcPoW(timestamp, ttl, pub_key, data) {
|
function calcPoW(timestamp, ttl, pubKey, data) {
|
||||||
const leadingString = timestamp.toString() + ttl.toString() + pub_key;
|
const leadingString = timestamp.toString() + ttl.toString() + pubKey;
|
||||||
const leadingArray = new Uint8Array(bb.wrap(leadingString, 'binary').toArrayBuffer());
|
const leadingArray = new Uint8Array(bb.wrap(leadingString, 'binary').toArrayBuffer());
|
||||||
// Payload constructed from concatenating timestamp, ttl and pubkey strings, converting to Uint8Array
|
// Payload constructed from concatenating timestamp, ttl and pubkey strings,
|
||||||
// and then appending to the message data array
|
// converting to Uint8Array and then appending to the message data array
|
||||||
const payload = leadingArray + data;
|
const payload = new Uint8Array(leadingArray.length + data.length);
|
||||||
const nonceLen = 8;
|
payload.set(leadingArray);
|
||||||
// Modify this value for difficulty scaling
|
payload.set(data, leadingArray.length);
|
||||||
// TODO: Have more informed reason for setting this to 100
|
|
||||||
const nonceTrialsPerByte = 1000;
|
// payloadLength + NONCE_LEN
|
||||||
let nonce = new Uint8Array(nonceLen);
|
const totalLen = (new BigInteger(payload.length.toString())).add(new BigInteger(NONCE_LEN.toString()));
|
||||||
let trialValue = numberToUintArr(Number.MAX_SAFE_INTEGER);
|
// ttl * totalLen
|
||||||
// Target is converter to Uint8Array for simple comparison with trialValue
|
const ttlMult = (new BigInteger(ttl.toString())).multiply(totalLen);
|
||||||
const target = numberToUintArr(Math.floor(Math.pow(2, 64) / (
|
// ttlMult / (2^16 - 1)
|
||||||
nonceTrialsPerByte * (
|
const innerFrac = ttlMult.divide((new BigInteger('2').pow(16)).subtract(new BigInteger('1')));
|
||||||
payload.length + nonceLen + (
|
// totalLen + innerFrac
|
||||||
(ttl * ( payload.length + nonceLen ))
|
const lenPlusInnerFrac = totalLen.add(innerFrac);
|
||||||
/ Math.pow(2, 16)
|
// NONCE_TRIALS * lenPlusInnerFrac
|
||||||
)
|
const denominator = (new BigInteger(NONCE_TRIALS.toString())).multiply(lenPlusInnerFrac);
|
||||||
)
|
// 2^64 - 1
|
||||||
)));
|
const two64 = (new BigInteger('2').pow(64)).subtract(new BigInteger('1'));
|
||||||
|
// two64 / denominator
|
||||||
|
const targetNum = two64.divide(denominator);
|
||||||
|
const target = bigIntToUint8Array(targetNum);
|
||||||
|
|
||||||
|
let nonce = new Uint8Array(NONCE_LEN);
|
||||||
|
let trialValue = bigIntToUint8Array(new BigInteger(Number.MAX_SAFE_INTEGER.toString()));
|
||||||
const initialHash = new Uint8Array(bb.wrap(hash(payload), 'hex').toArrayBuffer());
|
const initialHash = new Uint8Array(bb.wrap(hash(payload), 'hex').toArrayBuffer());
|
||||||
while (trialValue > target) {
|
const innerPayload = new Uint8Array(initialHash.length + NONCE_LEN);
|
||||||
|
innerPayload.set(initialHash, NONCE_LEN);
|
||||||
|
let resultHash;
|
||||||
|
while (greaterThan(trialValue, target)) {
|
||||||
nonce = incrementNonce(nonce);
|
nonce = incrementNonce(nonce);
|
||||||
trialValue = (new Uint8Array(bb.wrap(hash(nonce + initialHash), 'hex').toArrayBuffer())).slice(0, 8);
|
innerPayload.set(nonce);
|
||||||
|
resultHash = hash(innerPayload);
|
||||||
|
trialValue = (new Uint8Array(bb.wrap(resultHash, 'hex').toArrayBuffer())).slice(0, NONCE_LEN);
|
||||||
}
|
}
|
||||||
return bufferToBase64(nonce);
|
return bufferToBase64(nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start calculation in child process when main process sends message data
|
// Start calculation in child process when main process sends message data
|
||||||
process.on('message', (msg) => {
|
process.on('message', (msg) => {
|
||||||
process.send({nonce: calcPoW(msg.timestamp, msg.ttl, msg.pub_key, msg.data)});
|
process.send({nonce: calcPoW(msg.timestamp, msg.ttl, msg.pubKey, new Uint8Array(msg.data))});
|
||||||
});
|
});
|
Loading…
Reference in New Issue