如何從 bitcoind 中檢索所有交易?
我正在研究一個需要解析來自 bitcoind 的所有交易的項目。我一直在使用選項 txindex=1 的 bitcoind,並使用 RPC 呼叫 getRawTransaction 提取交易。它工作,但很慢。
有沒有什麼快速的方法可以從 bitcoind 中提取所有交易?(我不需要按順序拉它們)。
然後我嘗試直接從 bitcoind DB 中讀取,但根據What is the database for? 交易不儲存在levelDB中,只有blockXXX.dat,網路格式,所以我需要解析塊來提取它們,這似乎不太好(我猜孤塊也在那裡,所以會產生問題)。
每個事務進行一次 RPC 呼叫會增加很多成本。
目前尚不清楚“所有交易”的確切含義,但我假設您有一種方法可以辨識您想要交易的“所有區塊”。
然後,您可以將成本減少到每個塊一次 RPC 呼叫,方法是使用 getblock 並將第二個選項 (verbose) 設置為 false (getblock blockhash false),然後自己直接解析原始塊數據。
自己解析原始塊數據需要做一些工作,但如果您已經在處理原始交易數據,那麼我猜您可能已經有了程式碼來進行相關解碼。如果沒有,無論如何設置它並不太難。
作為副作用,請注意,此方法不再需要完整的交易索引 (txindex=1)。
我已經在這篇博文中更詳細地介紹了這個特定問題,因此請查看這篇文章了解更多詳細資訊以及一些範常式式碼。
添加到我上面的評論中,下面是解析整個比特幣區塊鏈並提取原始塊的 Scala 程式碼。它使用 Bitcoinj.jar 庫來進一步解析原始塊。
塊儲存在文件中
blkxxxxx.dat
。該文件的結構如下:4 | 4 | 80 | TxData | 4 | 4 | 80 | TxData | 4 | 4 | 80 | TxData | ...
- 前 4 個字節:魔術字節(辨識您所在的網路)
- 第二個4字節:剩餘塊的字節數
- 接下來的 80 個字節:塊頭本身
- Next NumBlockBytes - 80 字節:此塊中的交易數據 [ numTx | TX1 | TX2 | TX3 | … ]
在我的系統中,我能夠在 4 小時內遍歷所有文件(1000+)(沒有驗證或處理塊字節,只是下面的虛擬程式碼)。當時區塊鏈上有大約 140 GB 的數據。也許一些 Scala 大師可以讓它更快。
有趣的是,當我第一次同步 bitcoind 時,它在 6 小時內完成,包括下載和驗證區塊。所以這在 C++ 中會更快。
您還必須處理孤兒。
import java.io._ import java.nio._ import scala.collection.mutable.ArrayBuffer import org.apache.commons.io.FileUtils import org.bitcoinj.core._ import org.bitcoinj.params._ import scala.collection.JavaConversions._ object Utils { // Used for closing files implicitly def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B = try { f(param) } finally { param.close() } // this is the method that actually parses the file def parseFile(name:String) = { System.gc // large files (around 140 MB each, need to clear memory) using(new FileInputStream(name)) {fis => using(new BufferedInputStream(fis)) {bis => var currBlkByte = -1 // which byte of raw block are we reading? var currBlk = 0 // which block is currently being read? var currBlkSize = -1L // what is the size of block (in bytes) var endBlkByte = -1 // which is the ending byte of current block? val blkSizeBytes = new ArrayBuffer[Byte] // stores bytes containing data about block size val blkBytes = new ArrayBuffer[Byte] // stores bytes of block Stream.continually(bis.read).takeWhile(-1 !=).foreach{int => currBlkByte += 1 val byte = int.toByte // ignore first 4 bytes (magic bytes), next 4 bytes stores upcoming block's size in little endian if (currBlkByte >= 4 && currBlkByte < 8) blkSizeBytes += byte if (currBlkByte == 7) { // this byte is the last one encoding block's size currBlkSize = ByteBuffer.wrap(blkSizeBytes.toArray).order(ByteOrder.LITTLE_ENDIAN).getInt & 0xFFFFFFFFL; endBlkByte = currBlkSize.toInt + 7 // first 8 bytes for info, remaining encoding block blkSizeBytes.clear // clear for next block } if (currBlkByte > 7) blkBytes += byte // block data if (currBlkByte == endBlkByte) { // we have reached end of block // last block byte currBlk += 1 // increment block count currBlkByte = -1 // reset endBlkByte = -1 // reset parseBlk(blkBytes.toArray) // we have block in bytes, lets parse it blkBytes.clear // reset } } } } } val context = new Context(MainNetParams.get) // needed for Bitcoinj v 0.13 and above def parseBlk(bytes:Array[Byte]) = { // uses Bitcoinj new Block(MainNetParams.get, bytes).getTransactions.foreach {tx => val hash = tx.getHashAsString val inputs = tx.getInputs val outputs = tx.getOutputs // do something with above } } def getAllFiles(dir:String, extensions:Array[String], recursive:Boolean) = FileUtils.listFiles(new File(dir), extensions, recursive).toArray.map(_.toString) } import Utils._ object BlockParser { val dir = "/home/user/.bitcoin/blocks" //files have names like blk00000.dat, ..., blk01096.dat (last one at time of writing) val files = getAllFiles(dir, Array("dat"), false).collect { case name if name.contains("blk") => // collect only those file with names like "blkxxxxx.dat" val num = name.drop(s"$dir/blk".size).take(5).toInt // (take 5 is based on actual file names) (name, num) }.sortBy(_._2).unzip._1 // sort by file number files.foreach(parseFile) }