u8,u8国际,u8国际官方网站,u8国际网站,u8国际网址,u8国际链接,u8体育,u8体育官网,u8体育网址,u8注册,u8体育网址,u8官方网站,u8体育APP,u8体育登录,u8体育入口
一个区块包含若干笔交易,并存放于vectorCTransaction vtx。一个区块的哈希,由函数GetHash()生成,是通过计算区块的块头(block-header)而不是整个区块数据所得到。更具体讲,哈希由成员变量nVersion到nNonce生成,而并不包括交易容器vtx。成员变量uint256 hashMerkleRoot是块头的一部分。它由成员函数BuildMerkleTree()生成,该函数将所有交易vtx作为输入参数,并最终生成一个256位哈希hashMerkleRoot。uint256 hashPrevBlock是当前区块之前的区块哈希,被包括在块头当中。因此,一个区块的哈希与它在区块链当中先前区块的哈希有关。
BuildMerkleTree()建立一个梅克尔树并返回树根。该树的每个节点,包括叶节点和中间节点,均为一个uint256哈希值。该树被扁平化并放置在vectoruint256 vMerkleTree中。为了扁平化梅克尔树,该函数首先将第0层的叶节点放入vMerkleTree,接着将第1层的中间结点紧接着放在它们之后,向上重复该过程直到到达根结点。
位于第25行的变量j用于索引vMerkleTree的起始位置来放入树每一层的第一个节点。变量nSize是每层的节点个数。从第0层开始,nSize=vtx.size(),并且j=0。
最外层的for循环访问树的每一层。每层节点个数是上一层的一半(nSize=(nSize+1)/2,第26行)。里面的for循环依次访问某一层的每对节点,指针i每一步增量为2。节点对(i和i2)的哈希附加在容器vMerkleTree,其中i2=i+1。当i到达最后一对节点,i=nSize-1当nSize为奇数,或者i=nSize-2当nSize为偶数。在前者情形,最后一个节点(i2=i=nSize-1)则与其自身的复制组成节点对。
该函数返回一个梅克尔树分支。整个梅克尔树已被扁平化至容器vMerkleTree。参数nIndex指向容器vMerkleTree[nIndex]的那个节点。它的取值从0到vtx.size()。因此,nIndex永远指向一个叶节点,为某笔交易的哈希。
返回的梅克尔树分支包含了所有从vMerkleTre[nIndex]到树根所伴随的节点。回忆建立梅克尔树的过程,每一层的节点两两相配,成为下一层的节点。在第0层,若nIndex为偶数,则与节点vMerkleTree[nIndex]伴随的节点是vMerkleTree[nIndex+1];若nIndex为奇数,则伴随节点为vMerkleTree[nIndex-1]。为了简化,我们把vMerkleTree[nIndex]称作节点A0,其伴随节点为B0。A0与B0相结合并生成另一个位于第1层节点A1。在第1层,A1的伴随节点为B1,两者配对并生成位于第2层的节点A2。重复该过程,直到到达梅克尔树根。节点[A0, A1, A2, ...]形成一条从A0到根节点的路径,它们的伴随节点[B0, B1, ...]则构成所返回的梅克尔分支。
为了找到伴随节点,该函数遍历树的每一层(第4行)。在每一层,指针nIndex比它前一层的值减半。因此,nIndex永远指向路径节点[A0, A1, A2, ...]。另一个指针i设为min(nIndex^1, nSize-1),位于第46行。或运算符^1翻转nIndex最后一位,而不修改其他位。这将保证i永远指向nIndex所指向节点的伴随节点,例如节点[B0, B1, B2, ...]。
你可能会问,梅克尔树的作用是什么。它可以快速地验证一笔交易是否被包括在一个区块当中。下面我们将会介绍。
这个函数接受它的第一个参数hash,用它检查第二个参数vMerkleBranch,并返回一个生成的梅克尔树根。如果所返回的树根与hashMerkleRoot相同,则hash所指向交易的哈希被包括在该CBlock当中。第三个参数nIndex是第一个参数在vMerkleTree当中的位置;它与GetMerkleBranch()的唯一参数是相同的。
因此,若需要检查一笔交易是否被包括在一个CBlock里,则仅需生成该笔交易的哈希并调用该函数,验证返回值是否与hashMerkleroot相同。
返回WriteToDisk(),第三个参数nBlockPosRet是当前CBlock在区块数据文件中存放的起始位置。在Block::WriteToDisk()的第89行,nBlockRet被赋给返回值ftell(),其为区块数据文件的当前指针位置。在其后面,当前CBlock以序列化后的形式被存储在区块数据文件里(第92行)。
除了nFile和nBlockPos,CBlockIndex还包括一份它所指向的区块头的副本(除了字段hashPrevBlock,可通过pprev-phashBlock访问)。这样可以使计算得到区块哈希不需要从区块数据文件中读取整个区块。
从注释的1-6行,我们可以知道pprev指向其在区块链中紧挨着的前一个区块索引,pnext则指向区块链中紧挨着的下一个区块索引。
如果你仔细注意的话,你会发现我提到的是“区块链中的区块索引”而不是“区块链中的区块”。这里有点让人头晕,毕竟区块链是由区块而不是区块索引构成的,不是吗?的确,区块链由区块所构成,但你也可以换一种说法:区块链由区块索引构成。这是一位区块的完整数据被保存在磁盘上的区块数据文件当中,而一个区块索引则通过区块的成员变量nFile和nBlockPos引用这个区块。指针pprev和pnext分别指向一个区块的前一个和下一个区块,以构成整个区块链。所以,区块索引与区块链同样具有意义。
尽管被称为区块链,比特币的区块链其实并不是线性结构,而是树状结构。这棵树的根节点是创世区块,被硬编码至源码当中。其他区块则在创世区块之上依次累积。在一个区块上累积两个或更多区块是合法的。当这种情况发生之后,区块链将产生一个分叉。一个区块链可能含有若干分叉,而分叉出来的分支则可能进一步分叉。
由于分叉的存在,多个区块索引的pprev字段可能指向同一个前任。这些区块索引每一个都开始一个分支,而这些分支都基于同一个前任区块。然而,前任区块的pnext字段只能指向开始最长一条分支的继任区块。从树根(创世区块)到最长分支的最后一个区块的路径被称为最长链,或最佳链,或这个区块链的主链。
指针phashBlock指向该区块的哈希,可以通过CBlockIndex内嵌的区块头现场计算。
nHeight是该区块在区块链中的高度。区块链由高度为0的区块开始,即创世区块。紧接着的区块高度为1,以此类推。
CBlockIndex实例仅保存在内存当中。若要将区块索引存入磁盘,衍生类CDiskBlockIndex在这里定义。
该类拥有另外两个成员变量:前任区块索引和继任区块索引的哈希,hashPrev和hashNext。不要将它们与CBlockIndex中的pprev和pnext搞混,后者是指向区块索引的指针,而前者则是区块的哈希。
CBlockIndex没有序列化方法,而CDiskBlockIndex则通过宏IMPLEMENT_SERIALIZE实现序列化。这是因为后者需要保存在磁盘里,而前者只存在于内存当中。
CTransaction的实例被收集并生成一个CBlock。对于指定的CTransaction实例,它的梅克尔分支,和它在CBlock的vectorCTranaction vtx字段中的位置,可以用于查找该实例是否被包含在这个CBlock里面。这便是为什么CMerkleTx包含另外两个字段:一个梅克尔分支vMerkleBranch,和一个位置索引nIndex。一个CMerkleTx实例携带有这两个字段使其能方便地被验证是否属于一个区块。返回搜狐,查看更多