|
|
51CTO旗下网站
|
|
移动端

构建第一个 Swift 区块链应用

区块链作为一项革命性的技术,开始受到越来越多追捧。为什么呢?因为区块链是许多加密数字货币的底层技术,比如:比特币(BTC),以太坊(ETH)以及莱特币(LTC)。区块链具体是如何工作的?本篇教程会涵盖所有区块链相关的知识,还会教你如何构建 Swift 区块链。下面让我们开始吧!

作者:小袋子译来源:Swift翻译组|2018-08-22 17:32

区块链作为一项革命性的技术,开始受到越来越多追捧。为什么呢?因为区块链是许多加密数字货币的底层技术,比如:比特币(BTC),以太坊(ETH)以及莱特币(LTC)。区块链具体是如何工作的?本篇教程会涵盖所有区块链相关的知识,还会教你如何构建 Swift 区块链。下面让我们开始吧!

区块链的工作原理

顾名思义,区块链是一条由不同区块连接组成的链。每一个块包含三个信息:数据、哈希(hash)、以及前置区块的哈希。

  1. 数据 – 由于应用场景不同,存储在区块中的数据由区块链的类型决定。例如,在比特币区块链中,存储的数据是交易信息:转账金额和交易双方的信息。
  2. 哈希 – 你可以将哈希看做数字指纹,用来唯一标识一个区块及其数据。哈希的重要之处在于它是一个独特的字母数字代码,通常是 64 个字符。当一个区块被创建时,哈希也随之创建。当一个区块被修改,哈希也随之修改。因此,当你想要查看在区块上所做的任何变更时,哈希就显得非常重要。
  3. 前置区块的哈希 – 通过存储前置区块的哈希,你可以还原每个区块连接成区块链的过程!这使得区块链安全性特别高。

我们来看下这张图片:

区块链
区块链

你可以看到,每一个区块包含数据(图片中没有指明)、哈希以及前置区块的哈希。例如,黄色区块包含自身的哈希:H7s6,以及红色区块的哈希:8SD9。这样它们就构成了一条相互连接的链。现在,假如有一个黑客准备恶意篡改红色的区块。请记住,每当块以任何方式被篡改时,该区块的哈希都会改变!当下一个区块检查并发现前置哈希不一致时,黑客将无法访问它,因为他与前置区块的联系被切断了(译者注:即如果黑客想要要篡改一个区块的话,就需要把这个区块后面的所有区块都要改掉,而这个工作量是很难实现的)。

这使得区块链特别安全,几乎不可能回滚或者篡改任何数据。虽然哈希为保密和隐私提供了巨大的保障,但是还有两个更加安全妥当的措施让区块链更加安全:工作量证明(Proof-of-Work)以及智能合约(Smart Contracts)。本文我不会深入讲解,你可以在这里了解注册即领取38元体验金相关知识。

区块链最后一个保证自身安全性的方式是基于其定位。和大多数存储在服务器和数据库的数据不同,区块链使用的是点对点(P2P)网络。P2P 是一种允许任何人加入的网络,并且该网络上的数据会分发给每一个接收者。

每当有人加入这个网络,他们就会获得一份区块链的完整拷贝。每当有人新建一个区块,就会广播给全网。在将该块添加到链之前,节点会通过几个复杂的程序确定该块是否被篡改。这样,所有人、所有地方都可以使用这个信息。如果你是 HBO 美剧硅谷 的粉丝,对此应该不会感到陌生。在该剧中,主演(Richard)使用一种相似的技术创造了新型互联网(译者注:有趣的是剧中还发行了区块链数字货币 PiedPaperCoin,感兴趣的童鞋可以刷一下这部剧)。

因为每个人都有区块链或者节点的一份拷贝,他们可以达成一种共识并决定哪部分区块是有效的。因此,如果你想要攻击某个区块,你必须同时攻击网络上 50% 以上的区块(译者:51% 攻击),使得你的区块可以追上并替换原区块链。所以区块链或许是过去十年所创造的最安全的技术之一。

