Cách nhanh nhất để tìm hiểu cách hoạt động của Blockchains là tự tay xây dựng nó.
Bạn ở đây bởi vì cũng giống như tôi, bạn đang bị ấn tượng về sự tăng giá của tiền điện tử Bitcoin. Và bạn muốn biết cách hoạt động của Blockchains — công nghệ cơ bản đằng sau Bitcoin.
Nhưng hiểu được Blockchains không dễ - hoặc ít nhất là không phải đối với tôi. Tôi lướt qua các bài viết và các video hướng dẫn về Blockchain, làm theo các hướng dẫn nhỏ và xử lý sự thất vọng ngày càng tăng của quá ít ví dụ.
Tôi thích học bằng cách làm. Nó buộc tôi phải giải quyết vấn đề ở cấp độ code, điều này khiến nó trở nên thân quen. Nếu bạn làm như vậy, đến phần cuối của hướng dẫn này, bạn sẽ có một Blockchain hoạt động được và sự hiểu biết vững chắc về cách chúng hoạt động.
Hãy nhớ rằng một blockchain là một chuỗi bất biến, tuần tự các hồ sơ được gọi là các khối (Block). Chúng có thể chứa các giao dịch, file hoặc bất kỳ dữ liệu nào bạn thích. Nhưng điều quan trọng là chúng được liên kết với nhau bằng cách sử dụng hàm băm.
Nếu bạn không chắc hash là gì, đây là lời giải thích.
Hướng dẫn này nhắm đến ai? Bạn có thể đọc và viết code Python cơ bản, cũng như có một số hiểu biết về cách thức hoạt động của HTTP request, vì chúng ta sẽ thao tác với Blockchain thông qua giao thức HTTP.
Tôi cần những gì? Đảm bảo rằng máy của bạn đã cài đặt Python 3.6 + (cùng với pip
). Bạn cũng sẽ cần cài đặt thư viện Flask và Request:
pip install Flask==0.12.2 requests==2.18.4
Ồ, bạn cũng sẽ cần một HTTP Client, như Postman hoặc cURL.
Source code ở đâu? Source code của chương trình có sẵn ở đây.
Mở trình soạn thảo văn bản hoặc IDE yêu thích của bạn, cá nhân tôi ❤️ PyCharm. Tạo một file mới là blockchain.py
. Chúng tôi sẽ chỉ sử dụng một file duy nhất, nhưng nếu bạn bị lạc, bạn luôn có thể tham khảo mã nguồn.
Chúng ta sẽ tạo một lớp Blockchain
có hàm khởi tạo tạo ra một danh sách trống ban đầu (để lưu trữ Blockchain của chúng ta) và một lớp khác để lưu trữ các giao dịch. Đây là bản thiết kế cho lớp Blockchain
của chúng ta:
class Blockchain(object):
def __init__(self):
self.chain = []
self.current_transactions = []
def new_block(self):
# Creates a new Block and adds it to the chain
pass
def new_transaction(self):
# Adds a new transaction to the list of transactions
pass
@staticmethod
def hash(block):
# Hashes a Block
pass
@property
def last_block(self):
# Returns the last Block in the chain
pass
Lớp Blockchain
của chúng ta chịu trách nhiệm quản lý chuỗi. Nó sẽ lưu trữ các giao dịch và có một số phương pháp trợ giúp để thêm các khối mới vào chuỗi. Hãy bắt đầu tìm hiểu một số phương thức.
Mỗi khối (block) sẽ có các thông tin sau: chỉ mục, mốc thời gian (theo thời gian Unix), danh sách các giao dịch, bằng chứng công việc (sẽ nói chi tiết ở phần dưới của bài viết) và mã băm của khối trước đó.
Dưới đây là ví dụ về một khối (block) trông như thế nào:
block = {
'index': 1,
'timestamp': 1506057125.900785,
'transactions': [
{
'sender': "8527147fe1f5426f9dd545de4b27ee00",
'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
'amount': 5,
}
],
'proof': 324984774000,
'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
Tại thời điểm này, ý tưởng về một chuỗi phải rõ ràng — mỗi khối mới chứa bên trong nó hàm băm của khối trước đó. Điều này rất quan trọng bởi vì nó là thứ mang lại tính bất biến của blockchains: Nếu kẻ tấn công làm hỏng một khối trước đó trong chuỗi thì tất cả các khối tiếp theo sẽ chứa hàm băm không chính xác.
Điều này có nghĩa không? Nếu không, hãy dành một chút thời gian để nghĩ về nó — đó là ý tưởng cốt lõi đằng sau blockchain.
Chúng ta sẽ cần một cách để thêm các giao dịch vào một khối. Phương thức new_transaction()
sẽ chịu trách nhiệm về điều này và nó khá dễ hiểu:
class Blockchain(object):
...
def new_transaction(self, sender, recipient, amount):
"""
Creates a new transaction to go into the next mined Block
:param sender: <str> Address of the Sender
:param recipient: <str> Address of the Recipient
:param amount: <int> Amount
:return: <int> The index of the Block that will hold this transaction
"""
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
Sau khi phương thức new_transaction()
thêm giao dịch vào danh sách, nó trả về chỉ mục của khối mà giao dịch sẽ được thêm vào. Điều này sẽ hữu ích sau này đối với người dùng gửi giao dịch.
Khi lớp Blockchain
được khởi tạo, chúng ta sẽ cần thêm khối genesis (khối gốc) vào chuỗi. Chúng ta cũng sẽ cần thêm một “bằng chứng” vào khối gốc, đó là kết quả của việc khai thác (hoặc bằng chứng công việc). Chúng ta sẽ nói thêm về khai thác ở phần sau.
Ngoài việc tạo ra khối gốc trong phương thức khởi tạo, chúng ta cũng sẽ tạo các phương thức new_block()
, new_transaction()
và hash()
như sau:
import hashlib
import json
from time import time
class Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []
# Create the genesis block
self.new_block(previous_hash=1, proof=100)
def new_block(self, proof, previous_hash=None):
"""
Create a new Block in the Blockchain
:param proof: <int> The proof given by the Proof of Work algorithm
:param previous_hash: (Optional) <str> Hash of previous Block
:return: <dict> New Block
"""
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'proof': proof,
'previous_hash': previous_hash or self.hash(self.chain[-1]),
}
# Reset the current list of transactions
self.current_transactions = []
self.chain.append(block)
return block
def new_transaction(self, sender, recipient, amount):
"""
Creates a new transaction to go into the next mined Block
:param sender: <str> Address of the Sender
:param recipient: <str> Address of the Recipient
:param amount: <int> Amount
:return: <int> The index of the Block that will hold this transaction
"""
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
@property
def last_block(self):
return self.chain[-1]
@staticmethod
def hash(block):
"""
Creates a SHA-256 hash of a Block
:param block: <dict> Block
:return: <str>
"""
# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
Từ đầu tới giờ tôi đã thêm một sốcomment và docstrings để giúp giữ cho code rõ ràng. Chúng ta đã sắp hoàn thành việc xây dựng blockchain của mình. Nhưng tại thời điểm này, bạn phải tự hỏi làm thế nào các khối mới được tạo ra, khai thác.
Thuật toán Proof of Work (PoW) là cách các khối mới được tạo hoặc khai thác trên blockchain. Mục tiêu của PoW là tìm ra một con số để giải quyết một vấn đề. Số phải khó tìm nhưng dễ xác minh — bởi bất kỳ ai trên mạng. Đây là ý tưởng cốt lõi đằng sau Proof of Work (bằng chứng công việc).
Chúng ta sẽ xem xét một ví dụ rất đơn giản để giúp hiểu sâu về điều này.
Hãy quyết định rằng mã băm của số nguyên x * y
phải kết thúc bằng 0
. Nó sẽ trông như thế này hash(x * y) = ac23dc...0
. Đối với ví dụ đơn giản này, hãy thử với x = 5
. Triển khai điều này bằng Python:
from hashlib import sha256
x = 5
y = 0 # We don't know what y should be yet...
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
y += 1
print(f'The solution is y = {y}')
Giải pháp ở đây là y = 21
. Kể từ đó, hàm băm được tạo ra kết thúc bằng 0
:
hash(5 * 21) = 1253e9373e...5e3600155e860
Trong Bitcoin, thuật toán Proof of Work được gọi là Hashcash. Và nó không quá khác so với ví dụ cơ bản của chúng tôi ở trên. Đó là thuật toán mà các thợ đào cần giải quyết để tạo ra một khối mới.
Nói chung, độ khó được xác định bởi số lượng ký tự được tìm kiếm trong một chuỗi. Những người khai thác sau đó được thưởng cho giải pháp của họ bằng cách nhận được một coin - trong một giao dịch.
Mạng có thể dễ dàng xác minh giải pháp của họ.
Hãy triển khai một thuật toán tương tự cho blockchain của chúng ta. Quy tắc của chúng ta sẽ tương tự như ví dụ trên:
Tìm một số p mà khi băm với mã băm của khối trước đó tạo ra một mã băm có 4 số 0
đứng đầu.
import hashlib
import json
from time import time
from uuid import uuid4
class Blockchain(object):
...
def proof_of_work(self, last_proof):
"""
Simple Proof of Work Algorithm:
- Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
- p is the previous proof, and p' is the new proof
:param last_proof: <int>
:return: <int>
"""
proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
return proof
@staticmethod
def valid_proof(last_proof, proof):
"""
Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
:param last_proof: <int> Previous Proof
:param proof: <int> Current Proof
:return: <bool> True if correct, False if not.
"""
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == "0000"
Để điều chỉnh độ khó của thuật toán, chúng ta có thể sửa đổi số lượng các số 0 đứng đầu. Nhưng 4 là đủ. Bạn sẽ nhận ra rằng việc thêm một số 0 ở đầu tạo ra sự khác biệt lớn so với thời gian cần thiết để tìm ra giải pháp.
Lớp Blockchain
của chúng ta gần như đã hoàn thành và đã sẵn sàng để bắt đầu tương tác với nó bằng cách sử dụng các HTTP request.
Chúng ta sẽ sử dụng Python Flask Framework. Đó là một micro-framework và nó giúp dễ dàng ánh xạ các điểm cuối (endpoint) đến các hàm Python. Điều này cho phép chúng ta làm việc với blockchain bằng cách sử dụng các HTTP request.
Chúng ta sẽ tạo ba điểm cuối:
/transactions/new
để tạo một giao dịch mới cho một khối./mine
để yêu cầu máy chủ của chúng ta khai thác một khối mới./chain
để trả lại toàn bộ Blockchain.“Máy chủ” của chúng ta sẽ tạo thành một nút duy nhất trong mạng blockchain của chúng ta. Hãy tạo bộ khung code như sau:
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask
class Blockchain(object):
...
# Instantiate our Node
app = Flask(__name__)
# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace('-', '')
# Instantiate the Blockchain
blockchain = Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
return "We'll mine a new Block"
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
return "We'll add a new transaction"
@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Giải thích ngắn gọn về những gì chúng ta đã thêm ở trên:
Blockchain
./mine
sử dụng phương thức GET
./transactions/new
sử dụng phương thức POST
, vì chúng ta sẽ gửi dữ liệu đến đó./chain
sử dụng phương thức GET
để trả về toàn bộ Blockchain.Dưới đây là dữ liệu (nội dung) của một yêu cầu cho một giao dịch. Đó là những gì người dùng gửi đến máy chủ:
{
"sender": "my address",
"recipient": "someone else's address",
"amount": 5
}
Vì chúng ta đã có phương thức để thêm giao dịch vào một khối, nên phần còn lại rất dễ dàng. Hãy viết hàm để thêm giao dịch:
import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
# Check that the required fields are in the POST'ed data
required = ['sender', 'recipient', 'amount']
if not all(k in values for k in required):
return 'Missing values', 400
# Create a new Transaction
index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
response = {'message': f'Transaction will be added to Block {index}'}
return jsonify(response), 201
Điểm cuối khai thác của chúng ta là nơi điều kỳ diệu xảy ra và thật dễ dàng. Nó phải làm ba điều:
import hashlib
import json
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/mine', methods=['GET'])
def mine():
# We run the proof of work algorithm to get the next proof...
last_block = blockchain.last_block
last_proof = last_block['proof']
proof = blockchain.proof_of_work(last_proof)
# We must receive a reward for finding the proof.
# The sender is "0" to signify that this node has mined a new coin.
blockchain.new_transaction(
sender="0",
recipient=node_identifier,
amount=1,
)
# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(proof, previous_hash)
response = {
'message': "New Block Forged",
'index': block['index'],
'transactions': block['transactions'],
'proof': block['proof'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
Lưu ý rằng người nhận khối đã khai thác là địa chỉ của nút của chúng ta. Và hầu hết những gì chúng ta đã làm ở đây chỉ là tương tác với các phương thức trên lớp Blockchain của chúng ta. Tại thời điểm này, mọi thứ đã hoàn tất và chúng ta có thể bắt đầu tương tác với blockchain của chúng ta.
Bạn có thể sử dụng cURL hoặc Postman để tương tác với API qua giao thức HTTP.
Khởi động máy chủ:
$ python blockchain.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Hãy thử khai thác một khối bằng cách đưa ra gửi một yêu cầu GET
tới điểm cuối http://localhost:5000/mine
như sau:
Hãy tạo một giao dịch mới bằng cách thực hiện một yêu cầu POST
tới điểm cuối http://localhost:5000/transactions/new
với nội dung giao dịch như sau:
Nếu bạn không sử dụng Postman, thì bạn có thể gửi yêu cầu bằng cách sử dụng cURL như sau:
$ curl -X POST -H "Content-Type: application/json" -d '{
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5
}' "http://localhost:5000/transactions/new"
Tôi khởi động lại máy chủ của mình và khai thác hai khối, tổng cộng là 3 khối. Hãy kiểm tra toàn bộ chuỗi bằng cách yêu cầu tới điểm cuối http://localhost:5000/chain
:
{
"chain": [
{
"index": 1,
"previous_hash": 1,
"proof": 100,
"timestamp": 1506280650.770839,
"transactions": []
},
{
"index": 2,
"previous_hash": "c099bc...bfb7",
"proof": 35293,
"timestamp": 1506280664.717925,
"transactions": [
{
"amount": 1,
"recipient": "8bbcb347e0634905b0cac7955bae152b",
"sender": "0"
}
]
},
{
"index": 3,
"previous_hash": "eff91a...10f2",
"proof": 35089,
"timestamp": 1506280666.1086972,
"transactions": [
{
"amount": 1,
"recipient": "8bbcb347e0634905b0cac7955bae152b",
"sender": "0"
}
]
}
],
"length": 3
}
Thật tuyệt vời. Chúng ta đã có một Blockchain cơ bản chấp nhận các giao dịch và cho phép chúng ta khai thác các khối mới. Nhưng một ưu điểm to lớn của Blockchain là chúng có thể phân cấp. Và nếu chúng được phân cấp, làm thế nào chúng ta đảm bảo rằng chúng đều phản ánh cùng một chuỗi? Đây được gọi là vấn đề Đồng thuận và chúng ta sẽ phải triển khai thuật toán đồng thuận nếu chúng ta muốn có nhiều hơn một nút trong mạng của mình.
Trước khi có thể triển khai thuật toán đồng thuận, chúng ta cần một cách để cho một nút biết về các nút lân cận trên mạng. Mỗi nút trên mạng của chúng ta phải giữ một sổ đăng ký của các nút khác trên mạng. Do đó, chúng ta sẽ cần thêm một số điểm cuối:
/nodes/register
để chấp nhận danh sách các nút mới ở dạng URL./nodes/resolve
để triển khai thuật toán đồng thuận của chúng ta, giải quyết mọi xung đột — để đảm bảo một nút có chuỗi chính xác.Chúng tôi sẽ cần sửa đổi phương thức khởi tạo của Blockchain và cung cấp phương thức đăng ký các nút:
...
from urllib.parse import urlparse
...
class Blockchain(object):
def __init__(self):
...
self.nodes = set()
...
def register_node(self, address):
"""
Add a new node to the list of nodes
:param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
:return: None
"""
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)
Lưu ý rằng chúng ta đã sử dụng set()
để lưu trữ danh sách các nút. Đây là một cách đơn giản để đảm bảo rằng việc thêm các nút mới là không quan trọng - nghĩa là bất kể chúng ta thêm một nút cụ thể bao nhiêu lần, nó sẽ xuất hiện đúng một lần.
Như đã đề cập, xung đột là khi một nút có một chuỗi khác với nút khác. Để giải quyết vấn đề này, chúng ta sẽ đưa ra quy tắc rằng chuỗi hợp lệ dài nhất là có thẩm quyền. Nói cách khác, chuỗi dài nhất trên mạng là chuỗi thực tế. Sử dụng thuật toán này, chúng ta đạt được sự Đồng thuận giữa các nút trong mạng của chúng ta.
...
import requests
class Blockchain(object)
...
def valid_chain(self, chain):
"""
Determine if a given blockchain is valid
:param chain: <list> A blockchain
:return: <bool> True if valid, False if not
"""
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
print(f'{last_block}')
print(f'{block}')
print("\n-----------\n")
# Check that the hash of the block is correct
if block['previous_hash'] != self.hash(last_block):
return False
# Check that the Proof of Work is correct
if not self.valid_proof(last_block['proof'], block['proof']):
return False
last_block = block
current_index += 1
return True
def resolve_conflicts(self):
"""
This is our Consensus Algorithm, it resolves conflicts
by replacing our chain with the longest one in the network.
:return: <bool> True if our chain was replaced, False if not
"""
neighbours = self.nodes
new_chain = None
# We're only looking for chains longer than ours
max_length = len(self.chain)
# Grab and verify the chains from all the nodes in our network
for node in neighbours:
response = requests.get(f'http://{node}/chain')
if response.status_code == 200:
length = response.json()['length']
chain = response.json()['chain']
# Check if the length is longer and the chain is valid
if length > max_length and self.valid_chain(chain):
max_length = length
new_chain = chain
# Replace our chain if we discovered a new, valid chain longer than ours
if new_chain:
self.chain = new_chain
return True
return False
Phương thức valid_chain()
chịu trách nhiệm kiểm tra xem một chuỗi có hợp lệ hay không bằng cách duyệt qua từng khối và xác minh cả mã băm và bằng chứng.
Phương thức resolve_conflicts()
duyệt qua tất cả các nút lân cận của chúng ta, tải xuống chuỗi của chúng và xác minh chúng bằng phương thức trên. Nếu tìm thấy một chuỗi hợp lệ có chiều dài lớn hơn chuỗi của chúng ta, chúng ta sẽ sử dụng chuỗi này để thay thế chuỗi của chúng ta.
Hãy đăng ký hai điểm cuối vào API của chúng ta, một điểm cuối để thêm các nút lân cận và một điểm cuối để giải quyết xung đột:
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
values = request.get_json()
nodes = values.get('nodes')
if nodes is None:
return "Error: Please supply a valid list of nodes", 400
for node in nodes:
blockchain.register_node(node)
response = {
'message': 'New nodes have been added',
'total_nodes': list(blockchain.nodes),
}
return jsonify(response), 201
@app.route('/nodes/resolve', methods=['GET'])
def consensus():
replaced = blockchain.resolve_conflicts()
if replaced:
response = {
'message': 'Our chain was replaced',
'new_chain': blockchain.chain
}
else:
response = {
'message': 'Our chain is authoritative',
'chain': blockchain.chain
}
return jsonify(response), 200
Tại thời điểm này, bạn có thể lấy một máy tính khác nếu muốn và tạo các nút khác nhau trên mạng của mình. Hoặc sử dụng cổng khác nhau trên cùng một máy tính. Tôi tạo một nút khác trên máy của mình, trên một cổng khác và đăng ký nó với nút hiện tại của tôi. Do đó, tôi có hai nút: http://localhost:5000
và http://localhost:5001
.
Sau đó, tôi khai thác một số Khối mới trên nút 2, để đảm bảo chuỗi dài hơn. Sau đó, tôi đã gọi GET /nodes/resolve
vào nút 1, nơi chuỗi được thay thế bằng Thuật toán đồng thuận:
Và đó là một kết thúc ... Hãy cùng một số bạn bè để giúp kiểm tra Blockchain của bạn.
Tôi hy vọng rằng điều này đã truyền cảm hứng cho bạn để tạo ra một cái gì đó mới. Tôi vui mừng về Tiền điện tử vì tôi tin rằng Blockchains sẽ nhanh chóng thay đổi cách chúng ta nghĩ về các nền kinh tế, chính phủ và lưu trữ hồ sơ.
Bạn có thể vui lòng tắt trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi duy trì hoạt động của trang web.
Hướng dẫn về Blockchain cho người mới bắt đầu. Tìm hiểu công nghệ Blockchain là gì?
Mã hóa khóa công khai trong Blockchain là gì? Hai tính năng quan trọng của mã hóa khóa công khai.
Blockchain là gì? Chi tiêu kép là gì? Tại sao blockchain lại được sử dụng trong Bitcoin. Bài viết này sẽ giúp bạn.