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

스위프트로 배우는 블록체인 -데이터구조

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

스위프트로 배우는 블록체인 - 데이터구조


순서

xcode9열기 -> get Started with a playground -> 위쪽 탭에 mac os 선택 -> blank 선택


개요

이번 포스팅에서는 블록체인 기술의 뼈대가 되는 데이터 구조를 만들어 보겠습니다. 먼저 거래를 담당하는 transaction 구조체, 거래내역과 해쉬, 이전해쉬값, nonce 값을 담는 block 구조체를 만들고 그 블록을 담을 blockchain 구조체를 만들어 보겠습니다. 위에서 오픈한 mac os playground를 열고 아래 코드를 작성해주세요.


transaction 구조체

//거래 데이터 구조체

class Transaction{

    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

    }

    }


block 구조체

//블록 구조체

class Block{

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

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int

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

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

    

    init(){

        self.nonce = 0

    }

}


blockchain구조체

//체인 구조체

class BlockChain {

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

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

}



key생성하기

먼저 class구조체를 json형식으로 사용할 수 있게 하기 위해서 class type을 Codable로 지정해줍니다. class Block : Codable{} , 모든class에 지정! 그리고 key를 Blok class의 getter 함수 key 를 만들어 주고, 그 아래 거래가 발생할때 거래 내역을 담아 줄 배열을 만들어 줍니다.

    //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)

    }


그리고 맨 아래 부분에서 거래를 한번 발생시키고, 새로운 블럭과 key를 만들어 봅시다.

let transaction = Transaction(from: "kim", to: "hong", amount: 1000)

let block1 = Block()

block1.addTransaction(transaction: transaction)

block1.key


Blockchain 구조체 초기화

이 클래스에서는 blocks 배열에 아무런 블럭이 없으면 현재 블록개수는 0이기 때문에 제네시스 블럭, 즉 첫번째 블록을 만드는 소스를 추가하고, blocks 배열에 추가해 주는 로직을 작성합니다. 그리고 해쉬값을 생성해주는 함수를 만들어서호출 하면 해쉬값을 리턴시켜줍니다. 이때 mac os 내장 모듈인 sha1Hash를 사용합니다.

//체인 구조체

class Blockchain : Codable {

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

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

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

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

    private func addBlock(_ block : Block){

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

        if self.blocks.isEmpty{

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

            block.previousHash = "0000000000"

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

            block.hash = generateHash(for :block)

        }

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

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

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

        let hash = block.key.sha1Hash()

        return hash

    }

}

sha1Hash 코드작성

// 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: "")

    }

}


제네시스 블록생성 후 체인에 추가

//제네시스 블록 생성

let genesisBlock = Block()


//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)



전체소스코드

//: Playground - noun: a place where people can play


/*

 

 

 

 */




import Cocoa


//거래 데이터 구조체

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

    }

    

}


//블록 구조체

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]()

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

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

    private func addBlock(_ block : Block){

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

        if self.blocks.isEmpty{

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

            block.previousHash = "0000000000"

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

            block.hash = generateHash(for :block)

        }

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

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

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

        let hash = block.key.sha1Hash()

        return hash

    }

}


//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)



let transaction = Transaction(from: "kim", to: "hong", amount: 1000)

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: "")

    }

}





특정값에 해당하는 hash값 리턴

위에서 block의 key값을 sha1Hash() 함수로 바꾸어서 임의의 문자열데이터로 만들어 주었다. 여기서 조건을 부여해서 해쉬 값이 특정조건에 맞으면 그 값을 리턴하게 하는 코드를 작성해 보겠습니다. class Blockchain (){} 함수 안에 있는 해쉬 생성 함수를 아래와 같이 변경 해줍니다.


    //해쉬 생성 함수

    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


}


새로운 블럭생성 함수

새로운 블럭에는 항상 이전 블럭의 해쉬값, 자신의 번호 , 자신의 해쉬 값이 들어갑니다. class Blockchain (){} 함수 안에 작성

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

    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]

    }



새로운 거래와 새로운 블록 생성

새로운 거래를 발생시키는 Transaction 함수를 이용해서 거래 내역을 만들고 이것을 다음에 생성될 새로운 블록안에 넣어주었다. 그리고 새로 생성된 블록은 blockchain 안에 있는 블록의 배열 안에 넣어주었다. 그리고 체인안에 존재하는 블록의 개수를 찍어 보았다. 총 2가 나온다. 처음만든 제네시스 블록 하나, 그리고 코드로 만들어준 블록 하나 총 2개다. 참고로 addBlock 함수에 접근하기 위해서 addBlock 함수 앞에 있는 private 키워드를 지워주자.

//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

let transaction = Transaction(from: "kim", to: "hong", amount: 1000)


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

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

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

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

blockchain.addBlock(block)

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

print(blockchain.blocks.count)


결과확인


코드 정리




import Cocoa


//거래 데이터 구조체

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

    }

    

}


//블록 구조체

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]()

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

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

     func addBlock(_ block : Block){

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

        if self.blocks.isEmpty{

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

            block.previousHash = "0000000000"

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

            block.hash = generateHash(for :block)

        }

        //삽입

        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)


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

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

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

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

blockchain.addBlock(block)

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

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: "")

    }

}

반응형

댓글