轉(zhuǎn)自 https://www.arcblock.io/zh/post/2018/08/16/index-bitcoin
如何解析 Bitcoin 數(shù)據(jù)?
眾所周知灭返,互聯(lián)網(wǎng)上的數(shù)據(jù)紛繁復(fù)雜蜓氨,而搜索引擎卻能在海量的數(shù)據(jù)里面迅速的查找出用戶(hù)想要的結(jié)果脆诉。其之所以能夠做到這一點(diǎn)是因?yàn)樗阉饕娴暮竺孢€有兩個(gè)默默付出的組件 -- 爬蟲(chóng)和倒排索引宠纯。 爬蟲(chóng)負(fù)責(zé)不停的從互聯(lián)網(wǎng)上搜集數(shù)據(jù)膊夹,倒排索引負(fù)責(zé)將數(shù)據(jù)以特定的形式存儲(chǔ),以便搜索引擎能夠快速的查詢(xún)昔脯。
Bitcoin 的數(shù)據(jù)都是以二進(jìn)制的形式存在節(jié)點(diǎn)的磁盤(pán)上的啄糙。但是數(shù)據(jù)是以一種對(duì)人不太友好的方式組織在一起的,所以我們首先需要一個(gè)解析器云稚,將這些二進(jìn)制數(shù)據(jù)讀出來(lái)隧饼,還原成它們本來(lái)的面目。之后我們?cè)賹?duì)這些數(shù)據(jù)進(jìn)行一些加工静陈,就得到了 我們所需要的數(shù)據(jù)燕雁。
Bitcoin 數(shù)據(jù)是如何存儲(chǔ)的
存儲(chǔ)數(shù)據(jù)的文件
Bitcoin 的原始數(shù)據(jù)可以在 $HOME/.bitcoin/blocks
下面找到。這個(gè)目錄下面主要有兩類(lèi)文件鲸拥,一種是類(lèi)似于這樣的文件:blk00952.dat拐格, 而另一種是類(lèi)似于這樣的文件:rev000952.dat。 前一種 blk 開(kāi)頭的文件就是存儲(chǔ) Bitcoin 源數(shù)據(jù)的文件刑赶,而后一種是用來(lái)做 rewind 的捏浊。關(guān)于 rewind 我們下回再表,我們這次的重點(diǎn)在源數(shù)據(jù)文件撞叨。每一個(gè) blk 數(shù)據(jù)文件大小的上限都是 128MB金踪。當(dāng)一個(gè)文件寫(xiě)滿(mǎn)后浊洞,Bitcoin 程序會(huì)新建一個(gè)類(lèi)似的文件來(lái)存儲(chǔ)新收到的區(qū)塊信息。
讓我們先通過(guò)如下命令热康,來(lái)看看這些文件里面都存了什么沛申。
od -x --endian=big -N 297 -An blk00000.dat
f9be b4d9 1d01 0000 0100 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 3ba3 edfd
7a7b 12b2 7ac7 2c3e 6776 8f61 7fc8 1bc3
888a 5132 3a9f b8aa 4b1e 5e4a 29ab 5f49
ffff 001d 1dac 2b7c 0101 0000 0001 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 ffff
ffff 4d04 ffff 001d 0104 4554 6865 2054
696d 6573 2030 332f 4a61 6e2f 3230 3039
2043 6861 6e63 656c 6c6f 7220 6f6e 2062
7269 6e6b 206f 6620 7365 636f 6e64 2062
6169 6c6f 7574 2066 6f72 2062 616e 6b73
ffff ffff 0100 f205 2a01 0000 0043 4104
678a fdb0 fe55 4827 1967 f1a6 7130 b710
5cd6 a828 e039 09a6 7962 e0ea 1f61 deb6
49f6 bc3f 4cef 38c4 f355 04e5 1ec1 12de
5c38 4df7 ba0b 8d57 8a4c 702b 6bf1 1d5f
ac00 0000 00f9 beb4 d900
上面的文件就是 Bitcoin 的 Genesis Block
。它是二進(jìn)制數(shù)據(jù)(以 16 進(jìn)制表示)姐军,每?jī)蓚€(gè)字符表示一個(gè)字節(jié)铁材。乍一看感覺(jué)亂七八糟,但是當(dāng)你了解他們的數(shù)據(jù)格式之后奕锌,一切都不是那么難著觉。
數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)
Bitcoin 官網(wǎng)上有關(guān)于數(shù)據(jù)格式的具體講解,但是他們的文檔的組織結(jié)構(gòu)并不容易閱讀理解惊暴,所以我們總結(jié)了一個(gè)更直觀的圖表饼丘。
現(xiàn)在是不是清楚多了?注意辽话,我并有沒(méi)把數(shù)據(jù)換成大端格式肄鸽,請(qǐng)自行轉(zhuǎn)換。
計(jì)算額外字段
細(xì)心的同學(xué)可能已經(jīng)注意到了油啤,這些數(shù)據(jù)中不包括幾個(gè)非常重要的字段典徘,Block Hash
,Block Height
益咬,Transaction Hash
以及 Addresses
逮诲。不錯(cuò),中本聰在設(shè)計(jì) Bitcoin 的時(shí)候可謂是極度的“摳門(mén)”幽告,能通過(guò)計(jì)算得出的數(shù)據(jù)絕對(duì)不存在鏈上梅鹦。從鏈的設(shè)計(jì)角度來(lái)看,這樣做是絕對(duì)正確的冗锁,它能節(jié)省許多磁盤(pán)空間齐唆,但是這卻給我們帶來(lái)了額外的工作。下面我們分別談?wù)劯鱾€(gè)字段的計(jì)算冻河。
計(jì)算 Block Hash 以及 Transaction Hash
Block 和 Transaction 的 Hash 值都是由相同的算法得出的蝶念,不同點(diǎn)只是在于參與計(jì)算的數(shù)據(jù)不同。對(duì)于 Block Hash 來(lái)講芋绸,我們的輸入數(shù)據(jù)只是 80 字節(jié)的 Block Header,而對(duì)于 Transaction 來(lái)講是整個(gè) Transaction 的數(shù)據(jù)担敌。Block Header 里面是包括 Merkle Root
的摔敛,所以在計(jì)算 Block Hash 的時(shí)候不需要整個(gè) Block 最為輸入。
Hash 值是由輸入數(shù)據(jù)進(jìn)行兩次 sha-256 運(yùn)算得出的全封,其形式如下:
hash = sha256(sha256(data))
我們分別將 Block Header 和整個(gè) Transaction 帶入上述公式便能得到相應(yīng)的 Hash 值马昙。
計(jì)算 Block Height
我們知道桃犬,Bitcoin 的原始數(shù)據(jù)是存儲(chǔ)在 blkxxxxx.data 中的,每個(gè) data 文件中都存放了許多個(gè) Block行楞。如果我們按順序從第 0 字節(jié)開(kāi)始讀攒暇,把其中的所有 Block 都解析出來(lái),我們會(huì)發(fā)現(xiàn)這些 Block 并不是完全有序的子房。換句話(huà)說(shuō)你有可能先讀出第 178 個(gè) Block形用,然后才讀出第 177 個(gè) Block。至于為什么會(huì)這樣证杭,大家可以想想以前用 BT 下載的時(shí)候田度,那個(gè)進(jìn)度條長(zhǎng)什么樣。
所以想要得到正確的 Block Height, 我們就必須對(duì)讀出來(lái)的解愤、無(wú)序的 Block 進(jìn)行排序镇饺。了解 Blockchain 的同學(xué)應(yīng)該知道,Blockchain 的數(shù)據(jù)結(jié)構(gòu)是一個(gè)倒過(guò)來(lái)的單鏈表送讲,我們可以通過(guò) Block Hash 和 Previous Block Hash 來(lái)將這些 Block 重新串聯(lián)起來(lái)奸笤。Genesis Block 的 Previous Block Hash 固定為全 0。這樣一來(lái)我們也就可以得出 Block Height 了哼鬓。
計(jì)算 Addresses
想要支持最開(kāi)始的那種根據(jù)地址查詢(xún) Transaction 的功能监右,我們就必須求出每筆交易的發(fā)款人和收款人地址。這兩個(gè)數(shù)據(jù)分別蘊(yùn)含在 Transaction Input 和 Output 的script字段里面魄宏。求出地址的方法大致分為兩步:
對(duì)于 Transaction Output秸侣,找出其中的 public key 或 public key hash,再根據(jù)一定的算法計(jì)算出地址宠互。
對(duì)于 Transaction Input, 從其對(duì)應(yīng)的 Previous Transaction Output 中取出地址味榛,作為它的地址。
限于篇幅予跌,我就不過(guò)多討論第一點(diǎn)了搏色。如果大家感興趣,不妨告訴我們券册,我可以單獨(dú)再用一期來(lái)講解如何解析 Bitcoin Script频轿。但是我想對(duì)第二點(diǎn)進(jìn)行一些補(bǔ)充說(shuō)明。雖然在 Transaction Input 里面你能拿到 public key烁焙,但是我不建議大家直接從 Transaction Input 里面計(jì)算地址航邢。這是因?yàn)榻?jīng)過(guò)隔離見(jiàn)證之后,計(jì)算地址的方式發(fā)生了變化骄蝇,直接從 Input 中解析地址很有可能得到一個(gè)與 Previous Transaction Output 不一樣的地址膳殷。簡(jiǎn)單來(lái)說(shuō)就是,你有可能以張三的名義收了一筆錢(qián)九火,卻用李四的名義花掉了這筆錢(qián)赚窃。張三李四本來(lái)是同一個(gè)人册招,但是因?yàn)檫@個(gè)錯(cuò)誤,計(jì)算賬戶(hù)余額的時(shí)候就會(huì)出現(xiàn)余額為負(fù)數(shù)的情況勒极。
Code
有個(gè) Eth 的實(shí)現(xiàn)
ethLeveldb.go
參考:
https://en.bitcoin.it/wiki/Transaction
http://www.reibang.com/p/637bbdcf17d1