主页 > imtoken苹果版 > 区块链学习1.5-比特币源码学习-Bitcoin Network
区块链学习1.5-比特币源码学习-Bitcoin Network
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网络路由节点
· 完整的区块链节点包括完整的区块链数据库、比特币P2P网络路由节点
· 轻量级(spv)钱包包含钱包和比特币P2P网络路由节点,不包含区块链数据库
· 完整的区块链节点包括挖矿功能、完整的区块链数据库、比特币P2P网络路由节点
· 矿池协议服务器网关路由,用于连接比特币P2P协议网络的其他节点
矿池矿工节点包含挖矿功能,不包含完整的区块链数据库,但运行其他矿池协议
· 轻量级(spv)Stratum钱包包含钱包和Stratum协议节点,不包含区块链数据
比特币协议、地层协议和矿池协议构成了比特币网络的基础,扩展后的比特币网络结构如下图所示。 少量的全节点客户端,少量的独立矿工,矿池和背后的大量矿池矿工。
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,连接建立成功。 整个过程如下:
有关建立连接的更多详细信息,请参见 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请求,以获取邻居节点可以连接的节点列表。 整个过程如下:
具体代码在开头提到的文件中。
2.3 同步区块链数据
连接建立后,两个节点互相发送同步请求。 节点相互比较BestHeight后,区块链多的一方向区块链少的一方发送inv响应,让落后的节点赶上。 滞后节点收到inv响应后开始发送getdata请求数据,整个过程如下:
具体代码在开头提到的文件中。
2.4 断开连接
如果两个节点建立网络后没有流量,节点会周期性地发送信息来维持连接。 如果两个节点在一定时间内没有发送信息,则认为该节点已断开连接,并启动一个新节点。
具体代码在开头提到的文件中。
3.简化支付验证SPV
比特币网络的重要组成部分,轻量级钱包。
为了支持一些轻量级设备,有一个简化的支付验证SPV,用户客户端只需要保存区块头就可以验证支付。 如果用户可以在区块链的某处找到匹配的交易,则证明网络已经批准了该交易以及它从网络收到了多少确认。 轻量级钱包只同步头部比特币源码在哪,大大降低了客户端的网络开销。
spv背后的技术原理是Bloom Filter。 布隆过滤器是一个概率搜索器。 搜索时不需要完整描述搜索到的模式。 这种查询虽然有一定比例的误命中,但是效率比普通查询高很多。 例如查询名称以g结尾的交易,交易金额的小数部分为0.618。 搜索条件的模糊程度和交易隐私泄露的程度构成了权衡关系。 使用SPV,我们可以在不暴露地址的情况下完成支付验证。
这里需要注意的是,SPV指的是“支付验证”,而不是“交易验证”。 这两种类型的验证之间存在很大差异。 “交易验证”非常复杂,涉及到验证是否有足够的余额可以花费,是否存在双花,脚本是否可以通过等,通常由运行全节点的矿工完成。 “支付验证”比较简单。 它只判断用于“支付”的交易是否经过验证,获得了多少算力保护(多少确认)。
分类:
技术要点:
相关文章: