首先先來看最後呈現效果
成果網址 : https://block-chain-simulation.onrender.com/
GitHub專案 : https://github.com/weiawesome/block_chain_simulation
(如果還喜歡可以在 GitHub 給我個 Star ★ )
詳細影片教學: https://www.youtube.com/watch?v=1ArVvAQKVmM
其實也費我不小功夫,接下來就以下幾點開始專案
UI 規劃上
專案架構上
語言與框架 就採用 React.js 加上 TypeScript
專案架構的組織上 :
├── src
│ ├── App.tsx
│ ├── App.css
│ │
│ ├── models
│ │ ├── block.ts
│ │
│ ├── utils
│ ├── encrypt.ts
│
├── other files
接下來重點 去實踐以上設計
檔案位置存放在 "src/models/block.ts"
首先 從 UML 文件 設計開始
(此份UML圖檔由 PlantUML 所生成)
Block(區塊) 物件:
屬性名稱 | 屬性作用 | 方法名稱 | 方法作用 |
---|---|---|---|
block_top | 區塊標頭 | setMarkleRoot | 計算區塊哈希 |
transaction | 所有交易內容 | checkDifficulty | 檢查 區塊哈希 是否符合 區塊難度 |
block_hash | 區塊哈希值 | addTransaction | 增加交易 |
status | 是否已完成挖掘 |
BlockTop(區塊標頭) 物件:
屬性名稱 | 屬性作用 | 方法名稱 | 方法作用 |
---|---|---|---|
version | 版本號 | setBlockHash | 計算 Markle根 |
previous_hash | 前一區塊哈希值 | toString | 物件轉字串方法 |
time_stamp | 時間戳記 | ||
difficulty | 區塊難度 | ||
nonce | 隨機數 | ||
markle_root | Markle根 |
Transaction(交易) 物件:
屬性名稱 | 屬性作用 | 方法名稱 | 方法作用 |
---|---|---|---|
id | 交易哈希值 | signature | 為此筆交易簽名 |
from | 交易發起者(地址) | verify | 驗證此筆交易的簽名 |
to | 交易接收者(地址) | ||
amount | 交易金額 | ||
fees | 給予礦工手續費 |
Block 物件
export class Block {
// 當前是否已完成挖掘 true->已挖掘完成 false->未挖掘
status:boolean;
// 區塊標頭
block_top: BlockTop;
// 所有交易內容
transaction:Transaction[];
// 區塊哈希值
block_hash:string;
// 建構子 (透過傳遞 前區塊哈希值 初始化 Block 物件)
constructor(hash:string) {
this.status=false;
this.block_top=new BlockTop(hash);
this.transaction=[];
this.block_top.setMarkleRoot(this.transaction);
this.block_hash=""
this.setBlockHash();
}
// 檢查 區塊哈希值 是否符合 區塊難度
checkDifficulty(){
for (let i=0;i<this.block_top.difficulty;i++){
if(this.block_hash[i]!=="0"){
this.status=false;
return false;
}
}
this.status=true;
return true;
}
// 計算 區塊哈希值
setBlockHash(){
this.block_hash=Hash_SHA256(Hash_SHA256(this.block_top.toString()));
}
// 增加交易
addTransaction(transaction:Transaction):boolean{
for (let i=0;i<this.transaction.length;i++){
if(this.transaction[i].id===transaction.id){
return false;
}
}
this.transaction.push(transaction);
this.block_top.setMarkleRoot(this.transaction);
this.setBlockHash();
this.status=false;
return true;
}
}
BlockTop 物件
export class BlockTop{
// 版本號
version:string;
// 前一區塊哈希值
previous_hash:string;
// 時間戳記
time_stamp:string;
// 區塊難度
difficulty:number;
// 隨機數
nonce:bigint;
// Markle根
markle_root:string;
// 建構子 (透過 前區塊哈希值 進行初始化 BlockTop 物件)
constructor(hash:string) {
this.version="v1";
this.previous_hash=hash;
let time=new Date();
this.time_stamp=time.toLocaleString();
this.difficulty=1;
this.nonce= BigInt(0);
this.markle_root="";
}
// 物件轉為字串方法
toString(){
return this.version.toString()+this.previous_hash.toString()+this.time_stamp+this.difficulty+this.nonce+this.markle_root;
}
// 計算 Markle根
setMarkleRoot(transactions:Transaction[]){
if (transactions.length===0){
this.markle_root=Hash_SHA256("");
return
}
let tmpRoots:string[]=[];
for (let i=0;i<transactions.length;i++){
tmpRoots.push(Hash_SHA256(transactions[i].toString()));
}
while (tmpRoots.length>1){
for (let i=0; i<tmpRoots.length; i++){
if (i%2===0){
if(i+1<tmpRoots.length){
tmpRoots[i/2]=Hash_SHA256(tmpRoots[i]+tmpRoots[i+1]);
}
else{
tmpRoots[i/2]=Hash_SHA256(tmpRoots[i]);
}
}
}
if (tmpRoots.length%2===0){
tmpRoots=tmpRoots.slice(0,tmpRoots.length/2)
}
else{
tmpRoots=tmpRoots.slice(0,tmpRoots.length/2+1)
}
}
this.markle_root=tmpRoots[0];
}
}
Transaction 物件
export class Transaction{
// 交易哈希值
id:string;
// 交易發起者
from: string;
// 交易接收者
to:string;
// 交易金額
amount:number;
// 給予礦工的手續費
fees:number;
// 建構子
constructor(from:string,to:string,amount:number,fees:number) {
this.from=from;
this.to=to;
this.amount=amount;
this.fees=fees;
this.id=Hash_SHA256(this.toString());
}
// 物件轉為字串方法
toString(){
return this.to+this.from+this.amount+this.amount;
}
// 交易簽名
signature(){
const { public_key, private_key } = GenerateRSAKeyPair();
let signature=Signature(this.toString(),private_key)
return {signature,public_key}
}
// 驗證簽名
verify(signature:string,publicKey:string){
return VerifySignature(this.toString(),signature,publicKey);
}
}
檔案位置設計於 "src/utils/encrypt.ts"
從加密學應用於區塊鏈的兩大領域 討論
哈希函式
// 引入相關套件
import * as CryptoJS from 'crypto-js'
// 對於傳進來的字串進行 SHA256 的哈希處理
export function Hash_SHA256(value:string) {
return CryptoJS.SHA256(value).toString();
}
非對稱式加密
// 引入相關套件
import * as forge from 'node-forge';
// 產生 公鑰與私鑰 對
export function GenerateRSAKeyPair() {
const keys = forge.pki.rsa.generateKeyPair(2048);
const publicKeyPem = forge.pki.publicKeyToPem(keys.publicKey);
const privateKeyPem = forge.pki.privateKeyToPem(keys.privateKey);
return { public_key: publicKeyPem, private_key: privateKeyPem };
}
// 對於資料內容進行簽名
export function Signature(data: string, privateKeyPem: string): string{
try {
const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
const md = forge.md.sha256.create();
md.update(data, 'utf8');
const signature = privateKey.sign(md);
return forge.util.encode64(signature);
} catch (error) {
console.error('簽名出錯:', error);
return "Error to signature";
}
}
// 驗證簽名
export function VerifySignature(data: string, signature: string, publicKeyPem: string): boolean {
try {
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const md = forge.md.sha256.create();
md.update(data, 'utf8');
const signatureBytes = forge.util.decode64(signature);
return publicKey.verify(md.digest().getBytes(), signatureBytes);
} catch (error) {
console.error('驗證簽名錯誤:', error);
return false;
}
}
詳細影片教學: https://www.youtube.com/watch?v=1ArVvAQKVmM
如果選擇是 可以看到 隨機數 與 區塊哈希 一同不斷變化
直到 區塊哈希值 符合區塊難度
代表 區塊哈希值 前方最少多少位為0
填寫完上方資訊 按簽署 即能根據交易內容生成 交易簽名 與 公鑰
當簽署完畢時 按提交 驗證後 即能增加交易至最新區塊
備註: 可以觀察到 當交易變化 -> Markle根變化 -> 區塊哈希值變化
透過整個實作
對於區塊鏈的運行應該有更深層次的理解
儘管沒有實際去打過一次程式碼 透過網站的 Demo 應該也能理解
真心話:
坦白說 一開始在打這篇時很猶豫 要不要把code全部放上來
感覺超佔篇幅(體驗感不佳) 但分兩天有感覺又太水
所以最後我還是沒有把前端code塞進來
(畢竟前端僅是呈現的部分 與區塊鏈運行關係不大)
因此期望能夠實踐的讀者可以自行去GitHub專案上 仔細專研
最後希望閱讀完這篇文章你能夠理解
透過以上的介紹
我想對於區塊鏈的運行 已經相當透徹。
不過現在的區塊鏈還有一個 令人著迷的變化功能
透過區塊鏈特性 安全 不可竄改 公開 去中心化等特性
所延伸的功能 "智能合約"!!!