使用 Swift 和 Vapor 構建區塊鏈伺服器

2021-09-11 11:43:04 字數 4338 閱讀 6297

本文參考

第一步是為區塊鏈 web api 建立必要的模型,如下所示。

block:block(區塊)類表示乙個區塊,包含交易的輸入和輸出。

class block: codable 

}func addtransaction(transaction: transaction)

init

() init(transaction: transaction)

}複製**

block 類的屬性解釋如下:

transaction:transaction(交易)由 sender(傳送者)、recipient(接收者)和被轉移的 amount(金額)組成。實現如下:

class transaction: codable 

init?(request: request)

self.from = from

self.to = to

self.amount = amount

}}複製**

transaction類的實現很直觀。由 from、to 和 amount 字段組成。為了簡單起見,from 和 to 欄位會用虛擬名字來表示,在實際中這兩個欄位還會包含包(wallet)id 。

blockchain:blockchain(區塊鏈)類是表示區塊列表的主類。每個區塊都指向鏈中的前乙個區塊。每個區塊可以包含多筆交易,表示信貸或借記。

class blockchain: codable 

init(_ genesisblock: block)

func addblock(_ block: block) else

block.hash = generatehash(for: block)

block.message = "此區塊已新增至區塊鏈"

}private func getpreviousblock() -> block

private func displayblock(_ block: block)

private func generatehash(for block: block) -> string

return

hash

}}複製**

有幾種不同方式來用 vapor 實現 web api 。我在這裡會建立乙個自定義的控制器來處理所有區塊鏈請求,這樣就不用把所有**都塞進 routes 類裡了。blockchaincontroller實現如下:

class blockchaincontroller 

private func setuproutes

() // 新增新交易

self.drop.post("transaction")

return try jsonencoder().encode(["message": "發生異常!"])

}// 獲得鏈

self.drop.get("blockchain")

return try! jsonencoder().encode(["message":"區塊鏈尚未初始化。請先挖礦"])}}

}複製**

web api 從三個基本的 endpoint 開始。

blockchaincontroller使用blockchainservice來執行所需操作。blockchainservice 的實現如下:

import foundation

import vapor

class blockchainservice

func addblock(_ block: block)

func getlastblock() -> block

func getblockchain() -> blockchain?

}複製**

下面我們就來檢查一下 web api 的 endpoint。啟動 vapor 伺服器然後傳送請求到 「mine」 endpoint。

工作量證明演算法生成了以「000」開頭的雜湊值。區塊被挖出後就立即轉換為 json 格式返回回來。通過 swift 4.0 的 codable 協議實現。

現在給區塊鏈新增一筆簡單的交易,從張嘉夫那裡轉移10美元給馬雲。

最後一步是檢查區塊鏈是否含有新新增的區塊。訪問 「blockchain」 endpoint 來檢視完整的鏈。

完美!我們的區塊鏈 web api 現在可以正常工作了。

還有一點遺憾的是,區塊鏈應該是去中心化的,但目前我們沒有新增新節點的機制。在下一節我們會更新區塊鏈實現以便讓其支援多個節點。

在給區塊鏈新增節點之前,首先要定義節點。節點模型的實現如下:

class blockchainnode :codable 

init?(request :request)

self.address = address

}}複製**

blockchainnode類很簡單,只有乙個address屬性,用於標識節點伺服器的 url。然後更新 blockchaincontroller 來新增註冊新節點功能。如下所示:

self.drop.get("nodes") 

self.drop.post("nodes/register")

self.blockchainservice.registernode(blockchainnode)

return try jsonencoder().encode(blockchainnode)

}複製**

還要更新 blockchainservice 以便註冊新節點。

func getnodes() -> [blockchainnode] 

func registernode(_ blockchainnode: blockchainnode)

複製**

下面來測試一下。啟動新的 vapor 伺服器然後試著註冊新節點。

節點註冊好後,可以使用 nodes endpoint 來獲取它,如下所示:

現在可以註冊新節點了,下面要著重解決(resolve)節點間的衝突。如果某個節點上的區塊鏈比其它節點的要大,就會產生衝突。在這種情況下,一般都是獲得臨近節點並用較大的區塊鏈更新它們。

為了建立衝突,我們需要第二台伺服器或是在另乙個埠上執行伺服器。本文會用後一種方法,在另乙個埠上啟動 vapor 伺服器。這兩個節點初始化後,各建立一些區塊和交易,這些區塊會被新增到各自的區塊鏈上。最後,呼叫 resolve endpoint 來解決節點間的衝突,並將節點更新為較大的那個區塊鏈。

給 blockchaincontroller 新增新的 endpoint 來解決衝突。

self.drop.get("nodes/resolve") }}

複製**

上面使用了 vapor 框架的 async response 功能來非同步處理響應。然後再更新 blockchainservice 來解決衝突。實現如下所示:

func resolve(completion: @escaping(blockchain) -> ())  else 

}}).resume()}}

複製**

resolve函式遍歷節點列表並獲取每個節點的區塊鏈。如果某個區塊鏈比當前區塊鏈要大,則替換當前區塊鏈為更大的那個,否則直接返回當前區塊鏈,因為當前區塊鏈已經是更大的區塊鏈了。

為了測試我們要在不同的埠開啟兩台伺服器,在8080埠上新增三筆交易,在8081上新增兩筆。可以在終端裡輸入下面的命令來啟動 vapor 伺服器。

vapor run serve -—port=8081

複製**

在 8080 埠上新增三筆交易,如下所示:

然後在 8081 埠節點上新增兩筆交易,如下所示:

最後,來一下測試 resolve endpoint。在 postman 裡訪問 「resolve」 endpoint,如下所示:

可以看到,resolve endpoint 返回了更大的區塊鏈,同時也更新了節點的區塊鏈。這樣解決衝突方案就完工了。

[github]

Swift 和?的使用

swift語言使用var定義變數,但是和別的語言不同,swift不會自動給變數賦初始值,也就是申明的變數不會有預設值,所以要求在使用之前不要對其初始化。如果在變數使用之前木有初始化就會報錯 var mystring string print mystring program ended with e...

優化Swift的構建時間

原文 regarding swift build time optimizations上週我拜讀了nickoneill的佳作 加速swift的構建 之後便不由稍微換了個角度來看待swift的 目前有乙個新問題出現 是否該將一行可以算是簡潔的 重構為9行 以便符合編譯器的需求?對於 來說,簡潔與編譯器...

swift學習之 函式 構建函式 kvc構建函式

一 函式格式及帶參函式 函式定義格式,函式名 形參列表 返回值 func sum x int,y int int 外部引數,在形參前加乙個名字,外部引數不會影響函式,外部引數讓函式看起來更直觀 外部引數使用 呼叫函式的時候會忽略形參名字 func sum1 number1 x int,number2...