2023-04-15

更换Bitcoin的核心共识协议(Consensus)

从开始着手研究这个项目到制作,到现在的测试阶段,一共花费了一年多的时间。从开始阅读Chia的核心算法和论文到着手开始修改解决原Bitcoin的Consensus,这一路过来虽然并不是很顺畅,不过也没有太大的无法解决的问题出现。基本上花时间去阅读文档和代码,最终还是把问题解决了。但是,时间上的确花费得比原来预想的要多,上一波修改这个代码的团队基本上也没有什么时间为我提供什么支持,而Chia的C++相关的代码真的写得和屎一样,要为自己所用得要魔改很多地方。并且因为对于这套机制的不熟悉,最终还是走了一些设计上的弯路,在这个过程中,有至少1/3的代码被推翻重写,还碰到了一堆的坑,其中包括第三方的包的坑,也有C++的坑。

被推翻的代码

chiavdf

一开始,想要把vdf计算编译到项目中使用,于是花了大量的时间来修改chiavdf这个项目。该项目主要的语言是C/C++,但是看得出来写这个代码的人似乎只有C的经验,C++的代码写得真是一言难尽。唯一的CMake脚本只是为了编译出Python的库。将其移植到自己的项目中真的是一件非常费力不讨好的事情,并且为后续的开发埋下了很多的问题。在仔细了解了chiavdf这个项目后,发现其有一个目标文件vdf_client,进程可以通过Socket连接到其它的进程,并接收一些简单的指令,虽然要使用Socket通信,但是好处是不再需要再将其编译至自己的项目中。于是最终vdf计算结果的获取方式将通过启动vdf_client进程并与其进行通讯来实现,主项目不再因为chiavdf而受到污染。

chiavdf不支持Windows,因其使用了不可移植的汇编语言,并且在Windows下的编译需要使用gmp的替代方案mpir。

共识代码

因为涉及空间和时间两种证明,所以,对于不同的空间证明有着对应的不同的时间证明要求。质量越好的空间证明,所需要的时间证明的时间就越短,那么质量越好的区块提交上来的时间就要比质量差的区块提交上来的时间要早。

刚开始并不是这样,所有的时间证明要求都一致,方便产出稳定的块间间隔,但这样会导致链上产生很多的forks。现在的这种模式虽然不会产生fork,但是却会让下一个区块的时间不固定,若大的算力出现连接问题会导致下一个出块的时间变得非常的长。但是现在这样的模式是比较合理的,算力的波动影响出块间隔本身在Bitcoin的PoW协议中也是同样存在的。

时间证明传播方式

因为时间证明对于所有的矿工来说除了时间这一参数外,其它的参数都是相同的。所以,在本地运行一台时间证明运算器并不是必需,只要能够获取到所需的时间证明即可。而且,因为运行时间证明的机器的CPU有快有慢,所以若运行时间证明的机器所使用的CPU太慢,反而会影响当前的矿工出块。

一开始,通过P2P网络来传播关于时间证明的请求及答案,但是这样会导致P2P网络传输的数据太多。复杂的数据包加上了复杂的逻辑使得这一方案最终让我决定不再使用P2P网络来传播时间证明相关的数据包,改为使用单一的Timelord服务器来运行时间证明。这种方式更加好控制,并且矿工也可以方便的在自己本地搭上一台时间证明服务器只稳定服务于自己的节点。

问题回顾

C++

C++的模版,偏特化及范型多参数其实很多坑,用的时候需要尽量小心,不然有时候都不知道具体是调用了哪个函数。

测试用例

尽可能的在代码写完前把测试用例给补全,这样可以节省非常多的时间,哪怕这段代码涉及的方方面面早就已经做过一次。就拿之前使用得非常多的sqlite3库来说,这次的项目中又再次使用其来做本地的数据存储,本来信心满满,但是说实话在测试用例中还是解决了不少问题。比如:sqlite3的内存管理方式就有一个不注意读文档就会踩到的坑,在提交sql字符串参数的时候,若没有特别的参数指定的话,该内存需要用户一直保持直到sql执行完成,但是我扔进去的是一个临时字符串对象,在执行sql的时候该字符串内存早已经释放,而正确的做法是,提交一个额外的flag,以告知这段 内存需要sqlite3自行复制以保证其有效性直到sql执行完成。若这些问题不是在测试用例中解决,而是在目标代码中运行的话,那调试起来会是非常的费劲。

第三方库

一些第三方库的依赖非常多,外部内部依赖就好几个,碰到这种库的时候,特别需要小心,这些库一不小心就要牵扯很多的开关,不同的环境又需要调试。有时候不如放弃,一些简单的功能可能自己写还容易过嫁接第三方库。

纠结的C++ IDE

在开发的过程中一直都很纠结使用什么来进行开发,一会儿VSCode,一会儿又是NeoVim,甚至有时候是VIM+CoC,我的强迫症着实是挺严重的。但是其实最后发现,能够在开始编码的时候快速的进入心流状态才是最好的,然而要达到这种效果,却是需要最少的打扰。

其实提供了丰富功能的IDE在某些时刻是一种打扰,而其提供的自动完成等功能其实带来的好处往往不及于其因为不断的打扰而带来的坏处。这种打扰不是说弹出的提示信息,而是因为有了自动提示后,使得人的脑子会不自主的进入一种懒惰不思考的状态,自然就非常的难进入心流。而开发其实像是CPU执行指令,常用的数据会被扔到Cache以达到快速访问的目的。同样,开发的过程中,当前涉及的代码片段应该尽可能的扔到脑子里的快速存取区域,以达到思维可以连续不被打断,而代码的自动完成等其它相关的提示其实不利于对快速存取区的数据结构的建立。

最终使用VIM+CTags可以进行大部分的开发,而且因为不需要照顾和配置Clangd或相关的Lsp,反而省掉了大量的因Lsp出问题导致的开发停顿。当然,每个人都有自己的开发习惯,但是对我来说,这样是最好的方式。

MATTHEW
桂ICP备17005075号