https://busy.org/@pangol/python-1 편에서는 블록체인 기본 기능인 블록요소로 해쉬 값을 생성하고 이 전 블록의 해쉬값을 연결하는 걸 구현해봤습니다. 이 번에는 블록을 생성할 때 마이닝을 하는 기능을 만들어 보겠습니다.
<p dir="auto">먼저, 이전에 구현한 Block 클래스를 보면 <pre><code>class Block(): def __init__(self, index, timestamp, data): self.index = index self.timestamp = timestamp self.data = data self.previousHash = 0 self.hash = self.calHash() def calHash(self): return hashlib.sha256(str(self.index).encode() + str(self.data).encode() + str(self.timestamp).encode() + str(self.previousHash).encode()).hexdigest() <p dir="auto">여기에서 추가해야 할 기능은, 해쉬를 맞추면 블록이 생성 되는 기능입니다.<br /> 우선 mine 함수를 정의하고 설정된 difficulty에 따라 해당 블록의 해쉬 값을 설정하는 코드를 만들어 보겠습니다 <pre><code> def mine(self, difficulty): ans = ["0"]*difficulty answer = "".join(ans) while(str(self.hash)[:difficulty] != answer): self.nonce += 1 self.hash = self.calHash() print('mined block: ', self.hash) return self.hash <p dir="auto">해쉬 값 맞추기 게임에서 난이도 설정 값을 받고 해쉬 값이 범위에 들어와 있으면 값을 설정하고 반환을 해주는 함수입니다. <p dir="auto">mine 함수의 1,2 라인은 계산된 해쉬 값이 범위안에 들어오는지 확인하기 위해 사용된 값입니다. 예를 들어 난이도가 2로 설정되었다면 ans의 값은 '00'이 되고 while 구문에서 hash의 0~2까지의 substring 이 00이 될 때까지 계속 해쉬 값을 찾는 구문입니다. 그리고 찾을 때 까지 nonce의 값을 1씩 올려서 계산된 hash값의 변화를 줍니다. <p dir="auto">해쉬를 계산할 때, nonce를 추가해서 계산할 수 있게 calHash함수와 생성자도 변경해줍니다. <pre><code> def __init__(self, index, timestamp, data): self.index = index self.timestamp = timestamp self.data = data self.previousHash = 0 self.nonce = 0 self.hash = self.calHash() def calHash(self): return hashlib.sha256(str(self.index).encode() + str(self.data).encode() + str(self.nonce).encode() + str(self.timestamp).encode() + str(self.previousHash).encode()).hexdigest() <p dir="auto">생성자에서는 nonce 초기화 하는 코드를 calHash함수에서는 str(self.nonce).encode를 추가해줬습니다. 아래는 block과 관련된 전체 코드입니다. <pre><code>class Block(): def __init__(self, index, timestamp, data): self.index = index self.timestamp = timestamp self.data = data self.previousHash = 0 self.nonce = 0 self.hash = self.calHash() def calHash(self): return hashlib.sha256(str(self.index).encode() + str(self.data).encode() + str(self.nonce).encode() + str(self.timestamp).encode() + str(self.previousHash).encode()).hexdigest() def mine(self, difficulty): ans = ["0"]*difficulty answer = "".join(ans) while(str(self.hash)[:difficulty] != answer): self.nonce += 1 self.hash = self.calHash() print('mined block: ', self.hash) return self.hash <p dir="auto">그럼 Blockchain 클래스에서 블록을 추가할 때 사용한 addBlock에서 mine함수를 통해서 블록을 생성할 수 있게 변경하겠습니다. <pre><code> def addBlock(self, nBlock): nBlock.previousHash = self.chain[len(self.chain)-1].hash nBlock.hash = nBlock.mine(self.difficulty) self.chain.append(nBlock) <p dir="auto">두 번째 줄의 nBlock.mine(self.difficulty)로 변경하였습니다. difficulty는 blockchain을 초기화할 때 값을 설정하도록 변경합니다. difficulty 수를 변경하면 마이닝 되는 블록의 시간을 조절할 수 있겠죵? <pre><code> def __init__(self, ): self.chain = [] self.difficulty = 2 self.createGenesis() <p dir="auto">아래는 Blockchain 클래스의 전체 코드입니다 <pre><code>class BlockChain: def __init__(self, ): self.chain = [] self.difficulty = 2 self.createGenesis() def createGenesis(self): self.chain.append(Block(0, time.time(), 'Genesis')) def addBlock(self, nBlock): nBlock.previousHash = self.chain[len(self.chain)-1].hash nBlock.hash = nBlock.mine(self.difficulty) self.chain.append(nBlock) def getLatestBlock(self): return self.chain[len(self.chain)-1] def isValid(self): i = 1 while(i<len(self.chain)): if(self.chain[i].hash != self.chain[i].calHash()): return False if(self.chain[i].previousHash != self.chain[i-1].hash): return False i += 1 return True <p dir="auto">설정된 난이도에 따라 마이닝이 어떻게 달라지는 한 번 보겠습니다.<br /> 처음에 난이도를 2로 설정하고 실행시켜보면.. <pre><code>onion = BlockChain() print('first block ..') start_time = time.time(); onion.addBlock(Block(len(onion.chain),time.time(), {"amount":4})) print('Passed time: ', time.time() - start_time) <pre><code>first block .. mined block: 00cff1af236ef6bc29f2f607ded9ee2d19880ef9075f292649b39835539a5002 Passed time: 0.0009968280792236328 <p dir="auto">난이도를 4로 설정하고 실행해보면.. <pre><code>first block .. mined block: 00007dced727e23b17f336320ea35a513fd0d8500af4d6712616e124ae87f1c7 Passed time: 0.2004687786102295 <p dir="auto">시간이 꽤 오래걸린다는 걸 확인해볼 수 있습니다~~ <p dir="auto">전체 코드 <pre><code>import hashlib import time import json class Block(): def __init__(self, index, timestamp, data): self.index = index self.timestamp = timestamp self.data = data self.previousHash = 0 self.nonce = 0 self.hash = self.calHash() def calHash(self): return hashlib.sha256(str(self.index).encode() + str(self.data).encode() + str(self.nonce).encode() + str(self.timestamp).encode() + str(self.previousHash).encode()).hexdigest() def mine(self, difficulty): ans = ["0"]*difficulty answer = "".join(ans) while(str(self.hash)[:difficulty] != answer): self.nonce += 1 self.hash = self.calHash() print('mined block: ', self.hash) return self.hash class BlockChain: def __init__(self, ): self.chain = [] self.difficulty = 4 self.createGenesis() def createGenesis(self): self.chain.append(Block(0, time.time(), 'Genesis')) def addBlock(self, nBlock): nBlock.previousHash = self.chain[len(self.chain)-1].hash nBlock.hash = nBlock.mine(self.difficulty) self.chain.append(nBlock) def getLatestBlock(self): return self.chain[len(self.chain)-1] def isValid(self): i = 1 while(i<len(self.chain)): if(self.chain[i].hash != self.chain[i].calHash()): return False if(self.chain[i].previousHash != self.chain[i-1].hash): return False i += 1 return True
전혀 모르겠네요 이런 건 그냥 멋있어보여서 보팅하고 갑니다 ㅎ
ㅎㅎ 감사합니다 :)