298 lượt xem

Tự xây dựng một hệ thống Blockchain đơn giản


Nói đến Bitcoin có lẽ rằng ai cũng biết, nhưng không phải ai cũng biết đến Blockchain, công nghệ tiên tiến sử dụng để xây dựng đồng xu tiền ảo mạnh nhất quốc tế đến thời gian 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 những bản ghi không bao giờ thay đổi, gọi là Block. Nó hoàn toàn có thể gồm có những thanh toán giao dịch, file hoặc bất kỳ kiểu tài liệu nào. Một khái niệm quan trọng mà bạn cần biết trước khi mở màn đó là hash .

Bạn hoàn toà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 như đinh 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 hoàn toàn có thể sử dụng bất kỳ một IDE hay text editor nào để code, cá thể mình chọn Pycharm vì nó dễ dùng, công dụng gợi ý code tương hỗ 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:  Address of the sender
        :param recipient:  Address of the recipient
        :param amount:   Amount
        :return:  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 những thanh toán giao dịch. Class sẽ có tính năng tàng trữ những chain và 1 số ít hàm có một số ít tính năng như thêm một khối mới hoặc một thanh toán 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, list những thanh toán giao dịch và proof ( sẽ nói chi tiết cụ thể 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 hạn để tâm lý 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 không bao giờ thay đổi, nếu một block bị hacker biến hóa thì những block sau sẽ bị hỏng vì hash sẽ không còn đúng nữa, đồng nghĩa tương quan 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:  Address of the sender
        :param recipient:  Address of the recipient
        :param amount:   Amount
        :return:  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 thanh toán giao dịch vào list và trả về index của block mà thanh toán giao dịch đó sẽ được thêm vào ( là block tiếp theo được đào ). Bạn sẽ hiểu rõ yếu tố hơn khi code công dụ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()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:  The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional)  hash of the previous block
        :return:  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:  Address of the sender
        :param recipient:  Address of the recipient
        :param amount:   Amount
        :return:  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:  Block
        :return: 
        """

        # 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ố xy. 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ựa như như ví dụ bên trên. Trong trong thực tiễn, những 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 khởi đầu bằng chuỗi “ 123456 ” ví dụ điển hình ( thực tiễn ví dụ này vẫn rất đơn thuầ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 nhận là người đó có giải được bài toán hay không thì rất đơn thuần. Và để trả công cho người giải được thì người đó sẽ được thưởng một đơn vị chức năng tiền qua một thanh toán 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: 
    :return:  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:  previous proof
    :param proof:  current proof
    :return:  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 tất cả chúng ta sẽ sử dụng Flask, framework khá đơn thuần và dễ dùng sẽ giúp tất cả chúng ta hoàn toàn có thể thực thi những thao tác với mạng lưới hệ thống Blockchain vừa xây dựng .
Chúng ta sẽ setup 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 thanh toán giao dịch vào block. Công việc còn lại khá đơn thuầ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 những hoạt động đào được diễn ra. Nó sẽ đảm nhiệm 3 việc sau :

  1.  Tính toán Proof Of Work
  2. Thưởng cho người thành công 1 đồng tiền
  3. 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 hoàn toàn có thể thấy rằng địa chỉ người nhận là địa chỉ của tất cả chúng ta, người gửi là 0, là mạng lưới 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ạ mạng lưới 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)
Screen Shot 2017-10-19 at 12.53.53 AM.png
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.
Screen Shot 2017-10-19 at 12.58.10 AM.png
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.
Screen Shot 2017-10-19 at 12.59.40 AM.png

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

Để hoàn toàn có thể setup được thuật toán Consensus, tất cả chúng ta phải khiến những node trong mạng biết đến sự sống sót của nhau. Mỗi node cần tàng trữ thông tin của những node khác. Chúng ta sẽ thực thi thiết lập thêm hai endpoint :

  1. /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
  2. /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 ít constructor và phân phối một method để register những 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:  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:  previous proof
        :param proof:  current proof
        :return:  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:  A blockchain
        :return:  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:  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 thực thi 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.
Screen Shot 2017-10-19 at 12.48.54 AM.png
Mình sẽ tiến hành đào một vài coin ở node mới tạo (cổng 5001).
Screen Shot 2017-10-19 at 12.49.26 AM.png
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ệ)
Screen Shot 2017-10-19 at 12.49.51 AM.png

Vậy là đã xong. Bạn đã tự tay xây dựng được cho mình một mạng lưới hệ thống Blockchain đơn thuần với những tính 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ệ tiên tiến mới và hot nhất lúc bấy giờ .

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)…

Chia sẻ:

Thích bài này:

Thích

Đang tải …

Source: https://trade.edu.vn
Category: Blockchain

Vote sao

Trả lời

Email của bạn sẽ không được hiển thị công khai.