我正在编写一个脚本来收集所有比特币块的哈希值。程序 bitcoind,如果更改了某个设置,会将所有块的元数据存储在 LevelDB 数据库中。每组元数据的键是块的哈希,通常用作它的标识符。本质上,我试图从每个块中获取元数据的特定部分(事务 ID)。我正在编写的脚本是在 Haskell 中编写的,尽管如果需要,我总是可以执行 shell 命令。概括地说我的问题,我不确定最简单的方法是否是找到所有块哈希(密钥),然后调用 bitcoind 来获取每个块的元数据。如果有任何方法可以直接从 LevelDB 数据库中获取每个值,那也可以。最简单有效的方法是什么?
2 回答
我不确切知道如何使用 haskell 完成此操作,但使用 C++ 执行此操作的最有效方法是使用 leveldb::Iterator 并迭代整个键范围:
0x00000000 -> 0xFFFFFFFF
(32 位密钥)
0x0000000000000000 -> 0xFFFFFFFFFFFFFFFF
(64 位密钥)
从示例:
leveldb::Slice start = ...; // 0x00000000
leveldb::Slice end = ...; // 0xFFFFFFFFFFFFFFFF
该示例显示,当您迭代时,可以加载键和值:
leveldb::Slice key = it->key();
leveldb::Slice value = it->value();
由于您不关心值,您可以跳过查询的部分it->value()
,只需收集您需要的键(在本例中为所有键),您应该会很好。我不会担心不必要地加载值对性能的影响。
然而,这里要注意一件大事:在 bitcoind 运行时你不能这样做!!!
出现这种情况的原因是因为在任何给定时间只能打开一个级别 DB 的实时实例,并且 bitcoind 很可能已经保持该 db 实例打开,尝试打开另一个实例(即使只是为了读取)将导致一个错误。您可以获取数据库的快照并迭代快照。快照允许您保证 bitcoind 的最短停机时间。
最后,如果您无法承受您的 bitcoind 客户端的任何停机时间,那么您可能需要为 bitcoind 或 leveldb 编写一个包装器。您将不得不在某处引入抽象级别。
围绕bitcoind的包装:
- 在制作快照时关闭 bitcoind。
- 缓冲对 bitcoind 的任何请求,直到您完成快照。
- 再次启动比特币。
包装 leveldb:
围绕级别 db 的包装有点困难,您必须保持对数据库实例的控制,并且您必须实际进入并修改 bitcoind 代码,然后构建 bitcoind 并运行它。
如果有任何方法可以直接从 LevelDB 数据库中获取每个值,那也可以。最简单有效的方法是什么?
从leveldb doc这是使用 c++ 从 leveldb 数据库中获取所有键和值的方法。
#include <cassert>
#include "leveldb/db.h"
#include <iostream>
using namespace std;
void get_all_data() {
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
// open the database
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
// iterate all keys and values
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
cout << it->key().ToString() << ": " << it->value().ToString() << endl;
}
assert(it->status().ok()); // Check for any errors found during the scan
delete it;
// When you are done with a database, just delete the database object.
delete db;
}