区块链学习1.5-比特币源码的学习-比特币网络

本篇文章有部分内容直接出自《Mastering Bitcoin》

比特币网络层主要是由 P2P网络,传播机制,验证机制三部分组成。

白皮书关于network的内容回顾一下:

The steps to run the network are as follows:

  1. 1)  New transactions are broadcast to all nodes.

  2. 2)  Each node collects new transactions into a block.

  3. 3)  Each node works on finding a difficult proof-of-work for its block.

  4. 4)  When a node finds a proof-of-work, it broadcasts the block to all nodes.

  5. 5)  Nodes accept the block only if all transactions in it are valid and not already spent.

  6. 6)  Nodes express their acceptance of the block by working on creating the next block in the

    chain, using the hash of the accepted block as the previous hash.

Nodes always consider the longest chain to be the correct one and will keep working on extending it. If two nodes broadcast different versions of the next block simultaneously, some nodes may receive one or the other first. In that case, they work on the first one they received, but save the other branch in case it becomes longer. The tie will be broken when the next proof- of-work is found and one branch becomes longer; the nodes that were working on the other branch will then switch to the longer one.

New transaction broadcasts do not necessarily need to reach all nodes. As long as they reach many nodes, they will get into a block before long. Block broadcasts are also tolerant of dropped messages. If a node does not receive a block, it will request it when it receives the next block and realizes it missed one.

这就是比特币网络运行的基本步骤和基本规则。

比特币网络实质是运行比特币P2P协议的节点集合,但这只是现阶段的协议,以后甚至其他币种已经有了很多扩展协议。这些桥接进比特币网络的节点属于扩展比特币网络。他们共同构成了比特币网络。

一. 比特币网络

1.1 节点类别

比特币P2P网络的去中心,准确地说是节点之间的关系对等,而不是所有节点功能完全一致。比特币网络中的节点有如下几种功能:

1.网络路由(Network Route, 简写为N)

2.完整区块链(Full Blockchain, 简写为B)

3.矿工(Miner, 简写为M)

4.钱包(Wallet, 简写为Wallet)。

如果一个比特币节点具有上述全部四种功能,那么这个节点叫做全节点。如果一个节点只保存部分区块,同时使用简化支付验证 (Simplified Payment Verification, SPV)的方法验证交易,那么这个节点叫做SPV节点或者轻量级节点。比特币网络中的大部分节点都不是全节点, 本地数据库只存储部分区块链的情况下,节点依然可以具有钱包功能或者挖矿功能。概括下来,扩展比特币网络中的节点主要分为以下几种:

· 参考客户端 包含钱包,矿工,完整区块链数据库,比特币P2P网络路由节点 

比特币节点类型-参考客户的.png-57.3kB

· 完整区块链节点 包含完整区块链数据库,比特币P2P网络路由节点

完整区块链节点.png-38.3kB

· 轻量级(spv)钱包 包含钱包和比特币P2P网络路由节点,不包含区块链数据库

轻量级(SPV)钱包.png-38kB

· 完整区块链节点 包含挖矿函数,完整区块链数据库,比特币P2P网络路由节点

纯矿工.png-46.5kB

· 矿池协议服务器 网关路由,用于连接比特币P2P协议网络的其他节点

矿池协议服务器.png-30.6kB

· 矿池矿工节点 包含挖矿函数,不包含完整区块链数据库,但运行着其他矿池协议

矿池矿工节点.png-27.8kB

· 轻量级(spv)Stratum钱包 包含钱包和Stratum协议节点,不包含区块链数据

轻量级Stratum钱包.png-38.9kB

比特币协议,stratum协议和矿池协议组成了比特币网络的基础,扩展比特币网络结构如下所示。少量的全节点客户端,少量的独立矿工,矿池及其背后大量矿池矿工。

完整网络.png-776.6kB

二. 典型场景

下面我们从更微观的角度解释比特币网络。比特币的网络模块主要包括以下几个功能:

1)建立初识连接

