BTC钱包地址到账相关处理
问题
项目需要对大量BTC账号的交易情况进行监听,例如发起交易,交易到账。交易数据可以查询Transaction获取到,但是地址数量达到千万时,不可能去轮询每个账号的, 这个方案延迟无法容忍,同时对节点接口的压力太大。那么换一个思路,我们能否一个区块一个区块来解析呢?这样压力就小了很多了。毕竟比特币每几十分钟才有一个块。
方案1
查找了一下服务提供商,发现有下面的接口,提供了对整个区块的所有交易进行解析并返回Json格式数据:
https://blockchain.info/rawblock/{blockHeight}?format=json
此接口返回的数据格式如下:
{
"hash": "000000000000e58392f3b59350e72cdaa81655460a0bd5547319522702799993",
"ver": 1,
"prev_block": "00000000000077bd10e83210feaebdafd89ade90d80f0bd219e28c550718c7fc",
"mrkl_root": "28893c39d71921af602921bf5916f815b946144d8044e3f01e7049d74340eaf5",
"time": 1301066849,
"bits": 453047097,
"next_block": [
"0000000000007f51a1c13814ecb9698f56b91b2599552d7c47ec1d5b7517ae81"
],
"fee": 4000000,
"nonce": 3674390797,
"n_tx": 5,
"size": 1247,
"block_index": 115000,
"main_chain": true,
"height": 115000,
"weight": 4988,
"tx": [
{
"hash": "8de81b651caaeba5021ae8459b450dffe71c8a630039efc3d5a699fa055eda78",
"ver": 1,
"vin_sz": 1,
"vout_sz": 1,
"size": 134,
"weight": 536,
"fee": 0,
"relayed_by": "0.0.0.0",
"lock_time": 0,
"tx_index": 4252136820921140,
"double_spend": false,
"time": 1301066849,
"block_index": 115000,
"block_height": 115000,
"inputs": [
{
"sequence": 4294967295,
"witness": "",
"script": "0439f3001b016b",
"index": 0,
"prev_out": {
"n": 4294967295,
"script": "",
"spending_outpoints": [
{
"n": 0,
"tx_index": 4252136820921140
}
],
"spent": true,
"tx_index": 0,
"type": 0,
"value": 0
}
}
],
"out": [
{
"type": 0,
"spent": true,
"value": 5004000000,
"spending_outpoints": [
{
"tx_index": 5081407785457285,
"n": 0
}
],
"n": 0,
"tx_index": 4252136820921140,
"script": "4104a09831d3e31a82d20cbe57476cfd678b11a9d91b010e7c377f1237e337e0f0e39852f2c5a8193b832075f89792dec799a13997bceb761e423ab388a791d84bdeac",
"addr": "1HmaNVUiUmqut6ikcSRRsH87bapAiQvHVR"
}
]
}
]
}
tx里的input和out分别代表支出和收入。 因此采用此接口就可以以区块为单位解析地址的交易情况了。
如何从交易里查找我们需要的地址呢?
我们需要将json格式的交易里的输入输出地址与我们的地址库相比较,如果是我们的地址,就说明该地址在该区块发生了交易。 但数千万的地址是存在数据库的,如果如果每次比较都查询数据库,效率太慢了,而且数据库压力会及较大,那有什么方法可以解决呢?这个其实是一个存在问题,单个字符串是否在某个数据集里,这种情况经常采用的 方法是布隆过滤器。
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它可能会误判,也就是说,它可能会把不存在的元素判定为存在,但不会出现将存在的元素判定为不存在的情况。
布隆过滤器的工作原理是使用多个哈希函数将元素映射到一个位数组上。当一个元素被添加时,对应的位数组位置会被设置为1。如果在查询某个元素时,所有的位数组位置都是1,那么这个元素可能存在,但也可能因为之前其他元素的哈希值冲突而产生误判。误判率与位数组的大小和使用的哈希函数数量有关。
布隆过滤器的主要优点是节省存储空间,尤其在处理大量数据时,相比于传统的列表或者哈希表,它的空间效率更高。但是,由于其概率性质,无法保证完全准确,这也是它的主要缺点。
应用场景包括:垃圾邮件过滤、URL去重、缓存系统等需要快速判断元素是否存在且对误判有一定容忍度的场景。
只需要使用我们的地址初始化一个布隆过滤器,并动态维护,这样判断地址的时候就可以优先使用布隆过滤器来判断了,大大降低了数据库的请求压力。
方案2
在方案1里我们是采用了第三方的接口来直接获取已经解析好的区块信息,那我们能否自己获取整个区块的原始数据,自己解析呢,这个是可以的,可以通过下面的接口获取区块原始数据:
https://blockchain.info/rawblock/{blockHeight}?format=hex
https://mempool.space/testnet/api/block/{blockHeight}/raw
这两个接口都可以获取二进制的区块信息,接下来需要编码解析,我们采用BitcoinJ库来解析
// 获取区块数据
byte[] bytes = httpReqUtil.getBytes(String.format(BLOCK_HEX_URL, btc_hash));
// 设置context
Context context = new Context(MainNetParams.get());
// 创建区块序列化器
BitcoinSerializer bitcoinSerializer = new BitcoinSerializer(TestNet3Params.get(), false);
// 解析区块
Block block = bitcoinSerializer.makeBlock(bytes);
// 获取交易信息
List<Transaction> transactions = block.getTransactions();
//获取支出交易
List<TransactionInput> inputs = transaction.getInputs();
//获取收入交易
List<TransactionOutput> outputs = transaction.getOutputs();
手动解析区块交易完毕。