关于示例程序

现在你已经对区块链的原理有了初步的认识,那么我们就开始写示例程序吧!你可以在这里下载原始项目。

如你所见,我们有两个比特币钱包。第一个账户 1065 有 500 BTC,而第二个账户 0217 没有 BTC。我们通过 send 按钮可以发送比特币到另外的账户。为了赚取 BTC,我们可以点击 Mine 按钮,可以获得 50 BTC 的奖励。我们主要工作是查看控制台输出,观察两个账户间的交易过程。

这里写图片描述
这里写图片描述

在左侧导航栏可以看到两个很重要的类:Block 和 Blockchain。目前这两个类都是空实现,我会带着你们在这两个类中写入相关逻辑。下面让我们开始吧!

这里写图片描述
这里写图片描述

在 Swift 中定义区块

首先打开 Block.swift 并添加定义区块的代码。在此之前,我们需要定义区块是什么。前面我们曾定义过,区块是由三部分组成:哈希、实际记录的数据以及前置区块的哈希。当我们想要构建我们的区块链时,我们必须知道该区块是第一个还是第二个。我们可以很容易地在 Swift 的类中做如下定义:

  1. var hash: String! 
  2. var data: String! 
  3. var previousHash: String! 
  4. var indexInt

现在需要添加最重要的代码。我曾提过区块在被修改的情况下,哈希也会随之变化,这是区块链如此安全的特性之一。因此我们需要创建一个函数去生成哈希,该哈希由随机字母和数字组成。这个函数只需要几行代码:

  1. func generateHash() -> String { 
  2.     return NSUUID().uuidString.replacingOccurrences(of"-"with""

NSUUID 是一个代表通用唯一值的对象,并且可以桥接成 UUID。它可以快速地生成 32 个字符串。本函数生成一个 UUID,删除其中的连接符,然后返回一个 String,最后将结果作为区块的哈希。Block.swift 现在就像下面:

这里写图片描述
这里写图片描述

现在我们已经定义好了 Block 类,下面来定义 Blockchain 类,首先切换到 Blockchain.swift 。

在 Swift 中定义区块链

和之前一样,首先分析区块链的基本原理。用非常基础的术语来说,区块链只是由一连串的区块连接而成,也可以说是一个由多个条目组成的列表。这是不是听起来很熟悉呢?其实这就是数组的定义!而且这个数组是由区块组成的!接下来添加以下代码:

  1. var chain = [Block]() 

快速提示: 这个方法可以应用于计算机科学世界里的任何事物。如果你遇到大难题,可以尝试把它分解成若干个小问题,以此来建立起解决问题的方法,正如我们解决在 Swift 中如何添加区块和区块链的问题。

你会注意到数组里面是我们前面定义的 Block 类,这就是区块链所需要的所有变量。为了完成功能,我们还需要在类中添加两个函数。请尝试着根据我之前教过的方法解答这个问题。

区块链中的两个主要函数是什么?

我希望你能认真思考并回答这个问题!实际上,区块链的两个主要功能是创建创世区块(最初的始块),以及在其结尾添加新的区块。当然现在我不打算实现分叉区块链和添加智能合约的功能,但你必须了解这两个是基本功能!将以下代码添加到 Blockchain.swift:

  1. func createGenesisBlock(data:String) { 
  2.     let genesisBlock = Block() 
  3.     genesisBlock.hash = genesisBlock.generateHash() 
  4.     genesisBlock.data = data 
  5.     genesisBlock.previousHash = "0000" 
  6.     genesisBlock.index = 0 
  7.     chain.append(genesisBlock) 
  8.  
  9. func createBlock(data:String) { 
  10.     let newBlock = Block() 
  11.     newBlock.hash = newBlock.generateHash() 
  12.     newBlock.data = data 
  13.     newBlock.previousHash = chain[chain.count-1].hash 
  14.     newBlock.index = chain.count 
  15.     chain.append(newBlock) 

1、我们添加的第一个函数的作用是创建创世区块。为此,我们创建了一个以区块数据为入参的函数。然后定义了一个类型为 Block 的变量 genesisBlock,它拥有此前在 Block.swift 中定义的所有变量和函数。我们将 generateHash() 赋值给哈希,将输入的 data 参数赋值给数据。由于这是第一个区块,我们将前置区块的哈希设为 0000,这样我们就可以知道这是起始区块。最后我们将 index 设为 0,并将这个区块加入到区块链中。

2、我们创建的第二个函数适用于 genesisBlock 之后的所有区块,并且能创建剩余的区块。你会注意到它与第一个函数非常相似。唯一的区别是,我们将 previousHash 的值设置为前一个区块的哈希值,并将 index 设置为它在区块链中的位置。就这样,区块链已经定义好了!你的代码应该看起来跟下图一样!

这里写图片描述
这里写图片描述

钱包后端

现在切换到 ViewController.swift,我们会发现所有的 outlet 都已经连接好了。我们只需要处理交易,并将其输出到控制台。

然而在此之前,我们需要稍微研究一下比特币的区块链。比特币是由一个总账户产生的,我们将这个账号的编号称为 0000。当你挖到一个 BTC,意味着你解决了数学问题,因此会发行一定数量的比特币作为奖励。这提供了一个发币的高明方法,并且可以激励注册即领取38元体验金人去挖矿。在我们的应用,让我们把挖矿奖励设为 100 BTC。首先,在视图控制器中添加所需的变量:

  1. let firstAccount = 1065 
  2. let secondAccount = 0217 
  3. let bitcoinChain = Blockchain() 
  4. let reward = 100 
  5. var accounts: [String: Int] = ["0000": 10000000] 
  6. 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。里面有 1000 万个比特币。你可以把这个账户想象成一个银行,所有因奖励产生的 100 个比特币都经此发放到合法账户中。我们还定义了一个提醒弹窗,每当交易无法完成时就会弹出。

现在,让我们来编写几个运行时需要的通用函数。你能猜出是什么函数吗?

  1. 第一个函数是用来处理交易的。我们需要确保交易双方的账户,能够接收或扣除正确的金额,并将这些信息记录到我们的区块链中。
  2. 下一个函数是在控制台中打印整个记录 —— 它将显示每个区块及其中的数据。
  3. 最后一个是用于验证区块链是否有效的函数,通过校验下一个区块的 previousHash 和上一个区块 hash 是否匹配。由于我们不会演示任何黑客方法,因此在我们的示例程序中,区块链是永远有效的。

交易函数

下面是一个通用的交易函数,请在我们定义的变量下方输入以下代码:

  1. func transaction(from: String, to: String, amount: Int, type: String) { 
  2.     // 1 
  3.     if accounts[from] == nil { 
  4.         self.present(invalidAlert, animated: true, completion: nil) 
  5.         return 
  6.     } else if accounts[from]!-amount < 0 { 
  7.         self.present(invalidAlert, animated: true, completion: nil) 
  8.         return 
  9.     } else { 
  10.         accounts.updateValue(accounts[from]!-amount, forKey: from
  11.     } 
  12.      
  13.     // 2 
  14.     if accounts[to] == nil { 
  15.         accounts.updateValue(amount, forKey: to
  16.     } else { 
  17.         accounts.updateValue(accounts[to]!+amount, forKey: to
  18.     } 
  19.      
  20.     // 3 
  21.     if type == "genesis" { 
  22.         bitcoinChain.createGenesisBlock(data: "From: \(from); To: \(to); Amount: \(amount)BTC"
  23.     } else if type == "normal" { 
  24.         bitcoinChain.createBlock(data: "From: \(from); To: \(to); Amount: \(amount)BTC"
  25.     } 

代码量看起来好像很大,但主要是定义了每个交易需要遵循的一些规则。一开始是函数的四个参数:

to,from,amount,type。前三个参数不需要再解释了,而 Type 主要用于定义交易的类型。总共有两个类型:正常类型(normal) 和创世类型(genesis)。正常类型的交易会发生在账户 1065 和 2017 之间,而创世类型将会涉及到账户 0000。

  1. 第一个 if-else 条件语句处理转出账户的信息。如果账户不存在或者余额不足,将会提示交易不合法并返回。
  2. 第二个 if-else 条件语句处理转入账户的信息。如果账户不存在,则创建新账户并转入相应的比特币。反之,则向该账户转入正确数量的比特币。
  3. 最后一个 if-else 条件语句处理交易类型。如果类型是创世类型,则添加一个创世区块,否则创建一个新的区块存储数据。

打印函数

为了确保交易正确执行,在每个交易结束后,我们希望拿到所有交易的清单。以下是我们在交易函数下方的代码,用来打印相关信息:

  1. func chainState() { 
  2.     for i in 0...bitcoinChain.chain.count-1 { 
  3.         print("\tBlock: \(bitcoinChain.chain[i].index!)\n\tHash: \(bitcoinChain.chain[i].hash!)\n\tPreviousHash: \(bitcoinChain.chain[i].previousHash!)\n\tData: \(bitcoinChain.chain[i].data!)"
  4.     } 
  5.     redLabel.text = "Balance: \(accounts[String(describing: firstAccount)]!) BTC" 
  6.     blueLabel.text = "Balance: \(accounts[String(describing: secondAccount)]!) BTC" 
  7.     print(accounts) 
  8.     print(chainValidity()) 

这是一个简单的循环语句,遍历 bitcoinChain 中的所有区块,并打印区块号码,哈希,前置哈希,以及存储的数据。同时我们更新了界面中的标签(label),这样就可以显示账户中正确的 BTC 数量。最后,打印所有的账户(应该是 3 个),并校验区块链的有效性。

现在你应该会在函数的最后一行发现一个错误。这是由于我们还没有实现 chainValidity() 函数,让我们马上开始吧。

有效性函数

判断一个链是否有效的标准是:前置区块的哈希与当前区块所表示的是否匹配。我们可以再次用循环语句来遍历所有的区块:

  1. func chainValidity() -> String { 
  2.     var isChainValid = true 
  3.     for i in 1...bitcoinChain.chain.count-1 { 
  4.         if bitcoinChain.chain[i].previousHash != bitcoinChain.chain[i-1].hash { 
  5.             isChainValid = false 
  6.         } 
  7.     } 
  8.     return "Chain is valid: \(isChainValid)\n" 

和之前一样,我们遍历了 bitcoinChain 中的所有区块,并检查了前置区块的 hash 是否与当前区块的 previousHash 一致。

就酱!我们已经将定义了所有需要的函数!你的 ViewController.swift 应该如下图一样:

这里写图片描述
这里写图片描述

收尾工作就是连接按钮和函数啦。让我们马上开始最后的部分吧!

让一切关联起来

当我们的应用第一次启动时,需要创世账户 0000 发送 50 BTC 到我们的第一个账户。再从第一个账户转账 10 BTC 到第二个账户,这只需要寥寥三行代码。最后 viewDidLoad 中的代码如下:

  1. override func viewDidLoad() { 
  2.     super.viewDidLoad() 
  3.     transaction(from"0000"to"\(firstAccount)", amount: 50, type: "genesis"
  4.     transaction(from"\(firstAccount)"to"\(secondAccount)", amount: 10, type: "normal"
  5.     chainState() 
  6.     self.invalidAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 

我们使用已定义好的函数转账,并调用 chainState() 函数。最后,我们还在 invalidAlert 中添加了一个标题为 OK 的 UIAlertAction。

现在让我们来实现剩下的四个函数:ReMeNe()、BrimeMeNe()、ReSdEnter()和BuLeScript()。

挖矿函数

挖矿函数特别简单,只需要三行代码。添加以下代码:

  1. @IBAction func redMine(_ sender: Any) { 
  2.     transaction(from"0000"to"\(firstAccount)", amount: 100, type: "normal"
  3.     print("New block mined by: \(firstAccount)"
  4.     chainState() 
  5.      
  6. @IBAction func blueMine(_ sender: Any) { 
  7.     transaction(from"0000"to"\(secondAccount)", amount: 100, type: "normal"
  8.     print("New block mined by: \(secondAccount)"
  9.     chainState() 

在第一个挖矿函数中,我们使用交易函数从创世账户发送了 100 BTC 到第一个账户。我们打印了挖矿的区块,然后打印了区块链的状态。同样地,在 blueMine 函数中,我们转给了第二个账户 100 BTC。

发送函数

发送函数和挖矿函数略微相似:

  1. @IBAction func redSend(_ sender: Any) { 
  2.     if redAmount.text == "" { 
  3.         present(invalidAlert, animated: true, completion: nil) 
  4.     } else { 
  5.         transaction(from"\(firstAccount)"to"\(secondAccount)", amount: Int(redAmount.text!)!, type: "normal"
  6.         print("\(redAmount.text!) BTC sent from \(firstAccount) to \(secondAccount)"
  7.         chainState() 
  8.         redAmount.text = "" 
  9.     } 
  10.      
  11. @IBAction func blueSend(_ sender: Any) { 
  12.     if blueAmount.text == "" { 
  13.         present(invalidAlert, animated: true, completion: nil) 
  14.     } else { 
  15.         transaction(from"\(secondAccount)"to"\(firstAccount)", amount: Int(blueAmount.text!)!, type: "normal"
  16.         print("\(blueAmount.text!) BTC sent from \(secondAccount) to \(firstAccount)"
  17.         chainState() 
  18.         blueAmount.text = "" 
  19.     } 

首先,我们检查 redAmount 或者 blueAmount 的文本值是否为空。如果为空,则弹出无效交易的提示框。如果不为空,则继续下一步。我们使用交易函数从第一个账户转账到第二个账户(或者反向转账),金额为输入的数量。我们打印转账金额,并调用 chainState() 方法。最后,清空文本框。

就酱,工作完成!请检查你的代码是否和图中一致:

这里写图片描述
这里写图片描述

现在运行应用并测试一下。从前端看,这就像一个正常的交易应用,但是运行在屏幕背后的可是区块链啊!请尝试使用应用将 BTC 从一个帐户转移到另一个帐户,随意测试,尽情把玩吧!

结论

在这个教程中,你已经学会了如何使用 Swift 创建区块链,并且创建了你自己的比特币交易系统。请注意,真正加密货币的后端,和我们上面的实现完全不一样,因为它需要用智能合约实现分布式,而本例仅用于学习。

在这个示例中,我们将区块链技术应用于加密货币,然而你能想到区块链的其他应用场景吗?请留言分享给大家!希望你能学到注册即领取38元体验金新东西!

为了参考,你可以从 GitHub 下载完整的示例。

【编辑推荐】

  1. 谷歌搞事情,Fuchsia OS操作系统运行Swift代码
  2. Swift多线程:GCD进阶,单例、信号量、任务组
  3. 谷歌开源 Swift for TensorFlow:我们是不是终于可以放下Python了?
  4. 用Swift编写网络层:面向协议方式
  5. Swift面向协议编程(POP)的一些Tips
【责任编辑:未丽燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +注册即领取38元体验金

精通Spring 2.0

本书是关于Spring 2.0的权威教程,是Java/Java EE开发者必备的参考书。本书详尽系统地介绍了Java EE的基础知识、Spring 2.0的各种功能,以...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
博聚网