2)地址传播发现

3)同步区块数据

4)断开连接

与比特币相关的代码主要在src/net.cpp, src/netbase.cpp, src/net_processing.比特币网络消息类型定义见src/protocol.h

要理解每一种消息的场景和含义,才能掌握比特币网络的核心内容。

version - verack 建立连接

addr - getaddr 地址传播

getblocks - inv - getdata 同步区块链数据

namespace NetMsgType {
/**
 * The version message provides information about the transmitting node to the
 * receiving node at the beginning of a connection.
 * @see https://bitcoin.org/en/developer-reference#version
 */
extern const char *VERSION;
/**
 * The verack message acknowledges a previously-received version message,
 * informing the connecting node that it can begin to send other messages.
 * @see https://bitcoin.org/en/developer-reference#verack
 */
extern const char *VERACK;
/**
 * The addr (IP address) message relays connection information for peers on the
 * network.
 * @see https://bitcoin.org/en/developer-reference#addr
 */
extern const char *ADDR;
/**
 * The inv message (inventory message) transmits one or more inventories of
 * objects known to the transmitting peer.
 * @see https://bitcoin.org/en/developer-reference#inv
 */
extern const char *INV;
/**
 * The getdata message requests one or more data objects from another node.
 * @see https://bitcoin.org/en/developer-reference#getdata
 */
extern const char *GETDATA;
/**
 * The merkleblock message is a reply to a getdata message which requested a
 * block using the inventory type MSG_MERKLEBLOCK.
 * @since protocol version 70001 as described by BIP37.
 * @see https://bitcoin.org/en/developer-reference#merkleblock
 */
extern const char *MERKLEBLOCK;
/**
 * The getblocks message requests an inv message that provides block header
 * hashes starting from a particular point in the block chain.
 * @see https://bitcoin.org/en/developer-reference#getblocks
 */
extern const char *GETBLOCKS;
/**
 * The getheaders message requests a headers message that provides block
 * headers starting from a particular point in the block chain.
 * @since protocol version 31800.
 * @see https://bitcoin.org/en/developer-reference#getheaders
 */
extern const char *GETHEADERS;
/**
 * The tx message transmits a single transaction.
 * @see https://bitcoin.org/en/developer-reference#tx
 */
extern const char *TX;
/**
 * The headers message sends one or more block headers to a node which
 * previously requested certain headers with a getheaders message.
 * @since protocol version 31800.
 * @see https://bitcoin.org/en/developer-reference#headers
 */
extern const char *HEADERS;
/**
 * The block message transmits a single serialized block.
 * @see https://bitcoin.org/en/developer-reference#block
 */
extern const char *BLOCK;
/**
 * The getaddr message requests an addr message from the receiving node,
 * preferably one with lots of IP addresses of other receiving nodes.
 * @see https://bitcoin.org/en/developer-reference#getaddr
 */
extern const char *GETADDR;

2.1 建立连接

在比特币客户端,可以用bitcoin-cli getpeerinfo命令之后查看可以连接到的网络节点信息:

$ bitcoin-cli getpeerinfo
[{
    "addr": "85.213.199.39:8333",
    "services": "00000001",
    "lastsend": 1405634126,
    "lastrecv": 1405634127,
    "bytessent": 23487651,
    "bytesrecv": 138679099,
    "conntime": 1405021768,
    "pingtime": 0.00000000,
    "version": 70002,
    "subver": "/Satoshi:0.9.2.1/",
    "inbound": false,
    "startingheight": 310131,
    "banscore": 0,
    "syncnode": true
}, {
    "addr": "58.23.244.20:8333",
    "services": "00000001",
    "lastsend": 1405634127,
    "lastrecv": 1405634124,
    "bytessent": 4460918,
    "bytesrecv": 8903575,
    "conntime": 1405559628,
    "pingtime": 0.00000000,
    "version": 70001,
    "subver": "/Satoshi:0.8.6/",
    "inbound": false,
    "startingheight": 311074,
    "banscore": 0,
    "syncnode": false
}]

找到相邻节点后,客户端就开始和同伴建立TCP连接。节点A向B发送版本号ver,B收到后,如果与自己兼容则确认连接,B返回verack,同时向A发送自己的版本号,如果A也兼容,A在此返回verack,成功建立连接。整个过程如下图:

初始连接.png-95.9kB

建立连接的更多细节见src/net.cpp。输入待连接的地址addrConnect,返回该地址的节点pnode。如果该节点已经连接,直接返回该节点。如果是新节点,尝试建立Socket连接或者通过代理连接。

CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure)
{
    if (pszDest == nullptr) {
        if (IsLocal(addrConnect))
            return nullptr;

        // Look for an existing connection
        CNode* pnode = FindNode(static_cast<CService>(addrConnect));
        if (pnode)
        {
            LogPrintf("Failed to open new connection, already connected\n");
            return nullptr;
        }
    }
    // 省略部分代码
    bool connected = false;
    SOCKET hSocket = INVALID_SOCKET;
    proxyType proxy;
    if (addrConnect.IsValid()) {
        bool proxyConnectionFailed = false;
        // 网络代理部分代码省略
        std::string host;
        int port = default_port;
        SplitHostPort(std::string(pszDest), port, host);
        connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);
    }
    if (!connected) {
        CloseSocket(hSocket);
        return nullptr;
    }
    NodeId id = GetNewNodeId();
    uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
    CAddress addr_bind = GetBindAddress(hSocket);
    CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(),hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ?pszDest : "", false);
    pnode->AddRef();

    return pnode;
}

