Tự Xây Dựng Blockchain Đơn Giản Bằng Python

Cách nhanh nhất để tìm hiểu cách hoạt động của Blockchain là tự tay xây dựng nó.

Bạn bị choáng ngợp bởi sự tăng giá của tiền điện tử Bitcoin? Bạn muốn “hiểu rõ” cách hoạt động của Blockchain – công nghệ nền tảng đằng sau Bitcoin nhưng mọi thứ chỉ giới hạn ở việc “biết” vì chỉ có thể lướt qua các bài viết và video hướng dẫn về Blockchain mà không có ví dụ cụ thể.

Bài viết này sẽ cùng bạn tạo ra 1 blockchain của chính bạn. Nếu làm theo đến phần cuối của hướng dẫn này, bạn sẽ tự khởi tạo được 1 blockchain hoạt động được và nắm được cách chúng hoạt động.

Trước khi bắt đầu…

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”. 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 (hash).

Nếu bạn chưa nắm rõ sơ lược về Blockchain, đây là bài viết dành cho bạn.

Hướng dẫn dành cho ai? Những bạn có thể đọc và viết code Python cơ bản và có hiểu biết về HTTP request, vì chúng ta sẽ thao tác với Blockchain thông qua giao thức HTTP.

Cần chuẩn bị những gì? Python 3.6+ cùng với pip. Ngoài ra bạn cần cài đặt thư viện Request và Flask.

pip install Flask==0.12.2 requests==2.18.4 

Nếu bạn đang sử dụng môi trường Windows, bạn có thể truy cập trực tiếp vào python.org để tải xuống phiên bản Python mới nhất.

Xây Dựng Blockchain

Bước 1: Tạo Blockchain

Mở Text Editor hoặc IDE yêu thích của bạn, tạo 1 file mới là blockchain.py. Tham khảo mã nguồn tại đây. Đầu tiên là tạo lớp blockchain.

Giới thiệu về lớp Blockchain

Lớp blockchain chịu trách nhiệm quản lý chuỗi. Nó sẽ lưu trữ giao dịch và có một số phương pháp để thêm các block mới vào chuỗi. Chúng ta sẽ tạo 2 lớp blockchain. Lớp blockchain thứ nhất có hàm khởi tạo tạo ra 1 danh sách trống ban đầu để lưu trữ blockchain. Lớp còn lại sẽ lưu trữ giao dịch. Dưới đâ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

Xác định Block

Tiếp theo, chúng ta sẽ xác định các block trông như thế nào. Mỗi block sẽ có các thông tin sau:

Ví dụ về một block:

block = {
    'index': 1,
    'timestamp': 1506057125.900785,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

Khi xác định block trông như thế nào, ý tưởng về blockchain đã phải thật rõ ràng – mỗi block mới chứa hash function (hàm băm) của khối trước đó. Đây là yếu tố quyết định cho tính bất biến của blockchains. Nếu kẻ tấn công làm hỏng một block trước đó trong chuỗi thì tất cả các khối tiếp theo sẽ chứ hàm băm không chính xác.

Thêm giao dịch vào một block

Chúng ta sẽ sử dụng phương thức new_transaction() để thêm giao dịch vào 1 block:

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ề index của block mà giao dịch sẽ được thêm vào.

Tạo block mới

Khi lớp blockchain được khởi tạo, chúng ta sẽ cần thêm khối genesis (khối đầu tiên) vào chuỗi. Ngoài ra, chúng ta sẽ cần thêm một proof vào khối genesis. Chúng ta sẽ tạo các phương thức new_block(), new_transaction()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()

Proof-Of-Work là gì

Thuật toán PoW là cách các blockchain mới được tạo ra hoặc khai thác trên blockchain. Mục tiêu của PoW là tìm một con số (sao cho ai cũng có thể xác minh nhưng lại khó tìm ra) để giải quyết 1 vấn đề nào đó. Chúng ta sẽ xem xét 1 ví dụ đơn giản để hiểu sâu về vấn đề này.

Mã băm của số nguyên x * y phải kết thúc bằng 0. Khi đó, mã băm có dạng:

hash(x * y) = ac23dc...0. Giờ chúng ta sẽ thử với x=5.

Triển khai 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 PoW được gọi là Hashcash. Đây là thuật toán mà các miners cần giải quyết để tạo ra 1 block mới.

Độ khó ở đây là số lượng ký tự được tìm kiếm trong một chuỗi. Những miners sau đó sẽ nhận được 1 coin như là phần thưởng cho giải pháp của họ – trong một giao dịch.

Triển khai Bằng chứng công việc (Pow) cơ bản

Hãy triển khai một thuật toán tương tự cho blockchain của chúng ta. Quy tắc 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. Ở đây chúng ta sẽ để 4 số. Lớp blockchain của chúng ta gần như đã hoàn thành và đã có thể tưởng tác bằng cách sử dụng các HTTP Request.

Bước 2: Tích hợp API

Vậy là blockchain của chúng ta đã hoạt động, giờ chúng ta cần tạo 1 API để sử dụng blockchain hiệu quả.

Chúng ta sẽ sử dụng một micro-framework là Python Flask Framework. Framework này 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.

Cấu hình Flask

“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)
  • Dòng 15: Khởi tạo nút. Đọc thêm về Flask tại đây.
  • Dòng 18: Tạo một tên ngẫu nhiên cho nút.
  • Dòng 21: Khới tạo lớp Blockchain.
  • Dòng 24–26: Tạo điểm cuối /mine sử dụng phương thức GET.
  • Dòng 28–30: Tạo điểm cuối /transactions/new sử dụng phương thức POST, vì chúng ta sẽ gửi dữ liệu đến đó.
  • Dòng 32–38: Tạo điểm cuối /chain sử dụng phương thức GET để trả về toàn bộ Blockchain.
  • Dòng 40–41: Khởi chạy máy chủ trên cổng 5000.

Điểm cuối giao dịch

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 1 block, giờ chúng ta sẽ 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

  1. Tính toán Pow.
  2. Tạo block mới bằng cách thêm nó vào chuỗi (chain).
  3. Thưởng cho miner (ở đây chính là chúng ta) bằng cách thêm một giao dịch cấp cho chúng ta 1 coin.
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 block đã khai thác là địa chỉ nút của chúng ta. Hiện tại, mọi thứ đã hoàn tất và chúng ta có thể bắt đầu tương tác với blockchain.

Bước 3: Tương tác với Blockchain

Bước cuối cùng là tương tác với blockchain. Chúng tôi sẽ sử dụng Postman để tương tác với Blockchain bằng cách sử dụng API mà chúng ta vừa tạo.
Chạy code bên dưới trên Python Console:

$ python blockchain.py

Kết Luận

Vậy chúng ta đã đến phần cuối của hướng dẫn. Hy vọng rằng hướng dẫn này sẽ truyền cảm hứng cho bạn để bắt đầu một dự án mới, hoặc đơn giản là giúp bạn có thêm động lực khi bước vào thế giới blockchain.

Nguồn tham khảo:

101blockchains

comdy