-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
121 lines (108 loc) · 3.82 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import SHA256 = require("crypto-js/sha256");
export const GENESIS_BLOCK_BODY = "Genesis block - First block in the chain ⛓️ !";
/** Helper function that create timestamp (i.e. valid UTC timestamps to validate with external ressources) */
export const createNewUtcDate = () => new Date().getTime().toString().slice(0,-3);
/** Helper function to hash a block using SHA256 */
export const hashBlock = (block: Block) => SHA256(JSON.stringify(block)).toString()
/**
* Simple block data model
*/
export class Block {
// Defining the Block types
hash: string;
height: number;
body: string; // In this exaple, the b./node_modules/.bin/tslint --initody will be a string
timestamp: string;
previousBlockHash: string;
constructor(bodyData: string) {
this.hash = "";
this.height = 0;
this.body = bodyData;
this.timestamp = "0";
this.previousBlockHash = "";
}
}
/**
* Blockchain
* Data model with function to support:
* - addBlock();
* - getLatestBlock();
* - getBlockByHash();
* - validateBlock();
* - validateChain();
*/
export class Blockchain {
chain: Block[];
constructor(){
this.chain = [];
// Creating the Genesis block to the blockchain
this.addNewBlock(GENESIS_BLOCK_BODY);
}
// Blockchain functions
/** Adding block to the blockchain */
addNewBlock(bodyData: string) {
const newBlock = new Block(bodyData);
// Adding a height to the new block
newBlock.height = this.chain.length;
// Adding a timestamp
newBlock.timestamp = createNewUtcDate();
// Adding the previous block hash if it exists
if(this.chain.length > 0){
newBlock.previousBlockHash = this.chain[this.chain.length - 1].hash;
}
// Setting the hash of the new block:
// Hashing the new block and returning it as a string
newBlock.hash = hashBlock(newBlock);
// Adding the new block to the chain
this.chain.push(newBlock);
}
/** Simply gets the last block added to the chain */
getLatestBlock() {
return this.chain[this.chain.length - 1];
}
/** Getting the block inside the chain from an hash */
getBlockByHash(hash: string){
return this.chain.find(block => block.hash === hash);
}
/** Validate that the block has the right hash */
validateBlock(blockToVerify: Block) {
// Saving the hash to verify before resetting the hash to an empty and running the sha256 hashing function
const oldHash = blockToVerify.hash;
const clonedBlockToVerify = Object.assign({}, blockToVerify);
clonedBlockToVerify.hash = "";
// comparing the 2 hashs to see if the data is the same
const blockIsValid = SHA256(JSON.stringify(clonedBlockToVerify)).toString() === oldHash;
if(!blockIsValid) {
console.error("❌ INVALID BLOCK FOUND:");
console.error(blockToVerify);
return false;
}
return true;
}
/** Validate that the chain is valid */
validateChain() {
// First get the last block
let blockToValidate = this.getLatestBlock();
// Validate each block until the genesis block and return if the chain is valid or not
while (blockToValidate.height >= 0) {
const isValidBlock = this.validateBlock(blockToValidate);
// If the block is not valid, return that the chain is not valid
if(!isValidBlock){
return false;
}
// If it's the genesis block, the chain is completely validated
if(blockToValidate.height === 0){
return true;
}
// Otherwise, get the previous block and validate it
const previousBlockHashToValidate = blockToValidate.previousBlockHash;
const nextBlockToValidate = this.getBlockByHash(previousBlockHashToValidate);
// If the next block didn't exist, the chain isn't valid
if(!nextBlockToValidate){
return false;
}
// Change the block to validate
blockToValidate = nextBlockToValidate;
}
}
}