比太钱包安卓版从v0.0.9开始不再基于Bitcoinj,而是使用了我们自行开发的Java语言比特币协议库Bitherj 。这其实是个很无奈的选择,我们也不想“重新发明轮子”,不过没办法,因为“Too Fragile!”(借用Mike Hearn对bitcoind的评价),我们不得不开发一套全新的Bitherj来更好的为比太钱包用户提供服务,这很辛苦,但却值得 。
Bitcoinj的问题如下:
1. 架构:
对于SPV轻钱包模型来说,普通用户并不关心整个区块链上的全部交易,而只关心与自己相关的(和依赖的)比特币交易 。基于一点,在设计轻钱包架构时,应更多的考虑使用关系模型而非序列化模型存储数据 。
但Bitcoinj并没有这么做,Bitcoinj的架构中,无论是缓存的区块链SPV头文件,还是钱包文件,都使用了谷歌自己的序列化方式进行读写,这其实是导致很多问题的根源 。
2. 性能:
由于Bitcoinj的架构设计,使得所有钱包文件的读写都必须是通过谷歌的序列化方式全读、全写,这对于只有一个钱包、一个地址且交易很少的用户来说可能还好(出问题的概率会低得多),但对于有多个钱包、多个地址的用户来说,就可能会成为性能“灾难”,钱包越多、地址越多、交易越多,就越是灾难 。
另外,由于Confidential模型的设计,使得在每一个新块到来的时候,所有钱包所有地址上的所有交易都需要进行更新,哪怕这个新块与任何一个地址上的任何一个交易都无任何关系,都需要把所有的钱包文件序列化的读到内存中,更新完所有的条目,再序列化的写回文件,这就是Bitcoinj性能问题的根源 。
3. 错误:
因架构不合理所导致的性能问题,如果结果仅仅是性能较差,那还算是在可以忍受的范畴 。可问题就在于,这种性能瓶颈可能会导致Bitcoinj的各种运行错误,某些情况下,甚至会出现“致命”的后果 。
正因为意识到了严重的性能问题,Bitcoinj的作者不得不妥协的设计了两套独立的写文件线程,一套高优先级的写线程用于写入那些比较重要的数据(比如通过广播收到了新交易等),一套低优先级的写线程用于延时同步各个钱包文件与SPV区块头文件 。
这使得Bitcoinj的钱包文件读写非常容易出错,经常是钱包文件与块头不一致,钱包文件内的交易之间状态不一致,各个交易的各种状态都频繁的处于正确、错误的来回切换之中,这次写文件没准给写对了,下一个延时的写操作没准又给改错了 。
此类问题,对于开发人员来说是非常头疼的,因为你完全无法依赖某一个值是否可靠,你只能尽可能的用其它方式来判断钱包的状态、交易的状态是否正确,如果发现不正确,再想方设法的用绕过Bitcoinj的方式去修改数据甚至是重置区块链数据(ResetBlockchain) 。
大部分人对于上述问题可能完全没有任何概念,但MultBit的开发者Jim618应该和我们团队一样,对这些问题深有感触 。如果您阅读过一些MultiBit的代码或者比太钱包安卓版v0.0.8之前的代码,就会发现,我们都写了很多方法,专门用来绕过Bitcoinj中可能存在的缺陷,而这些缺陷都是明确的、可复现的、但却几乎不可能在Bitcoinj的架构上解决的(我们团队从MultiBit上借鉴了很多,特此感谢) 。
MultiBit和比太都采用了非常多的迂回手段尽可能避开Bitcoinj中的架构陷阱,虽然已经能够在大部分情况下解决很多问题,但还是不能根治 。而另一个基于Bitcoinj的安卓手机钱包Bitcoin-Wallet,因为它的单钱包、单地址模型,使得出现此类问题的概率大大降低(该应用的开发者本人甚至可能都不知道Bitcoinj会有这么多问题),开发者选择的解决方案是出现问题时重置区块链数据(ResetBlockchain) 。
推荐阅读
- 虎门大桥抖动是怎么了 虎门大桥抖动为什么
- 人类为什么不灭绝蚊子
- 在线钱包Greenaddress.it疑难解答是什么?在线钱包Greenaddress.it解答!
- saber为什么这么喜欢吃
- 家里为什么不能放绿萝
- 舒肤佳为什么不能洗脸舒肤佳不能洗脸的原因
- 手机充不上电的正确处理方法 手机为什么充不进去电怎么办
- 重阳节为什么叫老人节?
- 九月九为什么叫重阳节?
- 为什么叫重阳节?