In this paper, the reference

  • Learn Blockchains by Building One by Daniel Van Flymen
  • Article by Mohammad Azam
  • Bitcoin Developer’s Guide proper Terms

In my last article, I discussed how to implement basic blockchain in Swift. In this article, the server-side Swift framework Vapor will be used to implement blockchain in the cloud. The blockchain Web API is built over the HTTP protocol, using different routes to provide the necessary functionality. To read this article, install the Vapor Framework on your computer and have a basic understanding of the Swift language.

The implementation model

The first step is to create the necessary model for the blockchain Web API, as shown below.

Block: The Block class represents a Block that contains the inputs and outputs of a transaction.

class Block: Codable {
    var index: Int = 0
    var dateCreated: String
    var previousHash: String!
    var hash: String!
    var nonce: Int
    var message: String = ""
    private (set) var transactions: [Transaction] = [Transaction]()

    var key: String {
        get {
            let transactionsData = try! JSONEncoder().encode(self.transactions)
            let transactionsJSONString = String(data: transactionsData, encoding: .utf8)

            return String(self.index) + self.dateCreated + self.previousHash + transactionsJSONString! + String(self.nonce)
        }
    }

    func addTransaction(transaction: Transaction) {
        self.transactions.append(transaction)
    }

    init() {
        self.dateCreated = Date().toString()
        self.nonce = 0
        self.message = "Dig up new blocks."
    }

    init(transaction: Transaction) {
        self.dateCreated = Date().toString()
        self.nonce = 0
        self.addTransaction(transaction: transaction)
    }
}
Copy the code

The attributes of the Block class are explained as follows:

  • Index — The position of the block in the blockchain. An index of 0 indicates that the block is the first block in the blockchain. An index of 1 represents the second block in the blockchain… And so on!
  • DateCreated — the date the block was created
  • PreviousHash – Hash value of the previous block
  • Hash – The hash value of the current block
  • Message – a memo for each block. Just for example
  • Nonce — Increasing number, critical for generating hash values
  • Transactions — a series of transactions. Each transaction represents a transfer of goods/value
  • Key – Evaluates the property, which is supplied to the function that generates the hash value

Transaction: THE Transaction consists of the sender, the recipient, and the amount being transferred. The implementation is as follows:

class Transaction: Codable {
    var from: String
    var to: String
    var amount: Double

    init(from: String, to: String, amount: Double) {
        self.from = from
        self.to = to
        self.amount = amount
    }

    init?(request: Request) {
        guard let from = request.data["from"]? .string,let to = request.data["to"]? .string,let amount = request.data["amount"]? .doubleelse {
            return nil
        }
        self.from = from
        self.to = to
        self.amount = amount
    }
}
Copy the code

The implementation of the Transaction class is straightforward. Consists of the FROM, to, and amount fields. For simplicity, the FROM and to fields are represented by virtual names, and in practice they also contain the package Wallet ID.

Blockchain: The Blockchain (Blockchain) class is the main class that represents a list of blocks. Each block points to the previous block in the chain. Each block can contain multiple transactions, representing credits or debits.

class Blockchain: Codable {
    var blocks: [Block] = [Block]()

    init() {

    }

    init(_ genesisBlock: Block) {
        self.addBlock(genesisBlock)
    }

    func addBlock(_ block: Block) {
        ifSelf.blocks. IsEmpty {// Add trands block // First block has no previoushash
            block.previousHash = "0"
        } else {
            let previousBlock = getPreviousBlock()
            block.previousHash = previousBlock.hash
            block.index = self.blocks.count
        }

        block.hash = generateHash(for: block)
        self.blocks.append(block)
        block.message = "This block has been added to the blockchain"
    }

    private func getPreviousBlock() -> Block {
        return self.blocks[self.blocks.count - 1]
    }

    private func displayBlock(_ block: Block) {
        print("------ 第 \(block.index) 个区块 --------")
        print("Created date: \(block.dateCreated)") / /print("Data: \(block.data)")
        print(Nonce: \ "(block. The Nonce)")
        print("Hash of previous block: \(block.previousHash!) ")
        print("Hash: \(block.hash!) ")
    }

    private func generateHash(for block: Block) -> String {
        var hash= block.key.sha256()! // Set the work proofwhile(! hash.hasPrefix(DIFFICULTY)) { block.nonce += 1hash = block.key.sha256()!
            print(hash)}return hash}}Copy the code

Each model follows the Codable protocol for conversion to JSON objects. If you read the previous article, the implementation above looks familiar. The next step is to configure the routing for the Web API, which will be implemented using the Vapor framework in the next section.

Vapor implements the Web API

There are several different ways to implement Web apis with Vapor. I’m going to create a custom controller here to handle all blockchain requests so I don’t have to cram all the code into the Routes class. BlockchainController is implemented as follows:

class BlockchainController {
    private (set) var drop: Droplet
    private (set) var blockchainService: BlockchainService! init(drop: Droplet) {self.drop = drop self.blockChainService = blockchainService () // Set the route for the controller setupRoutes()} private funcsetupRoutes() {
        self.drop.get("mine") { request in
            let block = Block()
            self.blockchainService.addBlock(block)
            returnTry JSONEncoder().encode(block)} // Add new transaction self.drop.post("transaction") { request in
            if letTransaction = transaction (request: request) {// Add transaction to block // get the last block duglet block = self.blockchainService.getLastBlock()
                block.addTransaction(transaction: transaction)

                return try JSONEncoder().encode(block)
            }
            return try JSONEncoder().encode(["message": "An exception has occurred!} // get the chain self.drop. Get ()"blockchain") { request in
            if let blockchain = self.blockchainService.getBlockchain() {
                return try JSONEncoder().encode(blockchain)
            }

            return try! JSONEncoder().encode(["message":"The blockchain has not yet been initialized. Please dig first."])}}}Copy the code

Web API starts with three basic endpoints.

