— Blockchain, Swift, Vapor — 3 min read
Exploring the nonce and playing with the mining service.
This article is part of the series "Building a Blockchain in Swift". You can find the intro here and find the part 1 here.
Now that we have our blockchain settled and we know how the blocks and transactions work, let’s take a look at the mining mechanism.
In our payload, we can’t really change a thing. We should maintain the block integrity, so it should be added to the chain verbatim. So what can we add to block's structure that can be tweaked for this sake?
Introducing the nonce.
This value is just a random number whose only purpose here is helping us find the "perfect" hash. The way we use it is to incrementing its value.
In a live blockchain, this algorithm isn’t part of the software. We are just putting it here for the experiment. This is the node's duty (and where all the gold relies), so it uses its own tailor-made solution.
1final class MiningService {2 3 // 1.4 private let blockchainDifficulty: String5 6 init(difficulty: String) {7 blockchainDifficulty = difficulty8 }9 10 // 2.11 func mineBlock(previousBlock: Block, transactions: [Transaction]) -> Block {12 13 let nextHeight = previousBlock.height + 114 print("⛏ Start mining block at height \(nextHeight)...")15 16 // 3. 17 let block = Block(height: nextHeight, previousHash: previousBlock.hash)18 transactions.forEach { block.add(transaction: $0) }19 20 // 4.21 let hash = generateHash(for: block)22 block.hash = hash23 24 return block25 }26 27 // 5.28 func generateHash(for block: Block) -> String {29 30 print("🔑 Finding the hash for block at height \(block.height)...")31 var hash = block.key.toSHA1()32 33 // 6.34 while !hash.hasPrefix(blockchainDifficulty) {35 block.incrementNonce()36 hash = block.key.toSHA1()37 print(hash)38 }39
40 print("🙌 Hash found! - \(hash)")41 return hash42 }43}
Wow, hold on! We have too much going on there. Let’s break it down:
mineBlock()
method that will take the previous block and a set of transactions we want to register and then return the recently minted block.P.S.: In this implementation I’m using a custom SHA-1 method which you can find here
You see, you don’t have any guarantee that the nonce
you're using next is the right one, so there's no other way apart from trial and error, to find this value.
This is a very simple way to implement a proof-of-work consensus, but not so efficient. So feel free to explore other ways to implement yours!
The beauty behind this is that it adds entropy to this problem. It’s a fair play for all nodes in the network. Whichever node finds the hash first can mine the block.
Now we have enough to try to create our own blockchain. This is how we can use it:
1// Exercise1.swift2
3// Creates the Mining Service4let miningService = MiningService(difficulty: "00") // starting with just 2 zeroes5
6// Generates Genesis Block7let genesisBlock = Block(height: 0, previousHash: "0000000000000000000000000000000000000000") // 40 zeroes8
9// Tries to find the hash for the genesis block10genesisBlock.hash = miningService.generateHash(for: genesisBlock)11// Once it finds the hash, create the Blockchain12do {13 let blockchain = try Blockchain(genesisBlock: genesisBlock)14 print("Blockchain is ready! 🎉")15} catch let _ {16 print("🚫 Oh-oh! The block hash isn't valid.")17}
If you execute the code above (you can use a Swift Playground for it), you should get some output similar to this one in your console:
1🔑 Searching the hash for block at height 0...21CA360046CDE59C93CDAE5EC5A1A2DC7495D4A583E42D0BBF1E810538CBDCE382C040B599A2DDADEE4D4D96B51564127AE292448F75603F347C5B6894757BAC36206D7C747879F765547E7B2A2F35DC50946
7....8
95AC1DF0C735FF12CA81623D5BCA5DA56553061B610556A5A8BA5B8EC0167B35D782F10A22A1D99766111D0E0A2882B36B08196A0CC291532D8F213898B7812006809C305F6F9790890E6D7C7319817F15FCEE013🙌 Hash found! - 006809C305F6F9790890E6D7C7319817F15FCEE014Blockchain is ready! 🎉
My machine needed to run the function 218 times to find a hash for the genesis block! Picture how many times it would take to find a block with today’s difficulty! 😬
Now you have a blockchain you can push transactions to! Let’s try to transfer some value to people:
1// (Assumes the Exercise1.swift preceds)2
3// Creates a new block4let transaction = Transaction(sender: "Felipe", receiver: "Tim Cook", amount: 100)5
6let newBlock = miningService.mineBlock(previousBlock: genesisBlock, transactions: [transaction])7
8do {9 try blockchain.add(block: newBlock)10} catch let _ {11 print("🚫 Oh-oh! The block hash isn't valid.")12}
In your console, you should get something like this:
1⛏ Starting to mine the block at height 1...2🔑 Searching the hash for block at height 1...3C84DA890248ED95DF39E7AD15CACE49EA57BD6A44B75FE89D8125D91B6EA7B836F5C1765737741F8E58D12099BE01B765E0EA1F2675525EEA216448E946E2DD2E47050DFB75C1217216EBB85E1D99A359357
8...9
10C94DB820D09643D478229EA142EC0B4819503CBD1148EAA579E1E8BA192D9F2748B30989FCE7F745C81243146E96DDBBC7201A1BCC9CA4C5EF588FA0219D1300B33255769C3A5CD38E757D2C31E8CF2ACDF3C214🙌 Hash found! - 00B33255769C3A5CD38E757D2C31E8CF2ACDF3C2
This time the function ran for 116 times!
And that’s how your blockchain should look when printed as a JSON object:
1{2 "blocks": [3 {4 "nonce": 218,5 "previousHash": "0000000000000000000000000000000000000000",6 "hash": "006809C305F6F9790890E6D7C7319817F15FCEE0",7 "transactions": [],8 "height": 09 },10 {11 "nonce": 116,12 "previousHash": "006809C305F6F9790890E6D7C7319817F15FCEE0",13 "hash": "00B33255769C3A5CD38E757D2C31E8CF2ACDF3C2",14 "transactions": [15 {16 "sender": "Felipe",17 "receiver": "Tim Cook",18 "amount": 10019 }20 ],21 "height": 122 }23 ]24}
We’ve just written our first blockchain in Swift! 🍾
As I’ve mentioned earlier, we aren’t using the most efficient algorithm to find the hash, but that’s a great start. There's a long discussion about how sustainable the proof-of-work protocol is as well, as it demands too much energy.
That isn’t the only way to achieve consensus, though. Most advanced blockchains nowadays use protocols such as proof-of-stake (e.g. Ethereum 2.0 and Cardano), but there are many others, such as proof-of-authority, proof-of-capacity, etc.
Hope you had as much fun as I had writing this. Thanks for reading! If you have any suggestions or questions, please hit me up on Twitter!
Valeu! ✌️