본문 바로가기
ios 뽀개기/swift-blockchain

스위프트로 배우는 블록체인 - 채굴구현

by 인생여희 2018. 6. 2.
반응형

스위프트로 배우는 블록체인 - 채굴구현


먼저 이전전 포스팅에서 다루었던 BlockChainSwift.playground를 vapor프로젝트에 App -> Model 안에 model.swift 라는 이름으로 새로 파일을 만들어서 복붙해준다.


//

//  models.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//

import Vapor

import Foundation

import Cocoa


//거래가 발생할때 거래 타입 선택 가능

enum TransactionType : String, Codable{

    case domestic

    case international

}


// 스마트 컨트랙드 프로토콜 함수 - 지원,신청하는 계약임

protocol SmartContract{

    func apply(transaction : Transaction)

}


//스마트 컨트랙트를 상속한 함수 구현

class TransactionTypeSmartContract : SmartContract{

    //스마트 컨트랙트 함수가 호출되면 apply 함수 실행

    func apply(transaction: Transaction) {

        

        var fees = 0.0

        //거래 타입에 따라 fees 계산해주기

        switch transaction.transactionType {

        case .domestic:

            fees = 0.02

        case .international:

            fees = 0.05

        }

        //거래 타입에 따라서 항목 계산 후 대입

        transaction.fees = transaction.amount * fees

        transaction.amount -= transaction.fees

    }

}




//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    

    //수수료

    var fees: Double = 0.0

    //거래 타입

    var transactionType : TransactionType

    

    //초기화

    init(from:String,to:String,amount:Double,transactionType:TransactionType) {

        self.from = from

        self.to = to

        self.amount = amount

        self.transactionType = transactionType

    }

}


//블록 구조체

class Block: Codable{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int

    

    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

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

        }

    }

    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }

    

    //초기화

    init(){

        self.nonce = 0

    }

}


//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //스마트 계약 관련 데이터 배열 변수

    private (set) var smartContracts : [SmartContract] = [TransactionTypeSmartContract()]

    

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    private enum CodingKeys : CodingKey {

        case blocks

    }

    

    

    //블록체인 구조체에 블록을 추가하는 함수

    func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        

        // run the smart contracts

        self.smartContracts.forEach { contract in

            block.transactions.forEach { transaction in

                contract.apply(transaction: transaction)

            }

        }

        

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        var hash = block.key.sha1Hash()

        // 특정값 (앞 2자리가 00 인 hash 값 구하기

        while(!hash.hasPrefix("00")){

            //조건이 맞지 않을 경우 block의 noce 값 증가

            block.nonce += 1

            hash = block.key.sha1Hash()

            print(hash)

        }

        

        return hash

    }

    

    //새로운 블락 구하는 함수, 다음 블록 생성

    func getNextBlock(transactions:[Transaction]) ->Block{

        //새 블록 객체

        let block = Block()

        //새로운 블록이 생성될때는 이전 거래 내역이 배열로 들어간다.

        transactions.forEach { (transaction) in

            block.addTransaction(transaction: transaction)

        }

        //새로운 블록에는 이전 블록의 hash 값, 번호, 자신의 hash 값이 들어간다.

        let previousBlock = getPreviousBlock()

        block.index = self.blocks.count

        block.previousHash = previousBlock.hash

        block.hash = generateHash(for: block)

        return block

    }

    

    //이전 블럭 가져오기

    private func getPreviousBlock() -> Block {

        return self.blocks[self.blocks.count - 1]

    }

}


//제네시스 블록 생성

//let genesisBlock = Block()

//추가

//let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

//let transaction = Transaction(from: "kim", to: "hong", amount: 1000,transactionType:.international)


//print("--------------------------------------------------------")

//위에서 발생한 거래내역을 새로운 블럭을 생성하는 함수안에 넣어주었다.

//let block = blockchain.getNextBlock(transactions: [transaction])

//새로만든 블록을 넣어주고

//blockchain.addBlock(block)



//인코딩

//let data = try! JSONEncoder().encode(blockchain)

//json데이터를 string으로 출력

//let blockchainJSON = String(data: data, encoding: .utf8)


//print(blockchainJSON!)


//블록의 총 개수를 확인해보자.

//print(blockchain.blocks.count)



//let block1 = Block()

//block1.addTransaction(transaction: transaction)

//block1.key



// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}





BlockchainService.swift 작성

//

//  BlockchainService.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//


import Foundation


class BlockchainService {

    

    //체인배열 변수

    private (set) var blockchain : Blockchain!

    

    //제네시스 블럭 초기화=

    init() {

        self.blockchain = Blockchain(genesisBlock: Block())

        

    }

    

    //체인 반환 함수

    func getBlockchain() -> Blockchain{

        return self.blockchain

    }

    

}


BlockchainController.swift 작성

//

//  BlockchainController.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//


import Foundation

import Vapor

import HTTP


class BlockchainController {

    private (set) var drop : Droplet

    private (set) var blockchainService : BlockchainService

    

    init(drop: Droplet) {

        self.drop = drop

        self.blockchainService = BlockchainService()

        setupRoutes()

    }

    

    //이부분 수정!

    private func setupRoutes(){

        //웹브라우저에서 /blockchain으로 진입했을 때 배열타입의 블록체인 json타입으로 반환

        self.drop.get("blockchain"){request in

            let blockchain = self.blockchainService.getBlockchain()

            return try! JSONEncoder().encode(blockchain)

        }

    }

    }


테스트



Model.swift에 class Transaction 부분 request 초기화 함수 추가

//거래 데이터 구조체

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

        

    }

    

    //request 초기화 함수

    init?(request : Request) {

        guard let from = request.data["from"]?.string,

        let to = request.data["to"]?.string,

        let amount = request.data["amount"]?.double

             else {

                return nil

        }

        //request로 받은 데이터 초기화 시켜줌

        self.from = from

        self.to = to

        self.amount = amount


    }

}

블록 마이닝을 위한 BlockchainController.swift 작성 - 주석확인

    private func setupRoutes(){

        

        //블록 마이닝을 위한 코드

        self.drop.post("mine"){request in

            if let transaction = Transaction(request:request){

                let block = self.blockchainService.getMinedBlock(transactions : [transaction])

                

                //block.addTransaction(transaction: transaction)

                // json 값으로 리턴

                return try JSONEncoder().encode(block)

            }

            return try JSONEncoder().encode(["msg":"뭔가 문제가..."])

        }

        

        //웹브라우저에서 /blockchain으로 진입했을 때 배열타입의 블록체인 json타입으로 반환

        self.drop.get("blockchain"){request in

            let blockchain = self.blockchainService.getBlockchain()

            return try! JSONEncoder().encode(blockchain)

        }

    }


블록 마이닝을 위한 BlockchainService.swift 작성 - 주석확인

import Foundation


class BlockchainService {

    

    //체인배열 변수

    private (set) var blockchain : Blockchain!

    

    //제네시스 블럭 초기화=

    init() {

        self.blockchain = Blockchain(genesisBlock: Block())

        

    }

    

    //체인 반환 함수

    func getBlockchain() -> Blockchain{

        return self.blockchain

    }

    //블록 마이닝 -> 새로운 블럭 생성

    func getMinedBlock(transactions: [Transaction]) -> Block{

         let block = self.blockchain.getNextBlock(transactions:transactions)

//채굴된 블락 추가

        self.blockchain.addBlock(block)

        return block

    }

    

}


확인







반응형

댓글