You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			138 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			138 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
/* global dcodeIO, crypto, JSBI */
 | 
						|
const NONCE_LEN = 8;
 | 
						|
// Modify this value for difficulty scaling
 | 
						|
const FALLBACK_DIFFICULTY = 10;
 | 
						|
 | 
						|
const pow = {
 | 
						|
  // Increment Uint8Array nonce by '_increment' with carrying
 | 
						|
  incrementNonce(nonce, _increment = 1) {
 | 
						|
    let idx = NONCE_LEN - 1;
 | 
						|
    const newNonce = new Uint8Array(nonce);
 | 
						|
    let increment = _increment;
 | 
						|
    do {
 | 
						|
      const sum = newNonce[idx] + increment;
 | 
						|
      newNonce[idx] = sum % 256;
 | 
						|
      increment = Math.floor(sum / 256);
 | 
						|
      idx -= 1;
 | 
						|
    } while (increment > 0 && idx >= 0);
 | 
						|
    return newNonce;
 | 
						|
  },
 | 
						|
 | 
						|
  // Convert a Uint8Array to a base64 string
 | 
						|
  bufferToBase64(buf) {
 | 
						|
    function mapFn(ch) {
 | 
						|
      return String.fromCharCode(ch);
 | 
						|
    }
 | 
						|
    const binaryString = Array.prototype.map.call(buf, mapFn).join('');
 | 
						|
    return dcodeIO.ByteBuffer.btoa(binaryString);
 | 
						|
  },
 | 
						|
 | 
						|
  // Convert BigInteger to Uint8Array of length NONCE_LEN
 | 
						|
  bigIntToUint8Array(bigInt) {
 | 
						|
    const arr = new Uint8Array(NONCE_LEN);
 | 
						|
    let n;
 | 
						|
    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
 | 
						|
      // (bigInt / 256**n) % 256;
 | 
						|
      const denominator = JSBI.exponentiate(JSBI.BigInt('256'), JSBI.BigInt(n));
 | 
						|
      const fraction = JSBI.divide(bigInt, denominator);
 | 
						|
      const uint8Val = JSBI.remainder(fraction, JSBI.BigInt(256));
 | 
						|
      arr[idx] = JSBI.toNumber(uint8Val);
 | 
						|
    }
 | 
						|
    return arr;
 | 
						|
  },
 | 
						|
 | 
						|
  // Compare two Uint8Arrays, return true if arr1 is > arr2
 | 
						|
  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
 | 
						|
  async calcPoW(
 | 
						|
    timestamp,
 | 
						|
    ttl,
 | 
						|
    pubKey,
 | 
						|
    data,
 | 
						|
    _difficulty = null,
 | 
						|
    increment = 1,
 | 
						|
    startNonce = 0
 | 
						|
  ) {
 | 
						|
    const payload = new Uint8Array(
 | 
						|
      dcodeIO.ByteBuffer.wrap(
 | 
						|
        timestamp.toString() + ttl.toString() + pubKey + data,
 | 
						|
        'binary'
 | 
						|
      ).toArrayBuffer()
 | 
						|
    );
 | 
						|
 | 
						|
    const difficulty = _difficulty || FALLBACK_DIFFICULTY;
 | 
						|
    const target = pow.calcTarget(ttl, payload.length, difficulty);
 | 
						|
 | 
						|
    let nonce = new Uint8Array(NONCE_LEN);
 | 
						|
    nonce = pow.incrementNonce(nonce, startNonce); // initial value
 | 
						|
    let trialValue = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]);
 | 
						|
    const initialHash = new Uint8Array(
 | 
						|
      await crypto.subtle.digest('SHA-512', payload)
 | 
						|
    );
 | 
						|
    const innerPayload = new Uint8Array(initialHash.length + NONCE_LEN);
 | 
						|
    innerPayload.set(initialHash, NONCE_LEN);
 | 
						|
    let resultHash;
 | 
						|
    let nextNonce = nonce;
 | 
						|
    while (pow.greaterThan(trialValue, target)) {
 | 
						|
      nonce = nextNonce;
 | 
						|
      nextNonce = pow.incrementNonce(nonce, increment);
 | 
						|
      innerPayload.set(nonce);
 | 
						|
      // eslint-disable-next-line no-await-in-loop
 | 
						|
      resultHash = await crypto.subtle.digest('SHA-512', innerPayload);
 | 
						|
      trialValue = new Uint8Array(
 | 
						|
        dcodeIO.ByteBuffer.wrap(resultHash, 'hex').toArrayBuffer()
 | 
						|
      ).slice(0, NONCE_LEN);
 | 
						|
    }
 | 
						|
    return pow.bufferToBase64(nonce);
 | 
						|
  },
 | 
						|
 | 
						|
  calcTarget(ttl, payloadLen, difficulty = FALLBACK_DIFFICULTY) {
 | 
						|
    // payloadLength + NONCE_LEN
 | 
						|
    const totalLen = JSBI.add(JSBI.BigInt(payloadLen), JSBI.BigInt(NONCE_LEN));
 | 
						|
    // ttl converted to seconds
 | 
						|
    const ttlSeconds = JSBI.divide(JSBI.BigInt(ttl), JSBI.BigInt(1000));
 | 
						|
    // ttl * totalLen
 | 
						|
    const ttlMult = JSBI.multiply(ttlSeconds, JSBI.BigInt(totalLen));
 | 
						|
    // 2^16 - 1
 | 
						|
    const two16 = JSBI.subtract(
 | 
						|
      JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(16)), // 2^16
 | 
						|
      JSBI.BigInt(1)
 | 
						|
    );
 | 
						|
    // ttlMult / two16
 | 
						|
    const innerFrac = JSBI.divide(ttlMult, two16);
 | 
						|
    // totalLen + innerFrac
 | 
						|
    const lenPlusInnerFrac = JSBI.add(totalLen, innerFrac);
 | 
						|
    // difficulty * lenPlusInnerFrac
 | 
						|
    const denominator = JSBI.multiply(
 | 
						|
      JSBI.BigInt(difficulty),
 | 
						|
      lenPlusInnerFrac
 | 
						|
    );
 | 
						|
    // 2^64 - 1
 | 
						|
    const two64 = JSBI.subtract(
 | 
						|
      JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(64)), // 2^64
 | 
						|
      JSBI.BigInt(1)
 | 
						|
    );
 | 
						|
    // two64 / denominator
 | 
						|
    const targetNum = JSBI.divide(two64, denominator);
 | 
						|
    return pow.bigIntToUint8Array(targetNum);
 | 
						|
  },
 | 
						|
};
 |