Source: lib/BlockHeader.js

'use strict';

const utils = require("./utils");
const Merkle = require("./Merkle");
const Web3 = require("web3");
/**
 * Block header
 */
class BlockHeader {
  /**
   * Create a block header
   * @param {number} blockNumber the block number
   * @param {string} previousHash hex string of previous hash
   * @param {string[]} data array of hex string representing transactions
   */
  constructor(blockNumber, previousHash, data) {
    const len = data.length;
    if (len > 256) {
      data = data.splice(0, 256);
    }
    for (let i = len; i < 256; i++) {
      data.push("");
    }
    this.blockNumber = +blockNumber;
    this.previousHash = previousHash; // 32 bytes
    this.data = data
    if (blockNumber == 0) {
      this.merkle = null;
      this.merkleRoot = "";
    } else {
      this.merkle = new Merkle(this.data.map((datum) => utils.removeHexPrefix(datum)));
      this.merkle.makeTree();
      this.merkleRoot = utils.addHexPrefix(utils.bufferToHex(this.merkle.getRoot(), false)); // 32 bytes
    }
    this.sigR = ''; // 32 bytes
    this.sigS = ''; // 32 bytes
    this.sigV = ''; // 1 byte
    this.timestamp = new Date().getTime();
  }

  /**
   * Set signature of the header
   * @param {string} signature hex string signature of the header
   */
  setSignature(signature) {
    let sig = utils.removeHexPrefix(signature);
    let sigR = sig.substring(0, 64);
    let sigS = sig.substring(64, 128);
    let sigV = parseInt(sig.substring(128, 130), 16);
    if (sigV < 27) {
      sigV += 27;
    }
    this.sigR = sigR;
    this.sigS = sigS;
    this.sigV = sigV.toString(16).padStart(2, "0");
  }

  /**
   * Sha3 hash of the hex string representing transactions in this block header
   */
  get hash() {
    return Web3.utils.sha3(utils.addHexPrefix(this.toString(true)));
  }

  /**
   * Convert this block header to string
   * @param {boolean} includingSig whether to include the signature in the block header string
   */
  toString(includingSig) {
    let blkNumHexString = this.blockNumber.toString(16).padStart(64, "0");
    let rawBlockHeader = blkNumHexString + utils.removeHexPrefix(this.previousHash) + utils.removeHexPrefix(this.merkleRoot);
    if (includingSig) {
      rawBlockHeader += this.sigR + this.sigS + this.sigV;
    }
    return rawBlockHeader;
  }

  /**
   * Create a block header from json
   * @param {Object} json json object representing block header
   */
  static fromJSON(json) {
    const len = json.data.length;
    for (let i = len; i < 256; i++) {
      json.data.push("");
    }
    let blockHeader = new BlockHeader(json.blockNumber, json.previousHash, json.data);
    blockHeader.sigR = json.sigR;
    blockHeader.sigS = json.sigS;
    blockHeader.sigV = json.sigV;
    blockHeader.timestamp = json.timestamp || new Date().getTime();
    return blockHeader;
  }

  /**
   * Convert this block header to json
   */
  toJSON() {
    return {
      blockNumber: this.blockNumber,
      previousHash: this.previousHash,
      merkleRoot: this.merkleRoot,
      data: this.data.filter((tx) => tx.length > 0),
      sigR: this.sigR,
      sigS: this.sigS,
      sigV: this.sigV,
      timestamp: this.timestamp,
    }
  }
}

module.exports = BlockHeader;