原文-Debugging Stellar and Bitcoin code bases integration build issue
本人水平有限,如发现错误,欢迎在评论区指正!
介绍
目前,我们的区块链团队正在研究Zagg协议
,这是一个在公有链上的隐私保护协议,使用使用Account和UTXO的混合模型来跟踪资产和价值。更多关于Zagg的信息可以在其官方网站上了解。最适合Zagg的区块链业务用例是Stellar
,但是(基于帐户的模型的)Stellar的生态系统不支持隐私——交易是公开的。Zagg需要隐私,因此需要保障隐私或隔离屏蔽交易,而方法之一就是使用零知识证明的概念(ZKP)。简单来说,就是一方(prover方,证明者)可以向另一方(verifier方,校验者)证明他们知道一个值,没有传达任何额外信息除了这样一个事实:他们知道的值。
为了实现ZKP,我们必须使用基于UTXO的区块链模型。因此,我们决定使用混合模型- Account和UTXO类型。为了实现UTXO功能,我们将Stellar-core和Bitcoin-core区块链代码一起构建。在这个博客里,我想谈谈一下几点:
- 如何将stellar-core和bitcoin-core一起构建
- 采用什么方法集成这两个代码库
- 在将他们一起构建时面临的所有挑战
- 采用什么方案解决上述问题
注:
如果您想了解区块链模型中UTXO和Account背后的基本概念,您可以在这里查看我的博客,或者在youtube上观看我关于这个主题的演讲。除此之外,那些想了解ZKP的人,可以浏览我的同事Abhijit Sinha写的一个简短但信息量很大的博客。
本文核心点:
从本博客中,您应该了解c++代码库的构建环境(非常基础),并了解集成和构建两个不同代码库所需的流程或操作。把这当作一个例子来理解,您可能没有遇到类似的问题,或者无需处理相同的问题,但是文中的概念和想法可供参考。
如果您是一个经验丰富的c++开发人员,那么这个博客可能对您没有太大帮助,但是您仍然可以阅读、验证并对我们所采用的方法提出有价值的建议或评论:)
问题描述
将Bitcoin-core与Stellar-core集成,以利用Bitcoin-core代码库中的UTXO功能。
解决方法
- 将Bitcoin-core转换为静态库(.a文件,本质上是目标文件(.o)的存档。
- 将Bitcoin-core作为Stellar-core的子模块。
- 将Bitcoin-core库及其依赖项链接到Stellar-core可执行文件。
实现
在第一个版本(v0.1)中,我们希望将比特币代码库集成到Stellar分支中,以实现账户余额和UTXO余额双重模型。以下是该协议的其他版本
的详细信息。在本博客中,我们将只关注v0.1。
让我们先复习一下基础知识!
C++项目结构
通常,使用Autotools进行配置的c++项目具有以下结构:
root\
lib\
submodule1\
submodule2\
MakeFile.am
src\
component1\
comp1.cpp
comp1.h
component2\
main.cpp
Makefile.am
.gitmodules
.gitignore
Makefile.am
configure.ac
autogen.sh
这是一个非常基本的项目结构,它可能会变得非常复杂,但为了给大家一个概念,让我们忍受一下。我不会提供这些文件的详细信息,但是您可以从我的github代码库下载我的c++样例工程。文件诸如Makefile.am
,configure.ac
等是为Autotools或GNU构建工具而存在的。Autotools是一组工具,包括如Autoconf
, Automake
, make utility
和Libtools
,用于构建c++项目。
Autotools简介
- Autoconf根据
configure.ac
的内容生成configure
脚本。 - 当
./configure
脚本运行时,它生成config.status
的脚本。 -
Automake
基于Makefile.am
文件生成Makefile.in
。 -
config.status
脚本将Makefile.in
作为输入,并转化为Makefile
文件输出,与构建环境相匹配。 - 最可执行文件
make
使用Makefile
从源代码生成可执行文件。
流程如下所示:
configure.ac Makefile.am
| [Autoconf] | [Automake]
configure Makefile.in
| execute |
configure.status <----------|
|
Makefile
| [make]
(executable)
Configure
脚本负责准备为您的特定系统构建软件。它确保其余构建和安装过程部分的所有依赖项都是可用的,并找出使用这些依赖项所需要知道的一切。
正如您所看到的,作为开发人员,我们只需要处理configure.ac
和Makefile.am
文件,其余都是由Autotools自动生成的文件。现在,如果您需要理解任何c++项目,您应该从位于根目录中的configure.ac
文件开始查看。在该文件中,你将找到一个m4
宏,AC_CONFIG_FILES,在这个宏中我们能够了解需要构建的所有Makefile文件。
Makefile
指导可执行文件make
如何编译和链接程序。在这个文件中,我们可以找到所有与程序相关的依赖项、库和程序文件。通常,这个文件出现在项目的src
目录中。看一下比特币代码中的Makefile.am
文件,你可以通过这个链接更好地理解Autotools。
Bitcoin-core代码库介绍
比特币代码库本质上是四个程序的组合:bitcoind
,bitcoin -cli
,bitcoin -tx
和bitcoin -qt
。你可以在src/Makefile.am
文件中搜索bin_PROGRAMS宏,看到这些程序。
if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
if BUILD_BITCOIN_CLI
bin_PROGRAMS += bitcoin-cli
endif
if BUILD_BITCOIN_TX
bin_PROGRAMS += bitcoin-tx
endif
比特币开发者们已经将模块(consensus、server、cli、wallet、common、crypto等)转换为静态库(.a文件),因此它们可以在所有这些程序之间共享和使用。为了理解这些文件是如何管理的,如前所述,我们需要研究在src
目录中的Makefile.am
文件。例如:
- 在比特币的
src/Makefile.am
文件中,我们有bitcoind,对吧?让我们在那个文件中搜索这个文本。您将获取该程序的源文件(通常为定义main()函数的文件)中的bitcoind_SOURCES
宏和链接到bitcoind
程序的静态链接库文件(.a文件)的bitcoind_LDADD宏。 - 现在让我们寻找一个链接库,比如
libbitcoin_server
,你将找到libbitcoin_server_a_CPPFLAGS
、libbitcoin_server_a_CXXFLAGS
和libbitcoin_server_a_SOURCES
宏。在libbitcoin_server_a_SOURCES
宏中,定义了所有.cpp和.h文件。
让我们开始吧!
现在我们已经对比特币代码库有了初步的了解,并且我们也复习了Autools的概念,现在是时候开始将Bitcoin集成到Stellar中了。首先,我们必须把Bitcoin转换成一个库。这样我们就可以将这个库导入到stellar-core代码中并使用它。
识别正确的程序:当我说比特币的时候,我指的是bitcoind
,因为这是所需的比特币核心程序,其他像钱包,cli等是代码的外部组件,这是用户与比特币网络交互时需要的。因此,我们通过向./configure
脚本传递--disable-*
标志位来禁用所有其他要构建的程序。
但正如我所说,整个比特币代码被分成多个子模块,这些子模块被转换成单独的静态库。我们只需要一个库。所以我们从所有这些库中创建一个库,并将其称为静态库libbitcoin_all.a
:
libbitcoin_all_a_LIBADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1)
然后,我们可以使用src/Makefile.am中提供的stellar_LDADD
宏将这个库与stellar链接起来。参见下面的示例:
stellar_core_LDADD =
.
..
$(libbitcoin_all_LIBS)
如果你想学习如何用c++创建一个简单的静态库,可以参考我的这个库。为了生成这个库,我们对bitcoind代码库src/Makefile.am
做了必要的修改,并构建了该项目。现在我们有一个静态库文件(libbitcoin_all.a
),我们通过Stellar的src/Makefile
文件将其链接到stellar-core
程序。我们在引入.h
文件后,从Stellar代码中调用所需的比特币代码,并试着整体构建这个工程(链接了Bitcoind库的Stellar),但是代码运行不起来。
我们得到了以下错误:
(SET 1)
/home/vishwas/zagg-core/src/main/main.cpp:198: undefined reference to `SendRawTransactionZagg()'
main/stellar_core-main.o: In function `AppInit':
/home/vishwas/zagg-core/src/main/main.cpp:68: undefined reference to `gArgs'
/home/vishwas/zagg-core/src/main/main.cpp:68: undefined reference to `ArgsManager::ParseParameters(int, char const* const*, std::__cxx11::basic_string, std::allocator >&)'
/home/vishwas/zagg-core/src/main/main.cpp:74: undefined reference to `gArgs'
...
..
.
(SET 2)
init.cpp:(.text+0x90c): undefined reference to `fsbridge::fopen(boost::filesystem::path const&, char const*)'
init.cpp:(.text+0xaf2): undefined reference to `GetPidFile()'
../lib/bitcoin/src/libbitcoin_server.a(libbitcoin_server_a-init.o): In function `HandleSIGHUP(int)':
init.cpp:(.text+0xd22): undefined reference to `g_logger'
...
..
.
完整的错误描述请参考链接
调试该错误
我们都知道,当我们遇到一些问题时,我们应该首先理解这个bug,并在坐下来修复它之前找出根本原因。让我们赶紧开始调试吧。
这些错误大多是
引用未定义
错误。如您所见,当我尝试调用bitcoin中的函数SendRawTransactionZagg()时,我得到了这个错误。这个错误意味着编译器能够找到这个函数的声明(因为我们包含了头文件),但是无法找到它的定义。其他方法也发生了类似的错误。如果您注意到下一组错误,同样是
引用未定义
,但这一次它们与boost
库有关,这是比特币的一个依赖库,这意味着我没有将boost-filesystem
库与Stellar的构建连接起来。-
从这些错误中可以明显看出,链接问题是根本原因。从这里,我们有两个努力的方向:
- 检查libbitcoin_alla是否有任何问题?
- 我们需要找出比特币的所有依赖库,并将它们与stellar链接起来。这是我们通过研究
boost
错误所假设的。
步骤1
首先想到的是,我们试图调用的函数SendRawTransactionZagg()是否出现在libbitcoin_all.a库中。正如我所说,这个静态库只是目标文件的存档,因此理想情况下,该函数应该存在于该库中。
我们需要解压它,但在我们这么做之前,首先让我们检查一下这个库是否已经存在或者曾被成功创建。
cd bitcoin
find . -name '*.a'
结果:
./src/.libs/libbitcoinconsensus.a
./src/libbitcoin_util.a
./src/univalue/.libs/libunivalue.a
./src/libbitcoin_wallet.a
./src/libbitcoin_consensus.a
./src/libbitcoin_common.a
./src/libbitcoin_all.a
./src/crypto/libbitcoin_crypto_shani.a
...
..
.
正如你所看到,libbitcoin_all.a库已经存在。现在让我们使用带有grep
的工具nm
来解压它,看看我们的函数是否存在。
cd src
nm -A libbitcoin_all.a | grep "Zagg"
执行该命令后没有产生任何结果,因此很明显,我们创建的
all
库有一些错误。
步骤2
接下来,我们试图找出bitcoind
使用的所有独立库及其依赖关系,并与stellar一起使用它们。
查找所有单独的库:
当我们运行make -n
命令并尝试构建比特币源代码时,请注意,它实际上运行gcc
来编译bitcoind
的源代码。我们还可以注意到,gcc
运行时带有链接的库和依赖项。我们将它们从控制台复制到记事本中,并仔细查看。
查看make -n
控制台命令:
.
..
...
bitcoind;/bin/bash ../libtool --silent --tag=CXX --preserve-dup-deps --mode=link g++ -std=c++11 -Wstack-protector -fstack-protector-all -fPIE --param ggc-min-expand=1 --param ggc-min-heapsize=32768 -pthread -Wl,-z,relro -Wl,-z,now -pie -o bitcoind bitcoind-bitcoind.o libbitcoin_server.a libbitcoin_server.a libbitcoin_common.a univalue/libunivalue.la libbitcoin_util.a libbitcoin_consensus.a crypto/libbitcoin_crypto_base.a crypto/libbitcoin_crypto_sse41.a crypto/libbitcoin_crypto_avx2.a crypto/libbitcoin_crypto_shani.a leveldb/libleveldb.a leveldb/libleveldb_sse42.a leveldb/libmemenv.a secp256k1/libsecp256k1.la libbitcoin_all.a -L/usr/lib/x86_64-linux-gnu -lboost_system -lboost_filesystem -lboost_thread -lboost_chrono -lcrypto -levent_pthreads -levent -levent
...
..
.
- 您可以看到都链接了哪些库,比如
libbitcoin_server.a
,libbitcoin_common.a
,libbitcoin_consensus.a
等,并注意一些依赖关系,如boost
,univalue
,crypto
,secp256k1
等。这意味着stellar还需要包含所有这些库。
在Stellar中使用单独的库和依赖项
我们首先移除了stellar链接的所有.a
库,然后我们添加了上面所有的库和依赖项。这是在stellar代码库中的src/Makefile.am
文件中实现的。参见下面的代码片段:
stellar_core_LDADD = \
.
..
...
$(libbitcoin_server_LIBS) \
$(libbitcoin_consensus_LIBS) \
$(libbitcoin_common_LIBS) \
$(libbitcoin_util_LIBS) \
$(libbitcoin_wallet_a) \
$(libbitcoin_univalue_LIBS) \
$(libbitcoin_crypto_base_LIBS) \
$(libbitcoin_crypto_sse41_LIBS) \
$(libbitcoin_crypto_avx2_LIBS) \
$(libbitcoin_crypto_shani_LIBS) \
$(libbitcoin_leveldb_LIBS) \
$(libbitcoin_leveldb_sse42_LIBS) \
$(libbitcoin_memenv_LIBS) \
$(libbitcoin_secp256k1_LIBS) \
-lboost_system \
-lboost_thread \
-lboost_chrono \
-lboost_filesystem \
-lcrypto \
-levent_pthreads \
-levent
可能有更好的方法来添加这些依赖库,但是为了解决这个问题,我们忽略了修饰。我们再次构建了stellar项目,你猜怎么着,这次成功了!我们可以成功地构建以比特币为库的stellar代码,并能够调用比特币中的函数。
结论
我忘记告诉你的一件事是,我们使用了一个demo项目来修复bug,而不是直接使用Stellar。因为这样我们可以更直接地控制makefile和其他源代码。我认为因为对于初学者来说,使用一个小项目总是好的,一流的项目可能看起来很大,我们会更难控制整个代码库。但这同样取决于我们的选择,我们想要如何继续。我相信把一个大问题分成小块总是有帮助的。我希望这个博客能够启发你,关于如何构建和集成c++项目,以及如果你在做的时候遇到了问题要如何处理。希望下次再见到你。
嘿,等等,你构建单个库(libbitcoin_all.a)的方法错在哪,你还没说、
这是你可能想到的问题,对吧?我将把这个问题留给读者思考,并在下面的评论部分告诉我你的答案。谢谢!