區塊鏈(Blockchain) 是一種突破性技術(Disruptive Technologies),近年漸獲關注。為什麼呢?因為區塊鏈是許多加密貨幣(Cryptocurrencies) 如比特幣(Bitcoin)、以太坊(Ethereum)、萊特幣(Litecoin) 的創始技術。那區塊鏈是如何運作的呢?在本次的教學裡,我將會談到所有關於區塊鏈技術的知識,以及如何用Swift 來製作自己的「區塊鏈」。那麼,讓我們開始吧!
區塊鏈的運作
顧名思義,區塊鏈就是一個由不同的區塊串連在一起的「鏈」,每個區塊包含三則資訊:資料(Data)、雜湊值(Hash)、和前一個區塊的雜湊值。
- 資料 ──依據使用情境,儲存在區塊的資料會因區塊鏈的型別而不同。例如,在比特幣區塊鏈中,儲存資料就是與交易有關的資訊,即是轉帳的金額、以及參與交易二人的資訊。
- 雜湊值 ──你可以把雜湊想成是一種數位指紋,它是用來識別一個區塊及其資料。雜湊最重要的地方,就是它是一個獨一無二的字母數字(Alphanumeric)程式碼,通常會由64個字元組成。當一個區塊被創造時,雜湊值也同時產生。當一個區塊被更動時,雜湊值也會同時被更動。透過這種方式,當你想要檢視區塊上的任何變動時,雜湊值就非常重要。
- 前一個區塊的雜湊值 ──每個區塊就是藉由儲存前個區塊的雜湊來鏈結在一起,組成一個區塊鏈!這就是讓區塊鏈如此安全的原因。 看看這張圖片:
如你所見,每個區塊由資料(未顯示)、雜湊值、和前一個區塊雜湊值所組成。舉個例子,黃色區塊包含了自己的雜湊值H7S6、和紅色區塊的雜湊值8SD9。以這種方式,它們組成了一個鏈結,並以此連結在一起。現在,假設有個駭客入侵併嘗試更動紅色區塊。請記住,每次一個區塊以任何方式被更動時,區塊的雜值湊也會被更動!因此,當下一個區塊執行確認、並看到前一個區塊的雜湊值並不吻合時,它會有效地將自己從「鏈」中分離出來,而不會被駭客讀取。
這就是區塊練會如此安全的原因,你幾乎不可能嘗試回溯並更改任何資料。雜湊值提供了不錯的保密及隱私,但還有兩個安全機制來讓區塊鏈更加安全:驗證(Proof-of-work)及智慧合約(Smart Contract)。雖然我不會詳述,但你可以在這裡瞭解更多。 區塊鏈最後一個保全方式就是基於它的位置。與大多儲存在伺服器或是資料庫內的資料不同,區塊鏈使用點對點網路(Peer-To-Peer, P2P),它是一種網路型態,允許任何人加入並將該網路上的資料分發給每位接收者。 每當有人加入這個網路時,他們就會得到區塊鏈的完整拷貝;每當有人建立一個新區塊時,就會傳送到網路內的所有人。然後,透過一些複雜的程式,讓節點(Node)在加入這個區塊到區塊鏈之前,先確認該區塊有否被竄改。就這樣,任何地方的任何人都可以取得這些資訊。如果你是HBO矽谷群瞎傳(Silicon Valley)的忠實粉絲,這聽起來可能有點熟悉,因為在這出美劇裡,主角就是用了類似的技術來建立一個全新網路。
因為每個人或節點都有一份區塊鏈的拷貝,他們可以有共識並確認哪些區塊是合法的。因此,如果你想要駭入一個區塊,你必須駭入網路上超過50% 的區塊來通過你的資訊。這就是為什麼區塊鏈或許是過去十年以來最安全的技術之一。
關於範例應用
現在你瞭解區塊鏈是如何運作了,那麼開始來製作我們的範例App吧!請先下載初始專案。 (https://raw.githubusercontent.com/appcoda/BlockchainDemo/master/BlockchainStarter.zip)
如你所見,我們有兩個比特幣錢包。第一個帳號Account 1065 擁有500 BTC,而第二個帳號0217 則什麼都沒有。我們利用傳送按鈕來傳送比特幣到其他帳號。為了賺取BTC,我們按下Mine 按鈕就可以獲得50 BTC 為獎勵。基本來說,我們做的是當App 執行時,利用控制檯來觀察兩個比特幣帳號間的交易情況。
你會注意到,側邊欄裡有兩個重要的類別:Block及Blockchain。開啟這些檔案,你會看到檔案是空的,那是因為我將會引導你寫出這些類別的邏輯。那我們開始吧!
在Swift 定義Block
前往Block.swift並新增程式碼以定義一個區塊。首先,讓我們瞭解一下區塊是什麼。我們先前定義了一個區塊由三個部分組成:雜湊值、記錄的實際資料、以及前一個區塊的雜湊值。當我們想建立自己的區塊鏈時,必須要知道區塊的排序,這一點可以很容易地在Swift中定義。新增以下程式碼到類別裡:
var hash: String!
var data: String!
var previousHash: String!
var index: Int!
複製程式碼
現在,我們需要新增最後一個重要的程式碼。我之前提到每次一個區塊被更動,雜湊就會改變;這就是區塊鏈如此安全的特色之一。所以我們必須建立一個函式來產生一個隨機字母數字的雜湊。這個函式只需要幾行程式碼:
func generateHash() -> String {
return NSUUID().uuidString.replacingOccurrences(of: "-", with: "")
}
複製程式碼
NSUUID是一個物件,代表橋接UUID的通用唯一值。它內建於Swift中,而且非常適合用來產生32字元的字串。這個函式產生一個UUID,消除所有連字元(-),然後回傳String,即區塊的雜湊。Block.swift現在應該看起來像這樣:
我們已經定義了Block類別,接著讓我們定義Blockchain類別吧。切換到Blockchain.swift。
在Swift 定義Blockchain
如前文所說,讓我們嘗試從基本面來了解區塊鏈。在基本的術語裡,區塊鏈只是區塊串在一起組成的鏈;換句話說,它是一個包含所有專案的列表。聽起來是不是有點熟悉呢?因為這就是陣列的定義,而這個陣列就是由區塊所組成!讓我們把下列的程式碼加進去吧:
var chain = [Block]()
複製程式碼
小提示:這幾乎可以應用在電腦科學世界的所有事上。如果你曾遇過大問題,試著將它拆解成小元件,然後以自己的方法來解決問題;就像我們弄清楚如何在Swift中加入區塊及區塊鏈一樣!
你會注意到在陣列裡面包含了先前定義的Block類別,那是我們在區塊鏈中需要的所有變數。加入兩個函式到類別裡,我們就完成了。試著用我前文所教的來回答這個問題:
在一個區塊鏈中,兩個主要的函式是什麼?
希望你能夠回答這個問題!這兩個區塊鏈擁有的主要函式,是用來建立初始區塊,以及在後面新增新的區塊。當然,現在我不會下放這個鏈並加入智慧合約,但是這些是基本函式!加入以下程式碼到Blockchain.swift:
func createGenesisBlock(data:String) {
let genesisBlock = Block()
genesisBlock.hash = genesisBlock.generateHash()
genesisBlock.data = data
genesisBlock.previousHash = "0000"
genesisBlock.index = 0
chain.append(genesisBlock)
}
func createBlock(data:String) {
let newBlock = Block()
newBlock.hash = newBlock.generateHash()
newBlock.data = data
newBlock.previousHash = chain[chain.count-1].hash
newBlock.index = chain.count
chain.append(newBlock)
}
複製程式碼
- 我們加入的第一個函式是用來建立初始區塊。為此,我們建立了一個函式來把區塊的資料作為Input。然後,我們定義一個名為genesisBlock的變數,並將它設為Block型別。因為它是Block型別,所以它有我們之前在Block.swift定義的所有變數及函式。我們設定generateHash()為雜湊、 Input data為資料。因為這是第一個區塊,所以我們將前一個區塊的雜湊設定為000,好讓我們知道這是初始區塊。我們將它的索引值設為0 ,然後放到區塊鏈chain。
- 我們建立的下一個函式則適用於所有genesisBlock後的區塊,而它會建立剩下的所有區塊。你會注意到它跟之前的函式非常相似,唯一的不同的是我們將previousHash設定為前一個區塊的雜湊,並將index設為它在區塊練的位置。 完成了!我們已經成功定義自己的Blockchain!你的程式碼應該如下圖所示!
接著,我們將所有的部分連線到ViewController.swift檔案,並看看執行成果吧!
錢包後臺(Wallet Backend)
切換到ViewController.swift,我們可以看到所有的UI元件都已經連結完畢。我們所需要做的就是處理交易,並將交易列印到控制檯上。 然而在開始之前,我們應該稍微探討一下比特幣區塊鏈。比特幣來自一個總帳號,假設這個帳號的編號是000。當你挖掘一顆比特幣時,就表示你解答了數學問題,並獲得一定數量的比特幣作為獎勵。這是發行貨幣一個很聰明的方法,同時也創造了讓更多人去挖掘的動機。在我們的App 裡,我們將100 BTC 作為獎勵。首先,讓我們在ViewController 新增需要的變數:
let firstAccount = 1065
let secondAccount = 0217
let bitcoinChain = Blockchain()
let reward = 100
var accounts: [String: Int] = ["0000": 10000000]
let invalidAlert = UIAlertController(title: "Invalid Transaction", message: "Please check the details of your transaction as we were unable to process this.", preferredStyle: .alert)
複製程式碼
我們定義兩個帳號:一個編號為1065,另一個編號為0217。我們同時新增一個bitcoinChain變數來作為我們的區塊鏈,並將reward設定為100。我們需要一個作為比特幣來源的主帳號:這是我們的初始帳號,編號為0000,它擁有一千萬個比特幣。你可以把這個帳號當成銀行,在每一次的獎勵中,就會從中取出100個比特幣,並轉至合法的帳號裡。我們也定義一個警告,在每次交易無法完成時顯示。
現在,讓我們來寫些將會執行的泛用函式。你可以猜到這些函式是什麼嗎?
- 第一個函式是用來處理交易的。我們要確認傳送者及接收者的帳號中,接收或扣除的金額是正確的,而且這個資訊會被記錄在我們的區塊鏈上。
- 下一個函式是要在控制檯裡印出完整的紀錄,它會顯示每個區塊及每個區塊內的資料。
- 最後一個函式是用來驗證區塊鏈是否合法,方法為確認前個區塊的雜湊是否符合下一個區塊的資訊。因為我們不會示範任何駭客方法,所以範例中的鏈永遠都是合法的。
Transaction 函式
以下是我們的泛用交易函式。在定義變數之下輸入以下程式碼:
func transaction(from: String, to: String, amount: Int, type: String) {
// 1
if accounts[from] == nil {
self.present(invalidAlert, animated: true, completion: nil)
return
} else if accounts[from]!-amount < 0 {
self.present(invalidAlert, animated: true, completion: nil)
return
} else {
accounts.updateValue(accounts[from]!-amount, forKey: from)
}
// 2
if accounts[to] == nil {
accounts.updateValue(amount, forKey: to)
} else {
accounts.updateValue(accounts[to]!+amount, forKey: to)
}
// 3
if type == "genesis" {
bitcoinChain.createGenesisBlock(data: "From: \(from); To: \(to); Amount: \(amount)BTC")
} else if type == "normal" {
bitcoinChain.createBlock(data: "From: \(from); To: \(to); Amount: \(amount)BTC")
}
}
複製程式碼
看起來程式碼很多,但是它的核心只是為每次的交易定義一些規則。在開頭的地方,我們有四個引數:to、from、amount以及type。To、From、及Amount的含義一目瞭然,而Type基本上就是定義交易的型別。這裡有兩種Type:Normal和Genesis。一個Normal的交易型別會是在帳號1065與0217之間進行,而Genesis交易型別則會涉及到帳號0000。
- 第一個if-else條件式是關於來源帳號。如果來源帳號不存在或金額不足,我們會顯示交易無效的警告,然後結束函式。而如果通過的話,我們會更新數值。
-
第二個if-else條件式是關於接收帳號。如果接收帳號不存在,那麼我們隨它而去,然後結束函式。要不然,我們就會傳送正確的比特幣數量到帳號。 複製程式碼
- 第三個if-else條件式處理交易的型別。如果一個交易涉及初始區塊,我們就建立一個新的初始區塊;反之我們建立一個新區塊來儲存資料。
Printing 函式
在每次交易的最後,我們想要看到一個清單列出所有交易,來確保我們知道所有發生的事情。以下是我們在transaction函式下輸入的程式碼:
func chainState() {
for i in 0...bitcoinChain.chain.count-1 {
print("\tBlock: \(bitcoinChain.chain[i].index!)\n\tHash: \(bitcoinChain.chain[i].hash!)\n\tPreviousHash: \(bitcoinChain.chain[i].previousHash!)\n\tData: \(bitcoinChain.chain[i].data!)")
}
redLabel.text = "Balance: \(accounts[String(describing: firstAccount)]!) BTC"
blueLabel.text = "Balance: \(accounts[String(describing: secondAccount)]!) BTC"
print(accounts)
print(chainValidity())
}
複製程式碼
這是一個簡單的for迴圈,包含bitcoinChain的每個區塊。我們印出區塊的編號、雜湊值、前個區塊的雜湊、以及儲存的資料,再更新UILabel來顯示每個帳號內正確的BTC數目。最後,印出一個列出每個帳號的清單(應該會有三個),並驗證鏈的合法性。 現在,你應該會在函式最後一行中發生錯誤。這是因為我們還沒定義chainValidity()函式,那麼就來開始吧!
Validity 函式
記住,如果前一個區塊的雜湊值符合目前區塊所描述的內容,那麼這一個鏈就是合法的。我們可以輕易地用另一個for 迴圈來重複驗證每個區塊。
func chainValidity() -> String {
var isChainValid = true
for i in 1...bitcoinChain.chain.count-1 {
if bitcoinChain.chain[i].previousHash != bitcoinChain.chain[i-1].hash {
isChainValid = false
}
}
return "Chain is valid: \(isChainValid)\n"
}
複製程式碼
跟之前有點相似,我們在bitcoinChain中重複驗證每個區塊,來確認前一個區塊的雜湊值是否與目前區塊所描述的內容符合。 這樣就完成了!我們已經定義了函式,並將會每次都用到它們!你的ViewController.swift現在應該看起來像這樣:
現在我們只需要將按鈕連線到函式就完成了,來開始最後的篇章吧!
將所有東西連結在一起
當我們的App首次啟動時,我們想讓初始帳號0000傳送50 BTC到我們的第一個帳號。然後,我們將讓第一個帳號傳送10 BTC到第二個帳號。這個步驟僅需三行程式碼就可以完成。如此更改你的viewDidLoad函式:
override func viewDidLoad() {
super.viewDidLoad()
transaction(from: "0000", to: "\(firstAccount)", amount: 50, type: "genesis")
transaction(from: "\(firstAccount)", to: "\(secondAccount)", amount: 10, type: "normal")
chainState()
self.invalidAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
}
複製程式碼
我們使用先前定義好的函式,並在最後呼叫chainState()。同時,我們新增一個OK按鈕到交易無效的警告中。 現在讓我們來看看剩下的四個函式裡要加入什麼:redMine()、blueMine()、redSend()及blueSend()。
挖礦函式
挖礦函式非常地簡單,只要三行程式碼就行了。這就是我們要新增的程式碼:
@IBAction func redMine(_ sender: Any) {
transaction(from: "0000", to: "\(firstAccount)", amount: 100, type: "normal")
print("New block mined by: \(firstAccount)")
chainState()
}
@IBAction func blueMine(_ sender: Any) {
transaction(from: "0000", to: "\(secondAccount)", amount: 100, type: "normal")
print("New block mined by: \(secondAccount)")
chainState()
}
複製程式碼
在第一個挖礦函式中,我們使用transaction函式從初始帳號傳送100 BTC到第一個帳號,先印出一個區塊被挖出,再印出chainState。同樣地,我們在blueMine函式裡將100 BTC傳送到第二個帳號。
傳送函式
傳送函式與先前的函式也稍微相似。
@IBAction func redSend(_ sender: Any) {
if redAmount.text == "" {
present(invalidAlert, animated: true, completion: nil)
} else {
transaction(from: "\(firstAccount)", to: "\(secondAccount)", amount: Int(redAmount.text!)!, type: "normal")
print("\(redAmount.text!) BTC sent from \(firstAccount) to \(secondAccount)")
chainState()
redAmount.text = ""
}
}
@IBAction func blueSend(_ sender: Any) {
if blueAmount.text == "" {
present(invalidAlert, animated: true, completion: nil)
} else {
transaction(from: "\(secondAccount)", to: "\(firstAccount)", amount: Int(blueAmount.text!)!, type: "normal")
print("\(blueAmount.text!) BTC sent from \(secondAccount) to \(firstAccount)")
chainState()
blueAmount.text = ""
}
}
複製程式碼
首先,我們確認redAmount或blueAmount中的文字欄位是否為空值。如果是,我們會顯示一個交易無效的警告。如果不是,我們就可以繼續。我們使用transaction函式輸入金額,並把交易設為normal型態,以將第一個帳號的金額傳送到第二個帳號(或相反)。我們印出被傳送的金額,然後呼叫chainState()函式。最後,把文字欄位清空。 這樣我們就完成囉!確認一下你的程式碼是否符合下圖所示。
執行App 試試看!從前端來說,它看起來就如一個普通的交易App,但你會知道它後臺的運作。試試使用App 將BTC 從一個帳號交易給另一個帳號、並試著欺騙App 吧!
結論
在這次的教學中,你學到了如何使用Swift 來建立一個區塊鏈,並建立自己的比特幣交易。請注意在真實的加密貨幣後臺裡,實作部分是跟上文是完全不一樣的東西,因為它需要藉由智慧合約來分散,但是上面的示範內容用來學習的。 在這個範例中,我們運用了比特幣來當加密貨幣,但你能想到區塊鏈還的其他用途嗎?歡迎在下面留言分享你的看法!希望你在此學到新的東西! 你可以在Github下載完整專案作參考。
完整專案:https://github.com/appcoda/BlockchainDemo
原文連結:https://www.appcoda.com/blockchain-introduction/
轉載自 AppCoda,一家專注 Swift 的高質量教學分享平臺。