Skip to content

Commit

Permalink
feat: util functions to static methods
Browse files Browse the repository at this point in the history
  • Loading branch information
fgladisch committed Mar 26, 2018
1 parent adb59f9 commit 74cc2c1
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 104 deletions.
49 changes: 33 additions & 16 deletions src/block.model.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import { calculateBlockHash, createHash } from "./util/crypto";
import crypto = require("crypto");

export interface BlockParams {
index: number;
previousHash: string;
data: string;
}
export class Block<T> {
public static calculateHash(block: Block<any>): string {
const json: string = JSON.stringify(block.data);
const text: string = `${block.index}${block.predecessor}${block.time}${json}`;
return crypto
.createHash("sha256")
.update(text, "utf8")
.digest("hex");
}

public static validate(block: Block<any>, predecessor: Block<any>): boolean {
return (
predecessor.index + 1 === block.index &&
predecessor.hash === block.predecessor &&
Block.calculateHash(block) === block.hash
);
}

export class Block {
public index: number;
public previousHash: string;
public timestamp: number;
public data: string;
public hash?: string;
public predecessor: string;
public time: number;
public data: T | string;
public hash: string;

constructor(predecessor: Block<T>, data: T | string) {
if (predecessor) {
this.index = predecessor.index + 1;
this.predecessor = predecessor.hash;
} else {
this.index = 0;
this.predecessor = null;
}

constructor({ index, previousHash, data }: BlockParams) {
this.index = index;
this.previousHash = previousHash;
this.data = data;

this.timestamp = Date.now();
this.hash = calculateBlockHash(this);
this.time = Date.now();
this.hash = Block.calculateHash(this);
}
}
46 changes: 26 additions & 20 deletions src/blockchain.model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,53 @@ import { assert } from "chai";
import { Block } from "./block.model";
import { Blockchain } from "./blockchain.model";

const TEST_DATA: string = "TEST";
interface User {
id: number;
firstName: string;
lastName: string;
}

const TEST_DATA: User = {
firstName: "Elon",
id: 42,
lastName: "Musk"
};

describe("Blockchain", () => {
it("should create a new blockchain", () => {
const blockchain: Blockchain = new Blockchain();
const blockchain: Blockchain<User> = new Blockchain<User>();
assert.isDefined(blockchain.getGenesisBlock());
});

it("should create a blockchain with existing blocks", () => {
const testBlock: Block = new Block({
data: TEST_DATA,
index: 0,
previousHash: null
});
const blockchain: Blockchain = new Blockchain([testBlock]);
const testBlock: Block<User> = new Block(null, TEST_DATA);
const blockchain: Blockchain<User> = new Blockchain([testBlock]);
assert.equal(blockchain.getChain()[0], testBlock);
});

it("should generate a new block", () => {
const blockchain: Blockchain = new Blockchain();
blockchain.generateNextBlock(TEST_DATA);
const latestBlock: Block = blockchain.getLatestBlock();
const blockchain: Blockchain<User> = new Blockchain<User>();
blockchain.add(TEST_DATA);
const latestBlock: Block<User> = blockchain.getLatestBlock();
assert.equal(latestBlock.data, TEST_DATA);
});

it("should overwrite the existing chain", () => {
const blockchain: Blockchain = new Blockchain();
blockchain.generateNextBlock(TEST_DATA);
const copy: Blockchain = new Blockchain(blockchain.getChain().slice());
copy.generateNextBlock(TEST_DATA);
const blockchain: Blockchain<User> = new Blockchain<User>();
blockchain.add(TEST_DATA);
const copy: Blockchain<User> = new Blockchain(blockchain.getChain().slice());
copy.add(TEST_DATA);
assert.lengthOf(blockchain.getChain(), 2);
blockchain.replaceChain(copy.getChain());
assert.lengthOf(blockchain.getChain(), 3);
});

it("should not overwrite the existing chain with a corrupted chain", () => {
const blockchain: Blockchain = new Blockchain();
blockchain.generateNextBlock(TEST_DATA);
const copy: Blockchain = new Blockchain(blockchain.getChain().slice());
copy.generateNextBlock(TEST_DATA);
const corruptedChain: Block[] = copy.getChain().slice();
const blockchain: Blockchain<User> = new Blockchain();
blockchain.add(TEST_DATA);
const copy: Blockchain<User> = new Blockchain<User>(blockchain.getChain().slice());
copy.add(TEST_DATA);
const corruptedChain: Block<User>[] = copy.getChain().slice();
corruptedChain.splice(1, 1);
blockchain.replaceChain(corruptedChain);
assert.notEqual(blockchain.getChain(), corruptedChain);
Expand Down
57 changes: 30 additions & 27 deletions src/blockchain.model.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,57 @@
import { Block, BlockParams } from "./block.model";
import { isValidChain } from "./util/blockchain";
import { calculateBlockHash } from "./util/crypto";
import { Block } from "./block.model";

const GENESIS_BLOCK_PARAMS: BlockParams = {
data: "GENESIS",
index: 0,
previousHash: null
};
export class Blockchain<T> {
public static validate(genesis: Block<any>, chain: Block<any>[]): boolean {
if (Block.calculateHash(genesis) !== chain[0].hash) {
return false;
}

return chain.reduce((valid, block) => {
if (block.index === 0) {
return true;
}
return Block.validate(block, chain[block.index - 1]) || valid;
}, true);
}

export class Blockchain {
private chain: Block[];
private chain: Block<T>[];

constructor(chain?: Block[]) {
constructor(chain?: Block<T>[]) {
if (chain) {
this.chain = chain;
} else {
const genesisBlock: Block = new Block(GENESIS_BLOCK_PARAMS);
const genesisBlock: Block<T> = new Block<T>(null, "GENESIS");
this.chain = [genesisBlock];
}
}

public getChain(): Block[] {
public getChain(): Block<T>[] {
return this.chain;
}

public getGenesisBlock(): Block {
public getGenesisBlock(): Block<T> {
return this.chain[0];
}

public getLatestBlock(): Block {
public getLatestBlock(): Block<T> {
return this.chain[this.chain.length - 1];
}

public generateNextBlock(data: string): Block {
const previousBlock: Block = this.getLatestBlock();

const nextBlock: Block = new Block({
data,
index: previousBlock.index + 1,
previousHash: previousBlock.hash
});

public add(data: T): Block<T> {
const predecessor: Block<T> = this.getLatestBlock();
const nextBlock: Block<T> = new Block<T>(predecessor, data);
this.chain.push(nextBlock);

return nextBlock;
}

public replaceChain(newBlocks: Block[]): void {
if (isValidChain(this.getGenesisBlock(), newBlocks) && newBlocks.length > this.chain.length) {
public replaceChain(newBlocks: Block<T>[]): void {
const isValid: boolean = Blockchain.validate(this.getGenesisBlock(), newBlocks);
if (isValid && newBlocks.length > this.chain.length) {
this.chain = newBlocks;
}
}

public toJSON(): string {
return JSON.stringify(this.chain, null, 2);
}
}
27 changes: 0 additions & 27 deletions src/util/blockchain.ts

This file was deleted.

14 changes: 0 additions & 14 deletions src/util/crypto.ts

This file was deleted.

0 comments on commit 74cc2c1

Please sign in to comment.