  • Mining: This endpoint starts the Mining process. Mining allows us to reach proof of work and then add blocks to the blockchain.
  • Transaction: This endpoint is used to add new transactions. Transactions contain information about the sender, receiver, and amount.
  • Blockchain: This endpoint returns the complete Blockchain.

BlockchainController uses BlockChainService to perform required operations. The implementation of BlockChainService is as follows:

import Foundation
import Vapor

class BlockchainService {
    
    typealias JSONDictionary = [String:String]
    private var blockchain: Blockchain = Blockchain()
    
    init() {

    }

    func addBlock(_ block: Block) {
        self.blockchain.addBlock(block)
    }

    func getLastBlock() -> Block {
        return self.blockchain.blocks.last!
    }

    func getBlockchain() -> Blockchain? {
        return self.blockchain
    }
}
Copy the code

Let’s examine the endpoint of the Web API. Start the Vapor server and send the request to the “mine” endpoint.

The proof-of-work algorithm generates a hash value starting with “000”. Blocks are mined and immediately returned in JSON format. Through Swift 4.0’s Codable protocol.

Now add a simple transaction to the blockchain, transferring $10 from Zhang Jiafu to Ma.

The final step is to check whether the blockchain contains newly added blocks. Access the “Blockchain” endpoint to see the complete chain.

Perfect! Our blockchain Web API now works.

It’s also a bit of a shame that blockchain is supposed to be decentralized, but currently we don’t have a mechanism to add new nodes. In the next section we will update the blockchain implementation to support multiple nodes.

Add nodes to the blockchain

Before adding nodes to a blockchain, you first define the nodes. The implementation of node model is as follows:

class BlockchainNode :Codable { var address :String init(address :String) { self.address = address } init? (request :Request) { guardlet address = request.data["address"]? .stringelse {
            return nil
        }
        
        self.address = address
    }
    
}
Copy the code

The BlockChainNode class is simple, with a single address attribute that identifies the URL of the node server. Then update BlockchainController to add the ability to register new nodes. As follows:

self.drop.get("nodes") { request in
            return try JSONEncoder().encode(self.blockchainService.getNodes())
        }

self.drop.post("nodes/register") { request in
            guard let blockchainNode = BlockchainNode(request: request) else {
                return try JSONEncoder().encode(["message": "Error registering node"])
            }
            
            self.blockchainService.registerNode(blockchainNode)
            return try JSONEncoder().encode(blockchainNode)
        }
Copy the code

Also update the BlockchainService to register new nodes.

	  func getNodes() -> [BlockchainNode] {
        return self.blockchain.nodes
    }
    
    func registerNode(_ blockchainNode: BlockchainNode) {
        self.blockchain.addNode(blockchainNode)
    }
Copy the code

So let’s test that out. Start the new Vapor server and try to register the new node.

After the node is registered, it can be obtained using the Nodes Endpoint, as shown below:

Now that you can register new nodes, you’ll focus on resolving conflicts between nodes. If the blockchain on one node is larger than another, there will be a conflict. In this case, it is common to take adjacent nodes and update them with larger blockchains.

Resolve conflicts between nodes

To create a conflict, we need a second server or run the server on a different port. This article uses the latter method to start the Vapor server on a different port. After the two nodes are initialized, each creates blocks and transactions that are added to its own blockchain. Finally, the Resolve endpoint is called to resolve conflicts between nodes and update the node to the larger blockchain.

Add a new endpoint to the BlockchainController to resolve the conflict.

self.drop.get("nodes/resolve") { request in
            return try Response.async { portal in
                self.blockchainService.resolve { blockchain in
                    let blockchain = try! JSONEncoder().encode(blockchain)
                    portal.close(with: blockchain.makeResponse())
                }
            }
        }
Copy the code

The Async Response function of Vapor framework was used above to process the response asynchronously. The BlockchainService is then updated to resolve the conflict. The implementation is as follows:

Func resolve(completion: @escaping(Blockchain) -> ()) {// Retrieve nodeslet nodes = self.blockchain.nodes
        
        for node in nodes {
            let url = URL(string: "http://\(node.address)/blockchain")!
            URLSession.shared.dataTask(with: url, completionHandler: { (data, _, _) in
                if let data = data {
                    let blockchain = try! JSONDecoder().decode(Blockchain.self, from: data)
                    
                    if self.blockchain.blocks.count > blockchain.blocks.count {
                        completion(self.blockchain)
                    } else {
                        self.blockchain.blocks = blockchain.blocks
                        completion(blockchain)
                    }
                }
            }).resume()
        }
    }
Copy the code

The resolve function iterates through the list of nodes and retrieves the blockchain for each node. If a block chain is larger than the current block chain, replace the current block chain with the larger one, otherwise return the current block chain directly, because the current block chain is already larger.

To test this we will start two servers on different ports, adding three transactions on port 8080 and two on 8081. The Vapor server can be started by entering the following command in the terminal.

Vapor run serve - - port = 8081Copy the code

Add three transactions on port 8080 as follows:

Then add two transactions to the port 8081 node as follows:

Make sure the node at address 8080 is registered as follows:

Finally, test resolve Endpoint. Access “resolve” endpoint in Postman as follows:

As you can see, the Resolve endpoint returns the larger blockchain and also updates the blockchain of the node. Thus the solution to the conflict is complete.

[GitHub]