Proof Of Work (PoW)
Using Deno v1
proofOfWork.ts
ts
import { createHash } from "https://deno.land/std/hash/mod.ts";
interface Transaction {
from: string;
to: string;
value: number;
}
/**
* Generate SHA-256
* @param {string} message
* @returns {string}
*/
const generateHash = (message: string): string => {
return createHash("sha256").update(message).toString();
};
/**
* Generate UUID V4
* @returns {string}
*/
const generateUuid = () => {
const PATTERN = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
return PATTERN.replace(/[xy]/g, (char: string) => {
const random = (Math.random() * 16) | 0,
idx = char === "x" ? random : (random & 0x3) | 0x8;
return idx.toString(16);
});
};
class Block {
timeStamp: string;
transactions: Transaction[];
prevHash: string;
hash: string;
nonce: number;
constructor(timeStamp = "", transactions: Transaction[] = []) {
this.timeStamp = timeStamp;
this.transactions = transactions;
this.prevHash = "";
this.hash = this.getHash();
this.nonce = 0;
}
getHash(): string {
return generateHash(this.timeStamp + JSON.stringify(this.transactions) + this.prevHash + this.nonce);
}
toString(): string {
return `[BLOCK]
TimeStamp: ${this.timeStamp}
Transactions: ${JSON.stringify(this.transactions)}
Previous Hash: ${this.prevHash}
Hash: ${this.hash}
Nonce: ${this.nonce}
`;
}
proofOfWork(difficulty: number) {
while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) {
this.nonce++;
this.hash = this.getHash();
}
}
}
class BlockChain {
chain: Block[];
difficulty: number;
constructor() {
this.chain = [this.getGenesisBlock()];
this.difficulty = 2;
}
getGenesisBlock(): Block {
const now = new Date().getTime().toString();
return new Block(now);
}
getLastBlock(): Block {
return this.chain[this.chain.length - 1];
}
addNewBlock(newBlock: Block) {
newBlock.prevHash = this.getLastBlock().hash;
// newBlock.hash = newBlock.getHash();
newBlock.proofOfWork(this.difficulty);
// I M M U T A B I L I T Y
const frozenBlock = Object.freeze(newBlock);
this.chain.push(frozenBlock);
}
isValid(): boolean {
for (let i = 1; i < this.chain.length; i++) {
const currentBlock = this.chain[i];
const prevBlock = this.chain[i - 1];
if (currentBlock.hash !== currentBlock.getHash() || prevBlock.hash !== currentBlock.prevHash) {
return false;
}
}
return true;
}
}
proofOfWork.test.ts
deno test proofOfWork.test.ts
ts
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
Deno.test("BlockChain", () => {
const myChain = new BlockChain();
const stAddress = generateUuid();
const ndAddress = generateUuid();
const now = new Date();
const stBlock = new Block(now.getTime().toString(), [
{
from: stAddress,
to: ndAddress,
value: 20
},
{
from: stAddress,
to: ndAddress,
value: 10
}
]);
myChain.addNewBlock(stBlock);
assertEquals(myChain.getLastBlock().hash, stBlock.hash);
const ndBlock = new Block(now.setMinutes(now.getMinutes() + 30).toString(), [
{
from: ndAddress,
to: stAddress,
value: 5
}
]);
myChain.addNewBlock(ndBlock);
assertEquals(myChain.getLastBlock().hash, ndBlock.hash);
const rdBlock = new Block(now.setHours(now.getHours() + 12).toString(), [
{
from: ndAddress,
to: stAddress,
value: 15
}
]);
myChain.addNewBlock(rdBlock);
assertEquals(myChain.getLastBlock().hash, rdBlock.hash);
assertEquals(myChain.isValid(), true);
});