
最近跑到幣圈玩樂,完全打開了新世界,一切都太新鮮、太有趣了(除了開空單爆倉好多次之外)!
也讓我開始好奇,區塊鏈背後的運作原理到底是什麼?常常聽到的「去中心化」又是什麼?所以手癢用 Swift 來動手實作,順便把 input 記錄下來,好好整理自己的思緒,所以這也算是我的個人筆記!歡迎各位糾正、討論。
以下內容以 Swift + Vapor 實作 API Server、SwiftUI 實作 Demo App,歡迎 clone 完整專案服用:
區塊鏈 Server:https://github.com/xiaohsun/Swift-Blockchain
區塊鏈 App:https://github.com/xiaohsun/Blockchain_Demo_App
使用方式:先用 Command line 將 Server 專案 run 起來、確定能連線到 8080 之後,就可以打開 App 專案互動了。
詳情請見 Repo 的 README,有需要協助也歡迎詢問!
部分實作參考了 Daniel van Flymen 超讚的文章,歡迎懂 Python 的各位前去朝聖。

區塊鏈是怎麼運作的?
區塊鏈是按順序排列的 Blocks(區塊),是由 Hash Value(雜湊值)所串接起來。Hash Value 就像「數位指紋」一樣,任何種類的資料,都能產生一串獨一無二、長度固定的數值。
/// 計算 Block 的 Hash Value
static func hash(block: Block) -> String {
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
guard let blockData = try? encoder.encode(block) else {
return ""
}
let hash = SHA256.hash(data: blockData)
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
一個 Block 裡面有什麼呢?
根據不同鏈有所不同,而大多數鏈上的 Block 包含了:
- Index(序號)
- Proof(工作量證明)
- Timestamp(時間戳)
- Transactions(交易)
- PreviousHash(前一個 Block 的 Hash Value)
先從 PreviousHash 說起,PreviousHash 代表每一個 Block 都記著前一個 Block 的數位指紋。
想想看,若有人竄改某個 Block 的數值,會導致該 Block 的 Hash Value 改變,那下一個 Block 紀錄的 PreviousHash 就會對不上,導致後續一連串 Block 全部失效。
因此,各種竄改都很容易被發現,這是區塊鏈信任的基石,也是形成「去中心化」的要點之一。
當然,鏈上的第一個 Block 不會有 PreviousHash,被稱為 Block 0(創世區塊)。
/// 創建新 Block 並加入鏈的尾巴
func newBlock(proof: Int, previousHash: String? = nil) -> Block {
let block = Block(
index: chain.count + 1,
timestamp: Date().timeIntervalSince1970,
transactions: currentTransactions,
proof: proof,
previousHash: previousHash ?? Blockchain.hash(block: chain.last!)
)
// 清空 Transactions 等待區
currentTransactions = []
// 將新 Block 接在鏈的尾巴
chain.append(block)
return block
}
init() {
// 創建第一個 Block
_ = newBlock(proof: 100, previousHash: "1")
}
Transactions 是買賣的交易紀錄,交易產生時,會被集中到「等待區」。等下一個 Block 產生時,蓋上當下的 Timestamp,與 PreviousHash 一同被打包進 Block ,並算出 Hash Value,將 Block 接到鏈的尾巴。
/// 創建新交易並添加到等待區
func newTransaction(sender: String, recipient: String, amount: Int) -> Int {
let transaction = Transaction(
sender: sender,
recipient: recipient,
amount: amount
)
currentTransactions.append(transaction)
// 欲打包此交易的區塊 index(下一個要被挖掘的)
return lastBlock.index + 1
}
那新的 block 是怎麼被挖掘出來的?
這就要講到 PoW( Proof of Work,工作量證明),PoW 能夠確保一個 Block 的產生是需要付出相應的努力,我們可以把它想像成一個數學考題:
Q: 假設我有一個數字 10,10 + y = 100,試問 y 為何?
若用區塊鏈的角度來出題,則是:
Q: 假設我有一個數字 10,10y 的 Hash Value 為 0000 開頭,試問 y 為何?
如果成功得出 y,就能夠建立 Block,這個過程就是俗稱的「挖礦」,礦工也會因此得到獎勵(代幣)。
/// 計算 PoW
func proofOfWork(lastProof: Int) -> Int {
var proof = 0
while !Blockchain.validProof(lastProof: lastProof, proof: proof) {
proof += 1
}
return proof
}
/// 檢查 PoW 是否正確
static func validProof(lastProof: Int, proof: Int) -> Bool {
let guess = "\(lastProof)\(proof)".data(using: .utf8)!
let hash = SHA256.hash(data: guess)
let hashString = hash.compactMap { String(format: "%02x", $0) }.joined()
// 檢查 Hash Value 是否以 4 個 0 開頭
return hashString.prefix(4) == "0000"
}
然而,算出 PoW 的過程許多的運算(如上方的 Loop )雖然能確保 Block 得來不易,卻也消耗許多時間與能源。
因此,也有鏈採用不同的驗證方式,如 PoS( Proof of Stake,權益證明)。
但,PoW 的解答可能不只一個
在成千上萬個電腦同時在解謎時,可能會找到不同的解答,因而建立不同的 Block,產生了分歧。
這時候就要依靠區塊鏈的「最長鏈共識」來解決。
最長鏈共識
在區塊鏈中,每個參與的電腦(個體)都要註冊節點,讓網路知道你的存在。我們可以透過訪問別人的節點,下載別人的鏈來檢查是否比自己的鏈最長、最有效(所有 PoW 都符合規範)。
若成功驗證,很不幸地,我們要拋棄自己的鏈,去採用上述那條鏈。
因此,就算區塊鏈一度出現分歧,透過最長鏈共識,最終還是會慢慢同步到那條最長的鏈上。這就是「去中心化」網路獲得共識的方式,不需要中心化的單位做裁決。
(口語雖常說「最長鏈」,但實際規則是累積工作量 (total work) 最大的鏈才被採納,不一定是最長。)
/// 註冊新節點
/// - Parameter address: 節點地址,例如 "http://192.168.0.5:5000"
func registerNode(address: String) {
if let url = URL(string: address), let host = url.host {
let node = url.port != nil ? "\(host):\(url.port!)" : host
nodes.insert(node)
}
}
/// 解決節點間的衝突
/// 若自己的鏈被替換,返回 true
func resolveConflicts() async throws -> Bool {
var newChain: [Block]? = nil
var maxLength = chain.count
// 獲取並驗證網路中所有節點的鏈
for node in nodes {
let urlString = "http://\(node)/chain"
guard let url = URL(string: urlString) else { continue }
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
continue
}
let decoder = JSONDecoder()
let chainResponse = try decoder.decode(ChainResponse.self, from: data)
// 檢查鏈的長度是否更長且有效
if chainResponse.length > maxLength && Blockchain.validChain(chain: chainResponse.chain) {
maxLength = chainResponse.length
newChain = chainResponse.chain
}
}
// 如果找到一個更長且有效的鏈,則替換自己的鏈
if let newChain = newChain {
chain = newChain
return true
}
return false
}
以上~是目前研究到的,之後再跟大家分享更多!
區塊鏈看似很神秘,但搞懂後發現,透過數字的環環相扣,居然真的能夠解決部分傳統金融機構中心化的痛點,不得不佩服第一個想出這個點子的大神。
身為幣圈的菜鳥,我非常期待未來區塊鏈的發展會如何改變人們的生活,讓我們繼續看下去吧!