파이썬으로 나만의 블록체인 만들기 #1

정보보안 2017. 11. 14. 19:59

#참고 : 이 글은 @ecomusing 님의 포스트를 제 맘대로 번역한 글입니다. 원문보기

#참고2 : 전체 Jupyter / iPython 소스코드 받기 


나만의 블록체인 만들기 - 기초


이 튜토리얼은 기초부터 차근차근 블록체인을 어떻게 만드는가에 대한 내용을 다룰 것입니다.

구체적인 예제들의 자세한 설명들이 블록체인의 장점과 한계에 대한 깊은 이해를 도울 것입니다.

보다 자세한 개론에 대해서는 BitsOnBlocks 의 글을 읽기를 추천합니다. 


거래내역(Transactions), 검증(Validation), 시스템 상태 업데이트(Updating system state)


블록체인의 중심에는 배포되는 데이터베이스가 있습니다. 이 데이터베이스는 새로 추가되는 데이터베이스에  대한 검증을 할수 있는 규칙들이 있습니다. 이것을 설명하기 위해서 가상의 돈을 서로 거래하는 가상의 인물 앨리스(Alice)와 밥(Bob)의 계좌를 따라가보도록 하겠습니다.


우선 입출금 거래내역들과 거래내역들의 유효성 검사 기능을 만들고 이것들을 블록으로 만드는 것이 필요합니다.


각각의 거래내역들의 '지문'을 만들기 위해 hash function 을 사용할 것입니다. 이 hash function 은 각각의 블록들을 다른 블록들과 연결시켜주는 역할을 합니다. 이 hash function 을 쉽게 사용하기 위해서 우리는 Python hash function 기능을 포함하는 helper function 을 정의할 것입니다.


In [1]:
import hashlib, json, sys

def hashMe(msg=""):
    # For convenience, this is a helper function that wraps our hashing algorithm
    if type(msg)!=str:
        msg = json.dumps(msg,sort_keys=True)  # If we don't sort keys, we can't guarantee repeatability!
        
    if sys.version_info.major == 2:
        return unicode(hashlib.sha256(msg).hexdigest(),'utf-8')
    else:
        return hashlib.sha256(str(msg).encode('utf-8')).hexdigest()

다음으로 우리는 앨리스와 밥 사이의 거래내역들을 생성해주는 기능을 만들 것입니다. 출금은 음수로, 입금은 양수로 나타내질 것입니다. 

거래내역들은 항상 우리가 만든 시스템 안의 두 사람 사이에서 발생하는 거래내역들이고, 입금 금액은 출금 금액과 똑같아야 합니다. 즉, 전체 돈은 늘어나거나 줄어들지 않습니다.


In [2]:
import random
random.seed(0)

def makeTransaction(maxValue=3):
    # This will create valid transactions in the range of (1,maxValue)
    sign      = int(random.getrandbits(1))*2 - 1   # This will randomly choose -1 or 1
    amount    = random.randint(1,maxValue)
    alicePays = sign * amount
    bobPays   = -1 * alicePays
    # By construction, this will always return transactions that respect the conservation of tokens.
    # However, note that we have not done anything to check whether these overdraft an account
    return {u'Alice':alicePays,u'Bob':bobPays}

이제 많은 양의 거래내역들을 만들어 봅시다.



In [3]:
txnBuffer = [makeTransaction() for i in range(30)]

다음으로 블록들을 만들 차례입니다. 우리는 임의로 생성한 거래내역들(transaction buffer)에서 k 거래내역들을 가져와서 블록으로 만들 것입니다. 이것을 하기 전에, 우리는 블록에 들어갈 거래내역들을 검증하는 방법을 정의하는 것이 필요합니다.


비트코인의 경우, 유효성 검사는 들어오는 값(input value)이 출력된 거래내역으로 올바르게 쓰였는지(UTXOs), 출력된 거래내역들이 입력 값보다 큰 지, 서명에 사용된 키들이 유요한지에 대해 검사합니다. 이더리움의 경우, 유효성 검사는 smart contracts 가 정확하게 실행되고 gas limits 을 준수했는지 확인합니다.


하지만 우리는 이렇게 복잡한 시스템을 만들 필요는 없습니다. 우리의 블록체인의 기본적인 토큰 시스템을 위한 간단한 규칙들을 정의를 하면 다음과 같습니다.


◆ 입금과 출금의 함은 반드시 0이여야 한다(토큰이 생성되거나 없어지지 않는다)

◆ 사용자의 계좌에 반드시 출금 금액보다 많은 자금이 있어야 한다.


둘 중에 하나의 조건이라도 만족하지 못하면, 그 거래내역은 등록이 거부될 것입니다.


In [4]:
def updateState(txn, state):
    # Inputs: txn, state: dictionaries keyed with account names, holding numeric values for transfer amount (txn) or account balance (state)
    # Returns: Updated state, with additional users added to state if necessary
    # NOTE: This does not not validate the transaction- just updates the state!
    
    # If the transaction is valid, then update the state
    state = state.copy() # As dictionaries are mutable, let's avoid any confusion by creating a working copy of the data.
    for key in txn:
        if key in state.keys():
            state[key] += txn[key]
        else:
            state[key] = txn[key]
    return state
In [5]:
def isValidTxn(txn,state):
    # Assume that the transaction is a dictionary keyed by account names

    # Check that the sum of the deposits and withdrawals is 0
    if sum(txn.values()) is not 0:
        return False
    
    # Check that the transaction does not cause an overdraft
    for key in txn.keys():
        if key in state.keys(): 
            acctBalance = state[key]
        else:
            acctBalance = 0
        if (acctBalance + txn[key]) < 0:
            return False
    
    return True

다음 예제는 가짜 거래내역이 포함되어 있는 몇가지 거래내역 입니다. 우리는 이제 위에서 만든 유효성 검사 함수로 가짜여부를 확인할 수 있습니다.


In [6]:
state = {u'Alice':5,u'Bob':5}

print(isValidTxn({u'Alice': -3, u'Bob': 3},state))  # Basic transaction- this works great!
print(isValidTxn({u'Alice': -4, u'Bob': 3},state))  # But we can't create or destroy tokens!
print(isValidTxn({u'Alice': -6, u'Bob': 6},state))  # We also can't overdraft our account.
print(isValidTxn({u'Alice': -4, u'Bob': 2,'Lisa':2},state)) # Creating new users is valid
print(isValidTxn({u'Alice': -4, u'Bob': 3,'Lisa':2},state)) # But the same rules still apply!
True
False
False
True
False

각각의 블록은 한 묶음의 거래내역, 이전 블록의 해쉬(블록 번호가 1보다 클 때), 해당 블록의 내용과 헤더에 대한 해쉬값을 가지고 있습니다.