Source: Models/Transaction.js

const SHA256 = require('crypto-js/sha256');
const { USER_PRIVATE_KEY } = require('../Utils/config');
const EC = require('elliptic').ec;
const ec = new EC('secp256k1');

/**
 * This class represent Transaction.
 * Transaction are being made between two people, and require their public key
 * the person who sends the amount must sign the transaction with his key,
 * if the key does not match or the transaction has not been signed, blockchain will not accept it
 * @class Transaction
 */
class Transaction {
    /**
     * @constructs
     * Construction of Transaction class.
     *
     * @param sender Wallet adress of the sender
     * @param receiver Wallet adress of the receiver
     * @param amount Amount of coins to send
     */
    constructor(sender, receiver, amount) {
        this.sender = sender;
        this.receiver = receiver;
        this.amount = amount;
        this.timestamp = Date.now();
    }

    /**
     * This function return calculated hash of the transaction
     * @returns {hash}
     */
    calculateHash() {
        return SHA256(this.sender + this.receiver + this.amount + this.timestamp).toString();
    }

    /**
     * This function checks the provided key and then signs the transaction with it
     * @param signingKey Key wich is used to sign the transaction
     */
    signTransaction(signingKey) {
        if (signingKey.getPublic('hex') !== this.sender && this.sender !== 'MINING REWARD') {
            throw new Error('You cant sign this transaction!')
        }

        const transactionHash = this.calculateHash();
        const sign = signingKey.sign(transactionHash, 'base64');
        this.signature = sign.toDER('hex');
    }

    /**
     * This function checks if the the transaction is valid.
     * It is checked first if the sender address is from mine (When we have mining reward transaction) - in that case functions return true
     * In other case function checks if transaction has signature and verify it.
     * @returns {Buffer | Boolean | boolean | * | PromiseLike<boolean>|boolean}
     */
    isValid() {
        if (this.sender === 'MINING REWARD')
            return true;

        if (!this.signature || this.signature.length === 0) {
            throw new Error('No signature found!');
        }

        const publicKey = ec.keyFromPublic(this.sender, 'hex');
        return publicKey.verify(this.calculateHash(), this.signature);
    }

    /**
     * This function is used to recreate Transaction class from received json transaction.
     * @param data
     * @returns {Transaction}
     */
    static fromResponse(data) {
        const myKey = ec.keyFromPrivate(USER_PRIVATE_KEY);
        const recreatedTransaction = new Transaction(
            data.sender,
            data.receiver,
            data.amount
        );
        recreatedTransaction.signTransaction(myKey);
        return recreatedTransaction;
    }
}

module.exports = Transaction;