主页 > imtoken苹果版 > 区块链学习1.5-比特币源码学习-Bitcoin Network

区块链学习1.5-比特币源码学习-Bitcoin Network

imtoken苹果版 2023-07-04 05:14:04

2021-12-19

本文部分内容直接来自《精通比特币》

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

回顾一下网络上白皮书的内容:

运行网络的步骤如下:

1) 新交易被广播到所有节点。

2) 每个节点将新交易收集到一个块中。

3) 每个节点都致力于为它的块找到一个困难的工作量证明。

4) 当一个节点找到一个工作量证明时,它会将这个块广播给所有节点。

5) 只有当其中的所有交易都有效且尚未花费时,节点才会接受该块。

6) 节点通过努力创建区块中的下一个区块来表达他们对区块的接受

链,使用接受块的哈希值作为前一个哈希值。

节点始终认为最长的链是正确的链,并将继续努力扩展它。 如果两个节点同时广播不同版本的下一个区块,一些节点可能会先接收一个或另一个。 他们收到了一个,但保存另一个分支以防它变得更长。 当找到下一个工作证明并且一个分支变得更长时,平局将被打破; 在另一个分支上工作的节点将切换到更长的分支。

新的交易广播不一定需要到达所有节点。 只要他们到达很多节点,他们很快就会进入一个区块。 块广播也可以容忍丢失的消息。 如果一个节点没有收到一个块,它会在收到下一个块并意识到它错过了一个时请求它。

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

比特币网络的本质是运行比特币P2P协议的节点集合,但这只是现阶段的协议,未来甚至其他货币还有很多扩展协议。 这些桥接到比特币网络的节点属于扩展的比特币网络。 它们共同构成了比特币网络。

一、比特币网络 1.1 节点类别

比特币 P2P 网络的去中心化恰恰是节点之间的对等关系,而不是所有节点都具有完全相同的功能。 比特币网络中的节点具有以下功能:

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

2. 全区块链(简称B)

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

4.钱包(Wallet,简称钱包)。

如果一个比特币节点具备以上四种功能,那么这个节点就称为全节点。 如果一个节点只保存部分区块,并使用简化支付验证(SPV)方式来验证交易,那么这个节点被称为SPV节点或轻量级节点。 比特币网络中的大多数节点都不是完整节点。 当本地数据库只存储部分区块链时,节点仍然可以具有钱包功能或挖矿功能。 综上所述,扩展后的比特币网络中的节点主要分为以下几类:

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

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

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

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

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

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

· 完整的区块链节点包括挖矿功能、完整的区块链数据库、比特币P2P网络路由节点

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

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

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

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

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

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

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

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

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

2. 典型场景

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

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将版本号ver发送给B,B收到后,如果与自己兼容,则确认连接。 B返回verack,将自己的版本号发送给A,如果A也兼容,A在这里返回verack,连接建立成功。 整个过程如下:

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

有关建立连接的更多详细信息,请参见 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(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请求,以获取邻居节点可以连接的节点列表。 整个过程如下:

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

具体代码在开头提到的文件中。

2.3 同步区块链数据

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

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

具体代码在开头提到的文件中。

2.4 断开连接

如果两个节点建立网络后没有流量,节点会周期性地发送信息来维持连接。 如果两个节点在一定时间内没有发送信息,则认为该节点已断开连接,并启动一个新节点。

具体代码在开头提到的文件中。

3.简化支付验证SPV

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

为了支持一些轻量级设备,有一个简化的支付验证SPV,用户客户端只需要保存区块头就可以验证支付。 如果用户可以在区块链的某处找到匹配的交易,则证明网络已经批准了该交易以及它从网络收到了多少确认。 轻量级钱包只同步头部比特币源码在哪,大大降低了客户端的网络开销。

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

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

这里需要注意的是,SPV指的是“支付验证”,而不是“交易验证”。 这两种类型的验证之间存在很大差异。 “交易验证”非常复杂,涉及到验证是否有足够的余额可以花费,是否存在双花,脚本是否可以通过等,通常由运行全节点的矿工完成。 “支付验证”比较简单。 它只判断用于“支付”的交易是否经过验证,获得了多少算力保护(多少确认)。

分类:

技术要点:

相关文章: