-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimpleChain.js
195 lines (179 loc) · 6.75 KB
/
simpleChain.js
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/* ===== SHA256 with Crypto-js ===============================
| Learn more: Crypto-js: https://github.com/brix/crypto-js |
| =========================================================*/
// import {addLevelDBData, getAmountEntries, getLevelDBData} from "./levelSandbox";
const db = require('./levelSandbox');
const SHA256 = require('crypto-js/sha256');
/* ===== Block Class ==============================
| Class with a constructor for block |
| ===============================================*/
class Block{
constructor(data){
this.hash = "",
this.height = 0,
this.body = data,
this.time = 0,
this.previousBlockHash = ""
}
}
/* ===== Blockchain Class ==========================
| Class with a constructor for new blockchain |
| ================================================*/
class Blockchain{
constructor(){
this.getBlockHeight().then((h) => {
if (h < 0) {
// create Genesis block
let genesisBlock = new Block("First block in the chain - Genesis block");
genesisBlock.previousBlockHash = "";
genesisBlock.height = 0;
genesisBlock.time = new Date().getTime().toString().slice(0, -3);
genesisBlock.hash = SHA256(JSON.stringify(genesisBlock)).toString();
// add Genesis block to chain
db.addLevelDBData(0, JSON.stringify(genesisBlock).toString())
.then((genesisBlock) => {
console.log('Added Genesis Block:');
console.log(genesisBlock);
}).catch((err) => {
console.log('Unable to add Genesis block!', err);
});
}
}).catch((err) => {
console.log('Unable to add Genesis Block!', err);
});
}
// Returns a new block
static createBlock(description) {
return new Block(description)
}
// Add new block
addBlock(newBlock) {
return new Promise((resolve, reject) => {
// Block height
this.getBlockHeight().then((height) => {
newBlock.height = height+1;
// UTC timestamp
newBlock.time = new Date().getTime().toString().slice(0, -3);
// previous block hash
this.getBlock(height).then((block) => {
newBlock.previousBlockHash = block.hash;
// Block hash with SHA256 using newBlock and converting to a string
newBlock.hash = SHA256(JSON.stringify(newBlock)).toString();
// Adding block object to chain
db.addLevelDBData(newBlock.height, JSON.stringify(newBlock).toString())
.catch((err) => {
reject(err);
}).then((block) => {
resolve(JSON.parse(block));
});
}).catch((err) => {
reject('Unable to get previous block! => block not added.', err);
});
}).catch((err) => {
reject('Unable to get block height! => block not added.', err);
});
});
}
// Get block height
getBlockHeight(){
return new Promise((resolve, reject) => {
db.getAmountEntries().then((height) => {
resolve(height-1);
}).catch((err) => {
reject(err);
});
});
}
// get block
getBlock(blockHeight){
return new Promise((resolve, reject) => {
db.getLevelDBData(blockHeight).then((value) => {
resolve(JSON.parse(value));
}).catch((err) => {
reject(err);
});
});
}
// getBlocksAddedBy returns all blocks added by given address.
getBlocksAddedBy(address) {
return new Promise((resolve, reject) => {
db.getBlocksForAddress(address).then((blocks) => {
resolve(blocks);
}).catch((err) => {
reject(err);
});
});
}
// getBlockWithHash returns the block with given hash.
getBlockWithHash(hash) {
return new Promise((resolve, reject) => {
db.getBlockWithHash(hash).then((block) => {
resolve(block);
}).catch((err) => {
reject(err);
});
});
}
// validate the whole Blockchain and return corresponding error log
async validateChain() {
let errorLog = [];
try {
const blockHeight = await this.getBlockHeight();
let errorLogBuf = await this.validateBlockConnectivity(blockHeight);
errorLog.concat(errorLogBuf);
errorLogBuf = await this.validateEachBlock(blockHeight);
errorLog.concat(errorLogBuf);
} catch (err) {
console.log('validateChain:', err);
}
return errorLog;
}
// validate block connectivity and return corresponding error log
async validateBlockConnectivity(blockHeight) {
let errorLog = [];
for (let i = 0; i < blockHeight; i++) {
try {
let currentBlock = await this.getBlock(i);
let nextBlock = await this.getBlock(i+1);
if (currentBlock.hash !== nextBlock.previousBlockHash) {
errorLog.push(i);
}
} catch (err) {
console.log('validateBlockConnectivity: Unable to receive each block.', err);
}
}
return errorLog;
}
// validate each block and return the corresponding error log
async validateEachBlock(blockHeight) {
let errorLog = [];
for (let i = 0; i <= blockHeight; i++) {
try {
await this.validateBlock(blockHeight);
} catch (err) {
console.log('validateEachBlock: ', err);
errorLog.push(i);
}
}
return errorLog;
}
// validate block
validateBlock(blockHeight){
return new Promise((resolve, reject) => {
this.getBlock(blockHeight).then((block) => {
let blockHash = block.hash;
block.hash = '';
let validBlockHash = SHA256(JSON.stringify(block)).toString();
if (blockHash === validBlockHash) {
resolve(true);
} else {
reject('Block #'+blockHeight+' invalid hash:\n'+blockHash+'<>'+validBlockHash);
}
}).catch((err) => {
console.log('validateBlock: Unable to get block #'+blockHeight);
reject(err);
})
});
}
}
module.exports = Blockchain;