比特币 (12):300行代码实现一个简化版比特币系统(二)

By | 2018-05-08
131,247 views | 9,903 Comments

这篇接着上一篇,在构建好区块链结构之后,怎么建立一个真正运行起来的区块节点和网络。
我们使用Python Flask框架,方便接收http请求,模拟不同节点之间的通信等。
我们将创建五个接口:
/transactions/new: 请求创建一个交易并添加到区块
/mine: 告诉服务器去挖掘新的区块,在实际的比特币系统中,挖矿节点时时刻刻都在挖矿,但是由于我们设置为没有难度,可以手工触发一下使其挖矿。
/chain: 返回整个区块链信息
/nodes/find_neighbors: 发现邻居节点
/nodes/resolve_conflicts: 使用最长链的共识解决冲突
这部分的代码在另一个python文件node.py中实现。

一、创建节点

主要实现三个函数,框架如下:

# -*- coding: utf-8 -*-
import time
from uuid import uuid4
from flask import Flask, request, jsonify
import block
import sys
import requests

app = Flask(__name__)
node_id = str(uuid4()).replace('-', '')
need_genesis = True if sys.argv[1] == "True" else False
blockchain = block.Blockchain(node_id, need_genesis)

@app.route("/mine", methods=["GET"])
def mine():

	pass

@app.route("/transactions/new", methods=["POST"])
def new_transaction():

    pass

@app.route('/chain', methods=['GET'])
def full_chain():
	
	pass

二、发送交易

接收的参数是发送者地址、接受者地址和金额,代码为:

@app.route("/transactions/new", methods=["POST"])
def new_transaction():

    values = request.values
    required = ["sender", "recipient", "amount"]
    if values is None or (not all(k in values for k in required)):
        return "Missing values", 400
    msg = blockchain.new_transaction(values["sender"], values["recipient"], values["amount"])
    response = {"message": msg}
    return jsonify(response), 201

这个很简单,主要是利用了上一篇中的交易函数。

三、挖矿

@app.route("/mine", methods=["GET"])
def mine():

    values = request.values
    miner = values["miner"] if "miner" in values else node_id
    blockchain.mine_block('f' * 64, miner)
    response = {"message": "mine success"}
    return jsonify(response), 200

这个也是直接调用了block里面的函数

四、获取区块链信息

这个是为了我们方便查看区块链的变化。


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        "chain": blockchain.chain,
        "length": len(blockchain.chain),
        "in_chain_transactions": blockchain.in_chain_transactions,
        "not_in_chain_transactions": blockchain.not_in_chain_transactions,
        "utxo": blockchain.utxo,
        "neighbors": str(blockchain.neighbors),
    }
    return jsonify(response), 200

其中neighbors只是的邻居节点。接下来会讲。

五、发现邻居节点

@app.route('/nodes/find_neighbors', methods=['POST'])
def find_neighbors():
    # there is a host list
    host_list = [
        "10.0.243.101:5000",
        "10.0.243.101:5001",
        "10.0.243.101:5002",
        "10.0.243.101:5003",
    ]
    for h in host_list:
        if h == request.host:
            continue
        url = "http://%s/nodes/response_neighbors" % (h)
        try:
            r = requests.get(url, params={"node_id": node_id, "port": request.host.split(':')[1]})
            if r.status_code != 200:
                continue
            r_json = r.json()
            blockchain.neighbors.add((r_json["remote_host"], r_json["remote_node_id"]))
        except:
            pass
    response = {
        "neighbors": str(blockchain.neighbors),
    }
    return jsonify(response), 200

新节点加入到区块链网络中的时候,一般开始会有一些种子节点,连接到种子节点之后,种子节点再将这个节点广播出去。这里讲一个端口当做一个节点,所以将500X开头的端口视为一定会扫描的节点,这里只列出了4个端口。
相应的,节点本身肯定得提供响应新节点发现的请求。也就是/nodes/response_neighbors这部分。

