Nói đến Bitcoin có lẽ ai cũng biết, nhưng không phải ai cũng biết đến Blockchain, công nghệ sử dụng để xây dựng đồng tiền ảo mạnh nhất thế giới đến thời điểm hiện tại. Theo cafef:
Blockchain là công nghệ lưu trữ và truyền tải thông tin bằng các khối được liên kết với nhau và mở rộng theo thời gian do đó được gọi là chuỗi khối (blockchain). Mỗi khối chứa đựng các thông tin về thời gian khởi tạo và được liên kết với các khối trước đó. Blockchain được thiết kế để chống lại sự thay đổi dữ liệu. Thông tin trong blockchain không thể bị thay đổi và chỉ được bổ sung thêm khi có sự đồng thuận của tất cả các nút trong hệ thống. Ngay cả khi nếu một phần của hệ thống blockchain sụp đổ, những máy tính và nút khác sẽ tiếp tục hoạt động để bảo vệ thông tin.
Bạn có thể tìm hiểu thêm về Blockchain và Bitcoin từ nhiều nguồn trên internet, cả tiếng Anh và tiếng Việt, ví dụ như từ bài viết này trên genk hoặc video này từ Diễn đàn kinh tế thế giới (World Economic Forum). Trong phạm vi bài viết này, mình sẽ tiến hành xây dựng một hệ thống Blockchain đơn giản, theo hướng dẫn từ bài viết Learn Blockchains by Building One của tác giả Daniel van Flymen được đăng tải trên medium. Bạn có thể xem mã nguồn của bài viết tại github của mình (có chỉnh sửa sắp xếp lại so với mã nguồn gốc của Daniel)
Github: https://github.com/nvh95/blockchain_demo
Chuẩn bị
Trước hết, Blockchain là chuỗi các bản ghi bất biến, gọi là Block. Nó có thể bao gồm các giao dịch, file hoặc bất cứ kiểu dữ liệu nào. Một khái niệm quan trọng mà bạn cần biết trước khi bắt đầu đó là hash.
Bạn có thể hiểu hash là một ánh xạ từ x->y=f(x), sao cho mỗi x sẽ có duy nhất một y. Nếu chưa chắc chắn về hash, hãy xem thêm tại đây
Bài viết này sẽ sử dụng Python 3.6+, Flask 0.12.2 và requests 2.18.4. Cài đặt Flask và requests sử dụng lệnh:
pip install Flask==0.12.2 requests==2.18.4
Các bạn có thể sử dụng bất cứ một IDE hay text editor nào để code, cá nhân mình chọn Pycharm vì nó dễ dùng, chức năng gợi ý code hỗ trợ viết code tốt.
Bước 1: Xây dựng Blockchain
Trước hết là xây dựng cấu trúc của Blockchain, mình sẽ xây dựng class Blockchain trong file blockchain.py
Dưới đây là khung của class Blockchain
class Blockchain(object): def __init__(self): self.chain = [] self.current_transaction = [] def new_block(self): """Create a new block and adds it the the chain""" pass 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> Index of block gold this transaction """ self.current_transaction.append({ 'sender': sender, 'recipient': recipient }) @staticmethod def hash(block): """Hash a block""" pass @property def last_block(self): """Returns the last block in the chain""" pass
Constructor của class này khởi tạo một list rỗng để chứa blockchain và một list khác để lưu các giao dịch. Class sẽ có chức năng lưu trữ các chain và một số hàm có một số chức năng như thêm một khối mới hoặc một giao dịch mới.
Cấu trúc của một khối
Mỗi một khối (block) sẽ có index, timestamp, danh sách các giao dịch và proof (sẽ nói chi tiết hơn ở sau) và hash của khối trước. Dưới đây là một khối ví dụ
block ={"index": 8, "previous_hash": "76c16ecf0b8e83cabae7e3c4e0c1898db0c31d7ed7a4e5de63d82cec4b4cc3db", "proof": 9675, "timestamp": 1507383586.468118, "transactions": [ { "amount": 1, "recipient": "19501b91d05a41f1915b6a2830a074c2", "sender": "0" } ]}
Hãy dành chút thời gian để suy nghĩ về việc mỗi một block chứa trong nó hash của block ngay trước đó. Điều này khiến cho blockchain có tính bất biến, nếu một block bị hacker thay đổi thì các block sau sẽ bị hỏng vì hash sẽ không còn đúng nữa, đồng nghĩa với việc có điều gì đó không ổn đã xảy ra.
Thêm một giao dịch vào Block
Chúng ta sẽ implement hàm new_transaction()
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> Index of block gold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1
Hàm này thêm một giao dịch vào list và trả về index của block mà giao dịch đó sẽ được thêm vào (là block tiếp theo được đào). Bạn sẽ hiểu rõ vấn đề hơn khi code chức năng đào một khối mới (mining-đào tiền)
Tạo khối mới
Khi khởi tạo Blockchain, chúng ta cần phải khởi tạo block genesis, đây là block đầu tiên (tham số proof sẽ được giải thích kỹ hơn ở phần sau)
class Blockchain(object): def __init__(self): self.current_transactions = [] self.chain = [] # Create the genesis block self.new_block(previous_hash=1, proof=100)
Chúng ta cũng implement các hàm new_block()
, new_transaction()
và hash()
. Code dưới đấy đi kèm comment khá dễ hiểu.
Cụ thể:
– Hàm new_block()
sẽ tạo ra một block mới và đẩy nó vào trong Blockchain.
– Hàm new_transaction()
tạo ra một giao dịch mới, sau này sẽ được lưu vào Block được đào tiếp theo.
– Hàm hash()
để tạo hash cho Block. Cụ thể ở đây sử dụng SHA-256
def new_block(self, proof, previous_hash=None): """ Create a new block and adds it the the chain :param proof: <int> The proof given by the Proof of Work algorithm :param previous_hash: (Optional) <str> hash of the previous block :return: <dict> a 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 transaction 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> Index of block gold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1 @staticmethod def hash(block): """ Hash a block using SHA-256 :param block: <dict> Block :return: <str> """ # The dictionary must be 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ì
Phần này sẽ mô tả cơ chế hoạt động của Bitcoin/Blockchain. Đây là phần khiến những người mới tìm hiểu cảm thấy khó hiểu nhất. Khi tra khảo trên mạng hoặc được người khác giải thích, bạn sẽ nghe thấy rằng: “Tiền ảo kiếm được là do chúng ta giải các bài toán phức tạp, sau khi giải được bài toán đó thì ta sẽ được… tặng tiền”.
Mình xin dám chắc phần lớn các bạn sẽ được giải thích trên và phản ứng của bạn sẽ là rất khó hiểu. Thế quái nào mà giải toán mà lại được tiền? Nghe vô lý thế, vậy thiên hạ đổ xô đi giải toán à? Hay bọn này lừa đảo nhỉ?
Trong phần này, mình sẽ giải đáp cho các bạn câu hỏi “Làm sao giải toán mà lại được tiền?” đi kèm ví dụ và mã nguồn minh hoạ để các bạn có thể tự cài đặt được.
Trước hết hãy nhắc lại về hash. Nó là một cách mã hoá hiểu nôm na như một hàm số. Cho hai đầu vào giống nhau thì sẽ luôn cho ra một kết quả giống nhau. Nhưng nếu có một đầu ra thì rất rất khó để có thể tìm lại được đầu vào. Bạn hãy xem lại ở đây (https://learncryptography.com/hash-functions/what-are-hash-functions).
Bài toán chúng ta đi tìm hiểu trong phần này là Proof of Work (PoW), nó là cách một khối của Blockchain được đào hoặc được tạo ra. Mục tiêu của chúng ta là tìm ra được lời giải của bài toán (thường là một con số). Tương tự, số đó phải dễ dàng kiểm chứng nhưng rất khó để tìm ra bởi bất cứ ai trong hệ thống.
Hãy lấy một ví dụ để làm rõ vấn đề này hơn. Giả sử ta có hai số x
và y
. Bài toán của chúng ta là hash của x*y phải kết thúc bởi số 0, cụ thể hash(x*y) = a2hc1w....0
. Để cho đơn giản. giả sử x = 5
. Hãy xem lời giải của bài toán qua đoạn code dứoi đây:
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}')
Đáp án là y = 21. Vì
hash(5 * 21) = 1253e9373e...5e3600155e860
Trong bitcoin thì thuật toán Proof of Work này gọi là Hashcash. Và nó cũng tương tự như ví dụ bên trên. Trong thực tế, các tay đào bitcoin phải giải những bài toán phức tạp hơn thế này gấp nhiều lần. Ví dụ như hash phải bắt đầu bằng chuỗi “123456” chẳng hạn (thực tế ví dụ này vẫn rất đơn giản và chỉ mất có 92 giây để tìm được lời giải). Việc một người giải được bài toán thì khó chứ để xác thực là người đó có giải được bài toán hay không thì rất đơn giản. Và để trả công cho người giải được thì người đó sẽ được thưởng một đơn vị tiền qua một giao dịch.
(Ví dụ vè một bài toán khó hơn, tìm (x,y)
sao cho hash(x*y)
bắt đầu bằng chuỗi "123456"
, có thể thấy ở file proof_of_work.py
trên repository của mình)
from hashlib import sha256 from time import time from random import randint x = randint(0,10000) y = 0 t1 = time() while (sha256(f'{x*y}'.encode()).hexdigest()[:6] != '123456'): print(y) y +=1 print("x = {}".format(x)) print("y = {}".format(y)) print(sha256(f'{x*y}'.encode()).hexdigest()) t2= time() print(t2-t1) # x = 5385 # y = 21069182 # hash(x*y) = 123456432081821574050d2ee4a7acef0f94a52a1011a77ad5e2f37543cf8629 # total_time_to_find_y = 92.40833306312561
Cài đặt Proof of Work
Xét bài toán tương tự: Tìm số p sao cho khi hash(pp')
thì 4 ký tự đầu đều là số 0
(p'
cho trước-là proof_of_work của block trước)
def proof_of_work(self, last_proof): """ Simple proof of work algorithm: - Find a number p' satisfied valid_proof() function - p is the previus proof, p' is the new proof :param last_proof: <int> :return: <int> new poof """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): """ Validate the proof if: - hash(last_proof, proof) contains 4 leading zeroes :param last_proof: <int> previous proof :param proof: <int> current proof :return: <bool> true or false """ guess = f'{last_proof*proof}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == '0000'
Bài toán của bạn có thể thay đổi để tăng độ khó lên. Bạn có thể thay đổi một cách đơn giản.
Đến đây, chúng ta đã implement gần xong phần lõi của hệ thống Blockchain rồi. Tiếp đến sẽ là phần tương tác để sử dụng hệ thống này qua HTTP.
Bước 2: Xây dựng API
Phần này chúng ta sẽ sử dụng Flask, framework khá đơn giản và dễ dùng sẽ giúp chúng ta có thể tiến hành các thao tác với hệ thống Blockchain vừa xây dựng.
Chúng ta sẽ cài đặt 3 method:
/transactions/new
to create a new transaction to a block/mine
to tell our server to mine a new block./chain
to return the full Blockchain.
Setting up Flask
Để thuận lợi hãy tạo file server.py và dựng khung chương trình
from uuid import uuid4 from blockchain import Blockchain from flask import Flask, jsonify # 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 will mind a new Block" @app.route('/transaction/new', methods=['POST']) def new_transaction(): return "Well will 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__': from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('-p', '--port', default=5000, type=int, help='port listening') args = parser.parse_args() port = args.port app.run(host='0.0.0.0', port=port)
Tóm tắt đoạn code trên:
- Dòng 8 : Khởi tạo Flask
- Dòng 11: Tạo tên ngẫu nhiên cho node.
- Dòng 14: Khởi tạo Blockchain class.
- Dòng 16–8: Tạo
/mine
endpoint, là GET request. - Dòng 20–22: Tạo
/transactions/new
endpoint, là POST request, chúng ta sẽ gửi dữ liệu đến đó. - Dòng 24-30: Tạo
/chain
endpoint, trả về full Blockchain. - Dòng 32-38: Chạy server.
The Transactions Endpoint
Một request lên server sẽ có dạng như sau:
{ "sender": "my address", "recipient": "someone else's address", "amount": 5 }
Chúng ta đã code hàm để thêm một giao dịch vào block. Công việc còn lại khá đơn giản:
@app.route('/transactions/new', methods=['POST']) def new_transaction(): values = request.get_json() print(values) # 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 parameters', 400 # Crate 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
The Mining Endpoint
Mining Endpoint là nơi các hoạt động đào được diễn ra. Nó sẽ phụ trách 3 việc sau:
- Tính toán Proof Of Work
- Thưởng cho người thành công 1 đồng tiền
- Tạo ra một block mới và thêm vào chain
@app.route('/mine', methods=['GET']) def mine(): # Run 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 block = blockchain.new_block(proof) response = { 'message': 'New Block Forged', 'index': block['index'], 'transactions': block['transactions'], 'proof': block['proof'], 'previous_hash': block['previous_hash'], } return jsonify(response), 200
Ta có thể thấy rằng địa chỉ người nhận là địa chỉ của chúng ta, người gửi là 0, là hệ thống.
Bước 3: Tương tác với hệ thống
Phần này mình sử dụng Postman để minh hoạ hệ thống. Trước hết, hãy chạy server bằng lệnh:
$ python blockchain.py --port 5000 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Chúng ta hãy bắt đầu bằng việc đào (mine) bằng cách gửi một request GET đến http://localhost:5000/mine
. Bạn có thể nhìn thấy bạn đã được thưởng 1 coin, và một giao dịch được khởi tạo với người nhận là bạn, ngưởi gửi là hệ thống (hệ thống có địa chỉ là 0)
Hãy tạo một giao dịch mới bằng việc gửi một request POST đến http://localhost:5000/transactions/new
với nội dung như hình.
Hãy gửi một request GET đến http://localhost:5000/chain
để xem toàn bộ nội dung của dãy chain.
Bước 4: Consensus
Chúng ta đã đi được một chặng đường tương đối. Đã xây dựng được một hệ thống Blockchain cơ bản, chạy được, có thể tiến hành đào và giao dịch. Tuy nhiên, một đặc điểm quan trọng của các hệ thống Blockchain đó là tính phân tán. Có nghĩa là hệ thống này sẽ được lưu trữ ở nhiều thiết bị khác nhau. Điều này nảy sinh một vấn đề đó là làm sao tất cả các nơi khác lưu trữ hệ thống này đều có chuối blockchain giống nhau. Câu hỏi này đươc đặt ra và có tên là Problem of Consensus. Trong phần này, chúng ta sẽ cài đặt thuật toán Consensus để giải quyết vấn đề khi trong mạng cho hơn hai node.
Register node mới
Để có thể cài đặt được thuật toán Consensus, chúng ta phải khiến các node trong mạng biết đến sự tồn tại của nhau. Mỗi node cần lưu trữ thông tin của các node khác. Chúng ta sẽ tiến hành cài đặt thêm hai endpoint:
/nodes/register
làm công việc giúp các node biết đến sự tồn tại của các nút khác/nodes/resolve
cài đặt thuật toán Consensus, giải quyết xung đột để chắc chắn rằng tất cả các node đều có chung một chuỗi Blockchain
Chúng ta cần sửa một chút constructor và cung cấp một method để register các node:
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 od node (e.g: http://192.168.0.2:5000') :return: None """ parsed_url = urlparse(address) self.nodes.add(parsed_url.netloc)
Chúng ta sử dụng cấu trúc dữ liệu set()
để lưu trữ các node vì tính chất của set()
chỉ lưu trữ các phần tử khác nhau nên chúng ta có register node nhiều lần cũng không vấn đề gì.
Cài đặt thuật toán Consensus
Trong một mạng Blockchain, xung đột xảy ra khi các node trong mạng không có chung một chuỗi Blockchain. Để giải quyết vấn đề này, chúng ta sẽ sử dụng luật chuỗi nào dài nhất là chuối hợp lệ.
class Blockchain(object) ... @staticmethod def valid_proof(last_proof, proof): """ Validate the proof if: - hash(last_proof, proof) contains 4 leading zeroes :param last_proof: <int> previous proof :param proof: <int> current proof :return: <bool> true or false """ guess = f'{last_proof*proof}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == '0000' 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 has 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): """ Consensus Algorithm: resolves conflicts by replacing our chain with the longest one in the network :return: <bool> True if our blockchain was replaced, False if not """ neighbors = self.nodes new_chain = None # We're only looking for chains longer than ours max_length = len(self.chain) # Downloads and Verify the chains from all the nodes in our networks for node in neighbors: 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 there is a new, valid chain longer than ours if new_chain: self.chain = new_chain return True return False
Method valid_chain()
chịu trách nhiệm cho việc kiểm tra một chain có hợp lệ hay không
Method resolve_conflicts()
kiểm tra xem các node trong mạng có hợp lệ hay không. Nếu một chain hợp lệ và dài hơn chain hiện tại thì nó sẽ tiến hành thay thế chain hiện tại.
Chúng ta tiến hành thêm 2 endpont API như đã nói bên trên
@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
Đến đây bạn có thể lấy một máy tính khác để tạo thành một node mới, hoặc có thể chạy chương trình trên các cổng khác nhau. Mình sẽ chạy thêm chương trình với cổng 5001, sau đó register.
Mình sẽ tiến hành đào một vài coin ở node mới tạo (cổng 5001).
Sau đó gọi /nodes/resolve
ở node 1 và thấy rằng chuỗi chain ở node 5000 đã được thay thế bởi chuỗi chain ở node 5001 (dài hơn và hợp lệ)
Vậy là đã xong. Bạn đã tự tay xây dựng được cho mình một hệ thống Blockchain đơn giản với những chức năng cơ bản nhất. Hi vọng bài viết sẽ giúp bạn hiểu hơn về Blockchain, một trong những công nghệ mới và hot nhất hiện nay.
P.S: Sau khi tự xây dựng được một hệ thống Blockchain, để hiểu hơn về cấu trúc thiết kế của đồng tiền ảo Bitcoin, mình gợi ý mọi người hãy xem paper Bitcoin: A Peer-to-Peer Electronic Cash System của Satoshi Nakamoto được công bố vào năm 2008. Bạn sẽ thấy được hệ thống Bitcoin được thiết kế phức tạp hơn hệ thống của chúng ta nhiều lần. Tuy nhiên, bạn sẽ thấy những khái niệm quan trọng tương tự xuất hiện trong bài báo như transactions, proof of work, phân phối nodes trong network, mining gold (trong phần 6. Incentive)…
Bài viết này sẽ sử dụng Python 3.6+, Flask 0.12.2 và requests 2.18.4. Cài đặt Flask và requests sử dụng lệnh:
pip install Flask==0.12.2 requests==2.18.4
Mình cài Python okay, Flask và requests không hiểu lắm, bạn cho link tải nha.
Cái này mình có thể cài đặt trên máy win chạy được không? hay phải linux?
Thanks.
ThíchThích
Chạy trên windows được bạn nhé. Bạn cứ mở cmd lên và gõ lệnh pip install Flask==0.12.2 requests==2.18.4 vào thôi. Nếu bị lỗi thì bạn hãy mở cmd bằng quyền Administrator rồi chạy lại lệnh trên là được.
ThíchThích
Mình chạy thử request POST đến http://localhost:5000/transactions/new
thông tin:
{
“sender”: “02a63a8be75745debb1f8a5bff83ed43”,
“recipient”: “12a63a8be75745debb1f8a5bff83ed43”,
“amount”: 5
}
nó báo:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
Vậy là sao hả bạn?
ThíchThích
Bạn kiểm tra lại trong post request header chứa “Content-Type”: “application/json” và body là raw, JSON/(application/json) nhé
ThíchThích
Bạn ơi , bạn có thể cho mình xin fb : Trao đổi thêm đc ko ?
ThíchThích
Cảm ơn anh về bài viết rất rõ ràng cho người mới bắt đầu như em, em có một số thắt mắc mong được giải đáp:
Nếu ứng dụng blockchain vào các lĩnh vực khác thì PoW có quan trọng không, và có thể bỏ qua nó không.
Em chưa hiểu lắm về cách lưu trữ các dữ liệu của blockchain và làm sao để các transaction có thể đồng nhất với tất cả máy khác trong mạng lưới blockchain.
ThíchThích
sao mình clone code về rồi chạy python blockchain.py –port 5000 nó báo lỗi như thế này vậy b:
File “blockchain.py”, line 112
guess = f'{last_proof*proof}’.encode()
^
SyntaxError: invalid syntax
ThíchThích
lỗi cú pháp
ThíchThích
Bài viết chi tiết lắm, cảm ơn bạn
ThíchThích
Cho mình hỏi : Ở bước số 3 đoạn POST sender là cái địa chỉ recipient ở đoạn GET mine. Vậy recipient là địa chỉ nào lấy ở đâu.
Thanks
ThíchThích