-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
446 lines (385 loc) · 18 KB
/
Copy pathmain.cpp
File metadata and controls
446 lines (385 loc) · 18 KB
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
// ____ _ _ _ _
// | _ \| | | | | | (_)
// | |_) | | ___ ___| | _____| |__ __ _ _ _ __
// | _ <| |/ _ \ / __| |/ / __| '_ \ / _` | | '_ \ Demo blockchain C++ (Nâng cấp RSA)
// | |_) | | (_) | (__| < (__| | | | (_| | | | | | Thư viện: Crypto++
// |____/|_|\___/ \___|_|\_\___|_| |_|\__,_|_|_| |_| Tác giả: Nguyễn Trường Chinh
//
// Bản quyền: MIT LICENSE 2026
/* DOC
────────────────────────────────────────────────────────────────────────────────────────────
Hoàn thành ngày 01/07/2026
Demo đào block và thêm block vào blockchain kết hợp chữ ký số RSA
Tìm hiểu hash sha256 là gì ?
- Hash là hàm băm dữ liệu với đầu vào con người có thể đọc hiểu
nhưng đầu ra là 1 chuỗi được mã hóa dài 256(bit) ~ 64 ký tự
- Mã băm không thể giải mã (hàm 1 chiều)
- Chỉ cần thay đổi nhỏ ở input -> output thay đổi hoàn toàn
- Hiệu ứng thác đổ (Avalanche Effect): Thay đổi dù chỉ 1 dấu chấm,
toàn bộ chuỗi hash đầu ra sẽ biến đổi hoàn toàn không theo quy luật.
- Phân biệt trong code: MD5 dùng để băm Transaction Code (nhanh, ngắn gọn),
còn SHA256 dùng để băm toàn bộ Block bảo vệ chuỗi liên kết.
Cơ chế chữ ký số (RSA Signature)
- Mỗi User tham gia mạng lưới sở hữu một cặp khóa: Private và Public Key
- Người gửi ký giao dịch bằng Private Key -> Tạo ra Signature dạng chuỗi
- Toàn bộ mạng lưới sử dụng Public Key để kiểm tra (Verify) giao dịch đó
- Đảm bảo tính bảo mật, chống gian lận và mạo danh ví người khác
- Chống chối bỏ (Non-repudiation): Khi một giao dịch đã được ký bằng
Private Key hợp lệ, người gửi không thể phủ nhận giao dịch đó do chính họ tạo ra.
- Mã hóa Hex: Chữ ký nhị phân thô (Raw bytes) sinh ra từ bộ ký sẽ được mã hóa
sang dạng chuỗi ký tự Hex nhằm truyền tải và lưu trữ an toàn trong tệp JSON.
Cách hoạt động
- Khi 1 block yêu cầu vào blockchain, hệ thống sẽ:
- Gom dữ liệu (transactions) đã được xác thực chữ ký thành công
- Lấy hash của block trước (previous hash) để liên kết chuỗi
- Thêm timestamp (thời gian khởi tạo khối)
- Thêm nonce (số thử bắt đầu từ 0)
- Validator (hoặc miner) sẽ:
- Thử nhiều giá trị nonce khác nhau
- Mỗi lần thử sẽ tính lại hash của block
- Mục tiêu: tìm được hash thỏa điều kiện (ví dụ: bắt đầu bằng "0000")
-> Quá trình này gọi là Proof of Work (PoW)
Ví dụ:
hash = SHA256(block_id + data + previous_hash + time + nonce)
Validator sẽ lặp:
nonce = 0,1,2,3,... cho đến khi:
hash thỏa điều kiện độ khó (difficulty)
Ví dụ nếu difficulty = 4
-> khi hash ra a6957asd.... (hash chưa thỏa)
-> Khi hash ra 000089a5.... (hash thỏa)
Độ khó (difficulty):
- Quy định số lượng số 0 ở đầu hash
- Difficulty càng cao -> càng khó tìm -> càng tốn thời gian
- Tính bất đối xứng: Việc tìm ra Nonce hợp lệ cực kỳ khó và tốn tài nguyên phần cứng,
nhưng khi đã tìm ra, các node khác chỉ mất một phép toán để kiểm tra lại (Verify nhanh).
Khi tìm được hash hợp lệ:
- Block được "mine" thành công
- Được thêm vào blockchain
- Validator nhận reward (phần thưởng)
-> Quá trình này thường được gọi là đào coin
Cấu trúc Genesis Block (Khối nguyên thủy)
- Là khối đầu tiên (Index = 0) đặt nền móng cho toàn bộ Blockchain.
- Không có khối đứng trước, vì vậy Previous Hash được gán mặc định chuỗi 64 ký tự '0'.
- Chứa thông điệp khởi thủy của nhà sáng lập mạng lưới.
Đặc điểm quan trọng:
- Không thể đoán trước hash
- Chỉ có thể brute-force (thử sai liên tục)
- Dễ verify (kiểm tra nhanh), khó tạo (tính lâu)
- Tính bất biến (Immutability): Nếu bất kỳ ai cố tình chỉnh sửa dù chỉ 1 byte dữ liệu ở một khối cũ,
mã hash khối đó thay đổi -> gãy liên kết với khối tiếp theo -> toàn bộ chuỗi phía sau trở nên vô hiệu.
Ứng dụng:
- Blockchain (Bitcoin, Ethereum, Smart Contract)
- Bảo mật mật khẩu, chữ ký điện tử doanh nghiệp
- Kiểm tra toàn vẹn dữ liệu phần mềm, file tải về
Tóm tắt:
Mining = tìm nonce sao cho hash hợp lệ
-> đảm bảo tính bảo mật, toàn vẹn dữ liệu và phi tập trung của blockchain
────────────────────────────────────────────────────────────────────────────────────────────
*/
#include <iostream>
#include <vector>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <random>
#include <fstream>
#include <thread>
#include <atomic>
// thư viện tóm tắt dùng hash trong openssl gọn như python
// xem hướng dẫn và tải tại: https://github.com/trgchinhh/hashlib-cpp
#include "lib/hashlib.h"
// Thư viện phụ dùng cho json
#include "lib/json.hpp"
// Thư viện phụ dùng chrono sinh ngày tháng năm
#include "lib/datetime.h"
// Thư viện Crypto++ cho RSA
// Cài từ Ming64
// Lệnh cài: pacman -S mingw-w64-x86_64-cryptopp
#include <cryptopp/rsa.h>
#include <cryptopp/osrng.h>
#include <cryptopp/base64.h>
#include <cryptopp/hex.h>
#include <cryptopp/pssr.h>
// Thư viện indicator cho progress bar
// Tải thư viện tại: https://github.com/p-ranav/indicators
#include "lib/indicators/progress_bar.hpp"
#include "lib/indicators/cursor_control.hpp"
using namespace std;
using namespace dt;
using namespace hashlib;
using namespace chrono;
using namespace CryptoPP;
using namespace indicators;
using namespace indicators::option;
using json = nlohmann::ordered_json;
const vector<string> names = {
"Truong Chinh", "Truong An", "Duy Minh", "Duy Quang", "Ngoc Tu",
"Thanh Phuc", "Manh Hung", "The An", "Thien An", "Gia Bao",
"Khanh Dang", "Hai Dang", "Khanh Duy", "Bao Khanh", "Manh Cuong"
};
string time_now(){
return datetime::now().strftime("%H:%M:%S - %d/%m/%Y");
}
class UserBlockchain {
public:
static void save_all_user_keys() {
AutoSeededRandomPool rng;
json public_data;
json private_data;
for (const string& name : names) {
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 1024);
CryptoPP::RSA::PrivateKey privateKey(parameters);
CryptoPP::RSA::PublicKey publicKey(parameters);
string privateStr, publicStr;
Base64Encoder privSink(new StringSink(privateStr));
privateKey.DEREncode(privSink);
privSink.MessageEnd();
Base64Encoder pubSink(new StringSink(publicStr));
publicKey.DEREncode(pubSink);
pubSink.MessageEnd();
public_data[name] = { {"public_key", publicStr} };
private_data[name] = { {"private_key", privateStr} };
}
ofstream publicfile("public_keys.json");
publicfile << public_data.dump(4);
ofstream privatefile("private_keys.json");
privatefile << private_data.dump(4);
}
};
class TransactionManager {
public:
string clean_key(string key) {
if(key.length() > 30) {
return key.substr(0, 15) + "..." + key.substr(key.length() - 15);
}
return key;
}
json create_transaction() {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, names.size() - 1);
string send_name = names[dis(gen)];
string receive_name = names[dis(gen)];
while (send_name == receive_name) {
receive_name = names[dis(gen)];
}
ifstream publicfile("public_keys.json");
json public_keys = json::parse(publicfile);
string sender_public_key = public_keys[send_name]["public_key"];
string receiver_public_key = public_keys[receive_name]["public_key"];
uniform_real_distribution<> amount_dis(100000, 10000000);
double amount = round(amount_dis(gen) * 1000.0) / 1000.0;
double fee = round((amount * 0.005) * 1000000.0) / 1000000.0;
string t_time = time_now();
string code_data = send_name + receive_name + to_string(amount) + t_time;
string tx_code = md5(code_data).hexdigest();
json tx_data;
tx_data["Send name"] = send_name;
tx_data["Id send"] = clean_key(sender_public_key);
tx_data["Receive name"] = receive_name;
tx_data["Id receive"] = clean_key(receiver_public_key);
tx_data["Amount"] = amount;
tx_data["Fee"] = fee;
tx_data["Transaction code"] = tx_code;
tx_data["Time"] = t_time;
ifstream privatefile("private_keys.json");
json private_keys = json::parse(privatefile);
string send_priv_b64 = private_keys[send_name]["private_key"];
CryptoPP::RSA::PrivateKey privateKey;
StringSource privSource(send_priv_b64, true, new Base64Decoder);
privateKey.BERDecode(privSource);
string message = tx_data.dump();
AutoSeededRandomPool rng;
RSASS<PKCS1v15, CryptoPP::SHA256>::Signer signer(privateKey);
string signature;
StringSource(message, true,
new SignerFilter(rng, signer,
new HexEncoder(new StringSink(signature))
)
);
tx_data["Signature"] = signature;
return tx_data;
}
bool verify_transaction(json transaction) {
try {
string send_name = transaction["Send name"];
string hex_signature = transaction["Signature"];
json verify_data = transaction;
verify_data.erase("Signature");
string message = verify_data.dump();
ifstream pubFile("public_keys.json");
json public_keys = json::parse(pubFile);
string send_pub_b64 = public_keys[send_name]["public_key"];
CryptoPP::RSA::PublicKey publicKey;
StringSource pubSource(send_pub_b64, true, new Base64Decoder);
publicKey.BERDecode(pubSource);
RSASS<PKCS1v15, CryptoPP::SHA256>::Verifier verifier(publicKey);
string binary_signature;
StringSource(hex_signature, true, new HexDecoder(new StringSink(binary_signature)));
bool result = false;
StringSource(message + binary_signature, true,
new SignatureVerificationFilter(
verifier,
new ArraySink((CryptoPP::byte*)&result, sizeof(result)),
SignatureVerificationFilter::PUT_RESULT | SignatureVerificationFilter::SIGNATURE_AT_END
)
);
return result;
}
catch (...) {
return false;
}
}
};
class Block {
private:
int block_id;
json block_data;
string block_prev_hash;
string block_curr_hash;
string block_time;
int block_nonce;
int mining_time;
bool isvalid;
public:
Block(json data) {
this->block_data = data;
this->block_id = 0;
this->block_nonce = 0;
this->isvalid = false;
}
string hash_sha256(){
string hash_data = to_string(this->block_id)
+ this->block_data.dump()
+ this->block_prev_hash
+ this->block_time
+ to_string(this->block_nonce);
return sha256(hash_data).hexdigest();
}
friend class Blockchain;
};
class Blockchain {
private:
int difficulty;
vector<Block> chain;
string str_difficulty;
public:
int total_time = 0;
Blockchain(int difficulty){
this->difficulty = difficulty;
this->str_difficulty = string(this->difficulty, '0');
this->genesis_block();
}
void genesis_block(){
json genesis_data;
genesis_data["msg"] = "This is a genesis block in chain coding by NTC++";
genesis_data["time"] = time_now();
Block genesis_block(genesis_data);
genesis_block.block_time = time_now();
genesis_block.block_prev_hash = string(64, '0');
genesis_block.block_curr_hash = genesis_block.hash_sha256();
genesis_block.isvalid = true;
this->chain.push_back(genesis_block);
this->print_block(genesis_block);
}
void add_block(json data){
Block block(data);
Block prev_block = this->chain.back();
block.block_id = chain.size();
block.block_time = time_now();
block.block_prev_hash = prev_block.block_curr_hash;
block.block_curr_hash = block.hash_sha256();
auto start_time = high_resolution_clock::now();
while(block.block_curr_hash.substr(0, this->difficulty) != this->str_difficulty){
block.block_nonce++;
cout << "\rNonce: " << block.block_nonce;
block.block_curr_hash = block.hash_sha256();
}
auto end_time = high_resolution_clock::now();
block.mining_time = duration_cast<seconds>((end_time - start_time)).count();
this->total_time += block.mining_time;
cout << endl;
block.isvalid = this->isvalid_block(block, prev_block);
this->chain.push_back(block);
this->print_block(block);
}
bool isvalid_block(Block block, Block prev_block){
if(block.block_prev_hash != prev_block.block_curr_hash){
return false;
}
if(block.block_curr_hash != block.hash_sha256()){
return false;
}
return true;
}
bool isvalid_chain(){
for(int i = 0; i < this->chain.size(); i++){
Block block = this->chain[i];
if(!block.isvalid){
cout << "Block: " << i << " không thể xác thực" << endl;
return false;
}
}
cout << "Toàn bộ block đã được xác thực" << endl;
return true;
}
void print_block(Block block){
json display_data = block.block_data;
if (display_data.contains("Signature")) {
string sig = display_data["Signature"];
display_data["Signature"] = sig.substr(0, 10) + "..." + sig.substr(sig.length() - 10);
}
cout << "Index: " << block.block_id << endl;
cout << "Data: " << display_data.dump(4) << endl;
cout << "Prev hash: " << block.block_prev_hash << endl;
cout << "Curr hash: " << block.block_curr_hash << endl;
cout << "Time: " << block.block_time << endl;
cout << "Mining time: " << block.mining_time << " s" << endl;
cout << "\n" << endl;
}
};
void loading_bar(int seconds){
ProgressBar bar{
BarWidth{40},
Start{"["},
Fill{"#"},
Lead{"#"},
Remainder{"-"},
End{"]"},
ShowPercentage{true},
PrefixText{"Đang chuẩn bị giao dịch: "},
};
const int total = seconds * 25;
for(int i = 0; i <= total; i++){
bar.set_progress(i*100/total);
this_thread::sleep_for(milliseconds(40));
}
}
int main(){
show_console_cursor(false);
cout << "Đang khởi tạo địa chỉ ví cho toàn user tham gia..." << endl;
UserBlockchain userblockchain;
userblockchain.save_all_user_keys();
int number_blocks = 5;
int difficulty = 4;
Blockchain blockchain(difficulty);
TransactionManager tx_manager;
loading_bar(3);
cout << "Bắt đầu đào " << number_blocks << " block với độ khó " << difficulty << "\n\n";
this_thread::sleep_for(seconds(2));
for(int i = 0; i < number_blocks; i++) {
json ts = tx_manager.create_transaction();
if(tx_manager.verify_transaction(ts)) {
cout << "Đã xác thực giao dịch " << (i + 1) << endl;
blockchain.add_block(ts);
} else {
cout << "Không thể xác thực giao dịch " << (i + 1) << endl;
}
}
blockchain.isvalid_chain();
cout << "Hoàn tất đào " << number_blocks
<< " block với " << blockchain.total_time << " giây" << endl;
show_console_cursor(false);
return 0;
}