Home Reference Source Repository

src/lib/dagchain/dagblock.js

import { DAGNode } from 'ipld-dag-pb';
import { validateTransactions, createRewardTransaction } from './../transaction';
import { hexFromMultihash, getDifficulty } from './../../utils';
import { isValid } from 'crypto-chain-validator';
import { BlockError } from '../errors';
import IPFS from 'ipfs-api';
const ipfs = new IPFS();

const calculateHash = async block => {
  block = await createDAGNode(block);
  return block.multihash.toString('hex').substring(4);
}

const createDAGNode = ({index, prevHash, time, transactions, nonce}) => {
  return new Promise((resolve, reject) => {
    DAGNode.create(JSON.stringify({
      index,
      prevHash,
      time,
      transactions,
      nonce
    }), [], 'sha2-256', (error, dagNode) => {
      if (error) {
        return reject(error);
      }
      return resolve(dagNode)
    });
  });
}

/**
 * Create new block
 *
 * @param transactions {array}
 * @param previousBlock {object}
 * @param address {string}
 * @return {index, prevHash, time, transactions, nonce}
 */
class DAGBlock {
	constructor(options) {
    if (!options) return;
		if (typeof options === 'object' && !Buffer.isBuffer(options)) return this.newBlock(options);
		else return this.get(options);
	}

	async newBlock({transactions, previousBlock, address}) {
		transactions = transactions.slice();
		transactions.push(createRewardTransaction(address, previousBlock.index + 1));
		this.data = {
			index: previousBlock.index + 1,
			prevHash: previousBlock.hash,
			time: Math.floor(new Date().getTime() / 1000),
			transactions,
			nonce: 0
		};
		this.data.hash = await calculateHash(this.data);
		return this.data;
	}
	transformBlock({multihash, data}) {
	  data = JSON.parse(data.toString());
	  data.hash = hexFromMultihash(multihash);
	  return data;
	};
	async get(hash) {
		this.node = await ipfs.object.get(hash);
		return this.transformBlock(this.node);
	}
	async put(block) {
		this.node = await createDAGNode(block || this.data);
		return await ipfs.object.put(this.node);
	}
}

/**
 * validate block
 *
 * @param {object} previousBlock
 * @param {object} block
 * @param {number} difficulty
 * @param {number} unspent
 */
const validate = async (previousBlock, block, difficulty, unspent) => {
	if (!isValid('block', block)) throw BlockError('data');
	if (previousBlock.index + 1 !== block.index) throw BlockError('index');
	if (previousBlock.hash !== block.prevHash) throw BlockError('prevhash');
	if (await calculateHash(block) !== block.hash) throw BlockError('hash');
	if (getDifficulty(block.hash) > difficulty) throw BlockError('difficulty');
	return validateTransactions(block.transactions, unspent);
};

export {
  DAGBlock,
  validate,
  calculateHash,
  createDAGNode
};