2.2 地址传播发现

一旦建立连接,节点便会向所有邻居节点发送addr信息,这条信息包含自己的iP地址,用于向更多的节点告知自己。此外,节点还会向它所有的邻居节点发送getaddr请求,获取邻居节点可以连接的节点列表。整个过程如下:

地址传播发现.png-96.2kB

具体代码在开始所说的文件中。

2.3 同步区块链数据

连接建立后,两个节点互相发送同步请求,节点比较对方的BestHeight后,区块链较多的一方向较少的一方发送inv响应,让落后的节点追上,收到inv响应后,落后的节点开始发送getdata请求数据,整个过程如下:

同步区块链数据.png-158.7kB

具体代码在开始所说的文件中。

2.4 断开连接

如果两个节点建立网络后没有流量,节点之间会定期发送信息保持连接,如果两个节点超过一定时间没有发送信息,那则认为节点已经断开,并开始寻找新的节点。

具体代码在开始所说的文件中。

三. 简化支付验证SPV

比特币网络中重要的组成部分,轻量级钱包。

为了支持一些轻量级的设备,于是有了简化支付验证SPV,用户客户端只需要保存区块header就可以验证支付。用户如果能够从区块链某处找到相符的交易,则证明网络已经认可了这笔交易,而且得到了网络的多少个确认。轻量级钱包只同步header也大大减少客户端的网络开销。

SPV节点同步区块header.png-107kB

spv背后的技术原理是Bloom Filter。Bloom Filter是一种概率搜索器,他在搜索时不需要完整描述被搜索的模式,这种查询虽然存在一定比例的伪命中,但效率远远高出普通查询。例如查询名字是以g结尾,交易金额的小数部分是0.618的交易。搜索条件的模糊程度和交易隐私泄露程度构成了一种权衡关系。有了SPV,我们可以在不暴露地址的情况下完成支付验证。

这里需要注意,SPV指的是“支付验证“,而不是“交易验证”。这两种验证有很大区别。”交易验证”非常复杂,涉及到验证是否有足够余额可供支出、是否存在双花、脚本能否通过等等,通常由运行完全节点的矿工来完成。“支付验证”则比较简单,只判断用于“支付”的那笔交易是否已经被验证过,并得到了多少的算力保护(多少确认数)。

猜你喜欢

转载自blog.csdn.net/Billy1900/article/details/82827629