@app.route('/nodes/response_neighbors', methods=['GET'])
def response_neighbors():
    # client_port = str(request.environ.get("REMOTE_PORT"))
    client_port = request.values["port"]
    client_host = "%s:%s" % (request.remote_addr, client_port)
    client_node_id = request.values["node_id"]
    response = {
        # "client_host": client_host,
        # "client_node_id": client_node_id,
        "remote_host": request.host,
        "remote_node_id": node_id,
    }
    if client_port[0] == '5' and len(client_port) == 4:
        blockchain.neighbors.add((client_host, client_node_id))
    return jsonify(response), 200

六、解决冲突

比特币使用的是最长链原则,也就是说当前最长的链是正确的主链。发现主链之后,如果自己的链不是主链则需要替换更新,这里为了简化,就是直接将主链的所有信息(除了邻居节点)都同步过来,也就是全量更新,但是实际中是增量更新。

@app.route('/nodes/resolve_conflicts', methods=['GET'])
def resolve_conflicts():
    replaced = blockchain.resolve_conflicts()
    if replaced:
        response = {
            'message': 'Our chain was replaced',
            'new_chain': blockchain.chain
        }
    else:
        response = {
            'message': 'Our chain is main chain',
            'chain': blockchain.chain
        }
    return jsonify(response), 200

七、运行区块链

1. 好了,现在可以自己运行区块链试试了。比如先启用节点1。

python node.py True 5000

True代表的是第一个节点,会挖掘创世区块,5000是端口号。
现在使用restlet client可以查看目前区块里的结果

在节点1上运行 /chain。

2. 接着,再起节点2。

python node.py False 5001

第二个节点里面的区块,是没有创世区块的。

节点2上运行 /chain。信息如下:

接着,不管是在哪个节点上,发起寻找邻居节点的请求。比如在节点1运行:/nodes/find_neighbors。
这个时候,再次查看节点1的区块信息:

节点2的区块信息:

可以看到,两个节点相互都发现了对方。

3. 在节点1上发起一个交易,并挖矿,

发起的请求依次为:

/transactions/new?recipient=f6704ea73bf14fdcb5538a6c9322f0c0&sender=4297526e78074df3a784140e0651c125&amount=10

/mine

得到以下信息:

4. 目前节点2和节点1的区块链信息不同,进行共识,在节点2上运行  /nodes/resolve_conflicts

可以看到,节点2中的区块已经被替换了,这样就解决了冲突。

 

这部分的完整代码为:node.py
可以下载下来,自己修改代码,完善代码,实现属于自己的区块链~

9,903 thoughts on “比特币 (12):300行代码实现一个简化版比特币系统(二)

  1. vurtilopmer

    I discovered your blog site on google and check a few of your early posts. Continue to keep up the very good operate. I just additional up your RSS feed to my MSN News Reader. Seeking forward to reading more from you later on!…

    Reply
  2. oprol evorter

    Great blog! Do you have any helpful hints for aspiring writers? I’m hoping to start my own site soon but I’m a little lost on everything. Would you advise starting with a free platform like WordPress or go for a paid option? There are so many choices out there that I’m completely confused .. Any tips? Appreciate it!

    Reply
  3. ScottFrush

    Good day! chenqingjie.com

    We advance

    Sending your business proposition through the feedback form which can be found on the sites in the contact section. Feedback forms are filled in by our software and the captcha is solved. The profit of this method is that messages sent through feedback forms are whitelisted. This technique improve the chances that your message will be read.

    Our database contains more than 25 million sites around the world to which we can send your message.

    The cost of one million messages 49 USD

    FREE TEST mailing of 50,000 messages to any country of your choice.

    This message is automatically generated to use our contacts for communication.

    Contact us.
    Telegram – @FeedbackFormEU
    Skype FeedbackForm2019
    Email – FeedbackForm@make-success.com

    Reply

发表评论

电子邮件地址不会被公开。