비트코인이 열풍이다. 일론 머스크가 쏘아올린 도지코인이 급등하는 모습을 보면 암호화폐는 그저 투기종목 같아 보인다. 그러나 암호화폐는 달러, 원화와 같은 기존 통화와 근본적으로 다르다.
암호화폐의 핵심은 정부, 은행과 같은 제 3의 기관의 통제 없이 개인 대 개인(Peer 2 Peer)사이에서 자유로운 거래가 가능하도록 하는 시스템이다.
신뢰 기반의 기존 통화 시스템과는 다르게 암호화폐는 Don't Trust. Verify를 말한다.
파이썬으로 비트코인의 백서를 구현해보며 이것이 어떻게 가능한지를 알아보도록 하자.
파이썬으로 비트코인 백서에서 설명하는 암호화폐를 구현하고 핵심 기술을 이해해보자!
from datetime import datetime
class Transaction:
def __init__(self, sender: 'Node', recipient: 'Node', amount: int):
self.sender = sender
self.recipient = recipient
self.amount = amount
self.timestamp = datetime.now()
def __repr__(self):
return f"{self.sender} -> {self.recipient} : {self.amount} coins"
class Node:
def __init__(self):
self.ledger = list() # 각 Node 마다 거래 리스트를 갖는다.
def new_transaction(self, recipient: 'Node', amount: int) -> Transaction:
tx = Transaction(sender=self, recipient=recipient, amount=amount)
self.ledger.append(tx)
return tx
def __repr__(self):
return f"Node_{id(self)}"
호정 = Node()
준우 = Node()
tx1 = 준우.new_transaction(호정, 3)
print(tx1)
> excute
Node_4510831232 -> Node_4510832240 : 3 coins
import rsa
class Node:
def __init__(self):
self.ledger = list()
(self.public_key, self.secret_key) = rsa.newkeys(512)
def sign_transaction(self, transaction) -> bytes:
signature = rsa.sign(str(transaction).encode(),
self.secret_key,
'SHA-256')
transaction.signature = signature
def new_transaction(self, recipient: 'Node', amount: int) -> Transaction:
tx = Transaction(sender=self, recipient=recipient, amount=amount)
tx = self.sign_transaction(tx)
self.ledger.append(tx)
return tx
@staticmethod
def verify_transaction(transaction, sender_public_key):
try:
rsa.verify(str(transaction).encode(), transaction.signature,
sender_public_key)
print("Transaction has valid signature")
except Exception as e:
print("Transaction has invalid signature!")
def __repr__(self):
return f"Node_{id(self)}"
호정 = Node()
준우 = Node()
성용 = Node()
tx1 = 준우.new_transaction(호정, 3)
print(tx1)
호정.ledger.append(tx1) # 자신의 장부에 거래 등록
호정.verify_transaction(tx1, 준우.public_key) # 거래 검증
호정.verify_transaction(tx1, 성용.public_key)
> result
Node_4514578832 -> Node_4512942352 : 3 coins
Transaction has valid signature
Transaction has invalid signature!
![image-20210711093604359](/Users/junumoon/Library/Application Support/typora-user-images/image-20210711093604359.png)
Hash 알고리즘 중 가장 일반적으로 사용되는 sha-256을 사용한다.
class Transaction:
class Node:
class Transaction:
def __init__(self, preivous_hash: str, sender: 'Node', recipient: 'Node', amount: int):
self.previous_hash = previous_hash
# ...
@property
def hash_id(self):
return rsa.compute_hash(message=str(self).encode(), method_name='SHA-256')
class Node:
def new_transaction(self, recipient: 'Node', amount: int) -> Transaction:
if not self.ledger: # 최초 거래의 previous_hash는 임의 지정
previous_hash = 'first_transaction'
else:
previous_hash = self.ledger[-1].hash_id # 장부에 등록된 마지막 거래의 hash_id
tx = Transaction(previous_hash=previous_hash, sender=self, recipient=recipient, amount=amount)
tx = self.sign_transaction(tx)
self.ledger.append(tx)
return tx
호정 = Node()
준우 = Node()
성용 = Node()
tx_list = []
tx1 = 준우.new_transaction(호정, 5)
호정.ledger.append(tx1)
tx2 = 호정.new_transaction(성용, 3)
성용.ledger.append(tx2)
print('tx1 hash_id:', tx1.hash_id.hex())
print('tx2 hash_id:', tx2.hash_id.hex())
> result
tx1 hash_id: 6ef97048c38298fede63d5beda5ac4e31523d8199fe17027891153516b235e65
tx2 hash_id: f5255f6984c94f4b030c9adefa7926d3e23779645ac209f4469431a0b361dd53
hash_id[:6] == "0" * 6
def proof_of_work(transaction):
nonce = 0 # 임시값
while True:
input_data = (str(transaction) + str(nonce)).encode()
computed_hash = rsa.compute_hash(input_data, 'SHA-256').hex()
if computed_hash[:6] == "0" * 6:
break
else:
nonce += 1
return computed_hash, nonce
과거 블록을 변경하려면 공격자는 그 블록과 그 뒤를 잇는 모든 블록의 작업증명을 재수행해야 하고 그러면서 가장 정직 한 노드들의 작업을 따라잡아 앞질러야 한다. 이어지는 블록이 추가될수록 더 느린 공격자가 따 라잡을 확률이 지수적으로 감소한다. _ 비트코인 백서 - 작업증명
class Block:
MINE_DIFFICULTY = 4
FIRST_NONCE = 100
def __init__(self, previous_hash, nonce, miner, transactions):
self.preivous_hash = previous_hash
self.nonce = nonce
self.miner = miner
self.timestamp = datetime.now()
self.transactions = transactions
@classmethod
def proof_of_work(cls, block):
nonce = 0
while True:
_hash = custom_hash(block, nonce)
if _hash[:cls.MINE_DIFFICULTY] == "0"*cls.MINE_DIFFICULTY:
break
else:
nonce += 1
return _hash, nonce