分享

在win10 vs 2015 上编译运行bitcoin v0.1源码 (上)

 quasiceo 2018-04-03


终于正式开始研究bitcoin源码了。

所谓看源码,根据各位elders的经验来看,最简单的方式就是根据git的记录找到初始版本,然后慢慢向上回溯,也可根据change log 慢慢添加。但是总之,从最原始的版本来学习才是最符合我们这些萌新的。

但是,根据github的git记录来看,最原始的版本并不是 神*中本聪 公布的最原始bitcoin源码。

可以看到在git记录中,最原始的tag是:

  • v0.1.5

版本,初始commit似乎也不是原始版本。

本来根据这个版本也是可以学习的,但是毕竟不正宗(摔),那不正宗的都是邪教#手动滑稽

所以我们就寻找v0.1版本的代码吧。

(加粗/醒目)以下的行为都基于 win10 64bit & Visual Studio 2015 community

并且编译的 bitcoin 源码只编译 x86 的 debug 平台!!

vs 2015 提供的是c++11 标准


一、下载 v0.1 版本的源码

我v0.1版本的源码是从这个链接来的(需要翻墙)

Who has the source code of the first version of Bitcoin (0.1), as released in February 2009?

里面提供了v0.1版本的链接


二、根据readme.txt中的要求配置好依赖库

解压压缩包后,可以发现根目录下是一个已经编译好的bitcoin.exe文件,而源码全部在src/目录下。

在src/目录下有一个 readme.txt文件(是 src/readme.txt 不是根目录下的readme.txt),打开可以看到:

Dependencies

Libraries you need to obtain separately to build:

default path download
wxWidgets \wxWidgets http://www.wxwidgets.org/downloads/
OpenSSL \OpenSSL http://www.openssl.org/source/
Berkeley DB \DB http://www.oracle.com/technology/software/products/berkeley-db/index.html
Boost \Boost http://www.boost.org/users/download/

由此可知,为了编译bitcoin需要先配置好这4个依赖库。

以下就是重点:

1. wxWidgets (3.1.0 版本)

我采用的是 wxWidgets - v3.1.0 注意版本号!!!

这是个神坑。如果你和我一样采用v.3.1.0之后,你将会遇到无尽的困难!!!

这个请根据自己情况做出选择,或许采用 v2.8 以下的版本会好很多。我没有去查找2009年时wxWidgets时候的版本。如果为了避免问题,可以尝试选用 2009 年 二月份 之前所 release 的wxWidgets 版本。。。

如果你和我一样选择wxWidgets 3.1.0。。 呃 请做好接下来修改大量代码的准备。。

  1. 使用 visual studio 编译源码:(我使用的)

The simplest way to start working with the library is this(当然我只选择了为win 32 debug 进行编译,没有全部编译)

选择vs编译没有 unicode 开关

  1. 其他方式:(请注意,使用其他方式编译可以选择unicode开关,这个对后面很重要,请根据选择来MSVC Setup Guide)
    1. msvc

wxWidgets Tips: Compile wxWidgets 3.1.0 using Visual Studio 2015

    1. minGW

Compiling wxWidgets with MinGW


2. openssl (1.0.1 版本) (注:似乎1.0.2后会出问题)

openssl 要使用源码编译很麻烦。。要安装perl

有一个取巧的地方:

Win32 OpenSSL

通过这种方法可以不需要编译就安装openssl库。注意,这里一定选择x86(32bit)版本,因为我们只编译32bit的源码。

如果要选择从源码编译,则除了安装perl后,在编译32bit版本的时候还会遇到汇编部分编译不通过的情况。这时候就只能再安装nasm汇编器进行编译(我没试过。。)

详情查阅这个链接 The simplest way to start working with the library is this:

注意!!!

选择这些直接编译好的二进制文件不清楚似乎有些版本有问题,在我使用python直接加载上面的那个链接下载的openssl中的dll后(1.0.2k 或者 1.0.2l),对于bitcoin使用的椭圆曲线会 概率性 的不能通过签名验证这个流程的,暂时不清楚其中的原因。对于这种问题,我采用其他编译的dll,但是这只是为了实验,如果是为了安全还是应该选择自己编译openssl (但是问题似乎在于1.0.2之后的版本会出现问题)

http://indy.fulgan.com/SSL/

经过验证,在上面这个版本的二进制中,1.0.1g能够通过验证反而1.0.2以后不能通过,不能通过(我只验证了1.0.2 j~l 这几个版本)

3. Berkeley DB (4.8.30 版本)

这tm就是个神坑,我后来在这里卡了好久。。。

Berkeley DB 现在归属 oracle (默默吐槽,oracle指染的东西每一个好用。。

这里我强烈建议不要使用最新版本的Berkeley db 进行构建。至少连现阶段的0.13.99版本的bitcoin都使用的是4.8版本的db。使用高版本不仅接口不对,很可能时候连环境都配不好(我瞎说的)

所以这里选择 Berkeley DB v4.8 版本。这里倒是有安装包,直接安装就好了,但是我要强烈黑他一下,因为我并不清楚我是不是个例。直接使用安装包提供的debug版本的 dll 会出问题!!!

在配置章节和下一篇会对这个问题有详细描述。

4. Boost (1.63.0 版本)

这个是最简单的

因为Boost完全不需要配置:介绍及下载链接

Nothing to Build?
Most Boost libraries are header-only: they consist entirely of header files containing templates and inline functions, and require no separately-compiled library binaries or special treatment when linking.

但只可惜因为c++11的关系,要剔除Boost是最容易的。。。而且在bitcoin源码中几乎没有什么存在感

使用vs的从“源码导入” 导入bitcoin源码

这里我们把 src/ 目录当作根目录。

新建立一个目录,比如叫做bitcoin什么的随便好啦。然后把src/目录下的所有文件都拷贝到这个新的目录下。我们现在把这个新的目录当做 / 。之后的描述都针对 / 作为根目录进行。

打开vs

  1. 点击 新建
  2. 从现有代码创建项目
  3. 弹出对话框,选择默认的Visual C++ ,点击下一步
  4. 项目位置填写上刚才的 / 目录的位置,项目名称取名 bitcoin-v0.1或者其他你喜欢的项目名字,点击下一步
  5. 使用visual studio -> 项目类型选择 “Windows 应用程序项目” (就是说直接使用默认),其他的不动,点击下一步
  6. 点击完成

此时就已经新创建了项目,并导入的源码。


三、配置

这步是很关键的第一步

现在我们先假设把刚才配置好的4个依赖库都设置成环境变量(如果不设置环境变量也行,那么等会在配置项目的依赖的时候就填写全路径就好了。)

回到桌面->对“此电脑”右键->属性->高级系统设置->环境变量->在系统变量的部分开始创建环境变量

点击新建:

举例: wsWidgets => 那么就在变量名写上 WXWidgetsPath ,然后变量值填写刚才安装(解压)widgets的根目录

其他3个依赖库同理,则总共配置了4个环境变量:

  • WXWidgetsPath
  • OpenSSLPath
  • BDBPath
  • BoostPath

当然上面这4个环境变量的名字是我乱取的,并没有什么约定,你可以根据自己想象取想取的名字。这里取这4个变量等会我们就会在项目的配置中引用这4个变量,自己取的名字等会就换成自己取的就好了。

此时先关闭刚才创建的项目,然后再重新启动(这个是为了让vs加载刚才取的环境变量,如果没配置环境变量用不着重启vs)

在解决方案中右键刚才创建的项目,选择属性。

1. 设置unicode 字符集

注意,这步的配置不是必须,如果你的wxWidgets是老版本(似乎是小于2.8?)或者你在编译wxWidgets库的时候选择关闭unicode开关,编译除了非unicode的wxWidgets的库时,可以直接跳过这步,不设置unicode字符集。否则在等会启动编译的时候会出现wxWidgets的setup.h找不到路径。

如果你是vs编译的,那么一定要设置unicode字符集。

在 配置属性-常规 中 找到 项目默认值 - 字符集

点击下拉框,选择 使用 Unicode 字符集

2. 设置VC++依赖

在 配置属性-VC++目录 下找到 包含目录库目录,点击 包含目录,填写

$(WXWidgetsPath)\include
$(WXWidgetsPath)\include\msvc

(注意这里的WXWidgetsPath就是刚才配置的环境变量,如果没有配置,就直接填写wxWidgets的全路径加上include路径,以下同理)

这样就引入了 wxWidgets 库的 头文件路径 以及 lib 路径。

注:因为wxWidgets库是和windows应用程序绑定的,不是普通的c++依赖库,所以必须在VC++的部分设置,否则,wxWidgets会不能通过编译。

3. 设置C++依赖

在 配置属性 - c/c++ - 常规 中 找到 “附加包含目录”,点击编辑,填写:

 $(BoostPath)
 $(OpenSSLPath)\include
 $(BDBPath)\include

这样就引入了 boost,openssl, bdb的头文件路径

在 配置属性 - 链接器 -常规 中找到 “附加库目录”,点击编辑,填写:

$(OpenSSLPath)\lib
 $(BDBPath)\lib  // 注意这条可能根据你的环境改变

(加粗|醒目) oracle神坑的地方在这里就体现出来了。$(BDBPath)\lib指代的 bdb 的 libs 的路径,但是!因为我们编译的是 debug 项目,所以等会只能引入 debug 的dll(lib) (如果正常应该是可以引入release的lib的,但是对于bdb来说,debug项目使用release的dll 在运行free()的时候会崩溃。。。)。

但是引入debug后,源安装包中的 debug的dll似乎是有问题的(加粗),会直接引起程序崩溃!!(不清楚我是不是个例。如果出现了这个问题,请重新在自己的电脑上编译debug版本的bdb,把$(BDBPath)\lib换成新的路径,参考下一篇文章)

在 配置属性 - 链接器 -输入 中找到 “附加依赖项”,点击编辑,填写:

libeay32.lib
libdb48d.lib  // 注意这个是带 'd' 的,也就是 debug 的dll库

然后打开资源管理器(就是点我的电脑。。),找到这个项目的目录,在本项目根目录 / 下创建 libs/ 目录,拷贝

$(OpenSSLPath) 目录下的 libeay32.dll

$(BDBPath)\bin\debug 目录下的libdb48d.dll

进入 libs 目录。

注意,刚才描述的问题的dll就是这个 libdb48d.dll, 如果出问题了,很可能需要替换成自己编译的dll

然后回到 VS 的项目配置中

在 配置属性 - 生成事件 - 后期生成事件 中找到 “命令行”

填写上:

xcopy /y /d  "$(ProjectDir)libs\*.dll" "$(OutDir)"

这个command的 libs 就是 刚才在项目的根目录下创建的 libs/ 目录。所以这句话的意思就是 把 libs/ 目录下的所有 dll 都拷贝到输出目录中(这里指代的就是 Debug 目录)


四、修改源码

错误

1. headers.h

vc6++ 与 vc9以上平台冲突

因为源码应该是在vc6++的平台下编译完成的,所有首先要先移除对于winnt的预定义,否则会产生兼容性问题。

找到headers.h文件,在第10行发现:

#ifdef _WIN32_WINNT
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0400 // 删除这4行

将这4行全部都注释掉,或者删除。

windows.h 与 winsokc2.h 顺序

找到headers.h文件第23行发现:

 #include <windows.h> 
 #include <winsock2.h>
 #include <mswsock.h> // 把 windows.h移动到最下面

移动windows.h得到

#include <winsock2.h>
#include <mswsock.h>
#include <windows.h>

或者引入一个宏也可以解决windows.h头文件引入的问题:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
 #include <windows.h>
 // ...
 #include "MyClass.h"    // Which includes <winsock2.h>

之后找到 net.cpp 文件,第5行交换头文件引入

#include "headers.h"
 #include <winsock2.h> // 和上面一样互换这两行
 // =>
 #include <winsock2.h> 
 #include "headers.h"

2. c++11 与现存代码冲突

net.h/net.c boost::array 与 c++11 的 std::array 冲突

找到 net.h 文件,在第419行发现

extern array<bool, 10> vfThreadRunning; // 添加命名空间
//  修改为 => 
 extern boost::array<bool, 10> vfThreadRunning; // 添加命名空间

找到 net.c 文件,第26行进行同样更改

array<bool, 10> vfThreadRunning; 
//  修改为 => 
 boost::array<bool, 10> vfThreadRunning;

winsock.h 的 bind 函数 与 std::bind 冲突

找到 net.c 文件, 第937行

if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr))
 == SOCKET_ERROR) // bind函数冲突
// 转变为=>
 if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, 
sizeof(sockaddr)) == SOCKET_ERROR) // 添加上::global 命名空间

3. serialize.h insert函数 const char*想要强制转换为const_iterator

找到serialize.h 文件,在第 808 行发现一个根据宏的定义

#if !defined(_MSC_VER) || _MSC_VER >= 1300
    void insert(iterator it, const char* first, const char* last)
    {
        insert(it, (const_iterator)first, (const_iterator)last);
    } 
#endif

修改为

#if !defined(_MSC_VER) || _MSC_VER >= 1300
    void insert(iterator it, const char* first, const char* last)
    {
        vector_type v(first, last);
        insert(it, v.begin(), v.end());
    } 
#endif

4. 似乎是关于MSVC8 allocator的兼容问题(不确定)

文件serialize.h 第699行

typedef vector<char,const secure_allocator<char> > vector_type;
// 修改为=>也就是把 作者原来写的这个 secure_allocator 移除,使用vector自带的。
 typedef vector<char> vector_type;

同时因为 vector_type的typedef已经等同于vector<char>,所以对于CDataStream类的构造函数也要更改

文件 serialize.h第741行, 删除或着注释掉这个构造函数

//CDataStream(const vector<char>& vchIn, int nTypeIn=0, int 
VersionIn=VERSION) : vch(vchIn.begin(), vchIn.end())
//{
//    Init(nTypeIn, nVersionIn);
//}

文件key.h 第40行

typedef vector<unsigned char, const secure_allocator<unsigned char> > 
CPrivKey;
// => 修改方式同上
typedef vector<unsigned char> CPrivKey;

5. unicode 字符的替换

ps: 若使用低版本的wxwidgets,总之就是没有开启unicode宏的话不需要替换

util.cpp文件的第74行

long ret = RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Global", NULL, 
NULL, pdata, &nSize);
// 把 “Global” 字符串该为 win32 支持的unicode => 
long ret = RegQueryValueEx(HKEY_PERFORMANCE_DATA, L"Global", NULL, 
NULL, pdata, &nSize);
// 或者换成另一个版本的win API也行
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, 
ULL, pdata, &nSize);

第173行

GetModuleFileName(NULL, pszModule, sizeof(pszModule));
//在 unicode 宏定义下的GetModuleFileName是宽字符版本,直接替换成Acsii版本=>
GetModuleFileNameA(NULL, pszModule, sizeof(pszModule));

第272行

return GetFileAttributes(psz) != -1;
// 与上面同理 =>
 return GetFileAttributesA(psz) != -1;

util.h 文件的第274行

OutputDebugString(p1);
// 与上同理  =>
 OutputDebugStringA(p1);

6. ui.cpp / ui.h 错误

ui.cpp 第254行,关于wsString的迭代器填充构造函数无法自动转化的问题 (或许当 wxWidgets版本低的时候不会出现这个问题)

return CDataStream(strData.begin(), strData.begin() + event.GetInt(), SER_NETWORK);
// 替换成为 =>
const char* s = strData.mb_str();
return CDataStream(s, s + event.GetInt(), SER_NETWORK);

类似的 第2650, wsString 到 std::string的转换

wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), 
strValue));
// 把 wsString 调用 ToStdString()  =>
wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]-
>GetLabel().ToStdString(), strValue));

ui.cpp 第1245行,字符缺少引号

ps: 强调!这里缺少引号,是因为本来源码中这里是个十分奇怪的字符,在导入vs后打开的时候就被转义了。所以这里的?实际上是代表原来的一个字符。但是这个函数并没什么鸟用,所以这里随便改正就好,不用还原原来的意思。

if (str.Find('?) != wxNOT_FOUND)
    str.Remove(str.Find('?), 1);
//  添加上缺失的引号
if (str.Find('?') != wxNOT_FOUND)
    str.Remove(str.Find('?'), 1);

2890行,宽字符

_CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, 
OPEN_EXISTING, 0, 0));
// =>
_CrtSetReportFile(_CRT_WARN, CreateFile(L"NUL", GENERIC_WRITE, 0, NULL, 
OPEN_EXISTING, 0, 0));

2913行,同上

HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");
// =>
HWND hwndPrev = FindWindow(L"wxWindowClassNR", L"Bitcoin");

3148行,对于3.0以上的wxWidgets版本来说 AddPendingEvent()函数已废弃,需要依靠eventhandler才能调用。

//pframeMain->AddPendingEvent(event);
pframeMain->GetEventHandler()->AddPendingEvent(event);

7. 去除因跨平台带来的兼容问题

文件util.h 第161行

inline string i64tostr(int64 n)
{
    return strprintf("%"PRId64, n);  
// 其中的PRId64 是为了跨平台带来的,修改为下面的代码
  // => 因为这里不关心跨平台问题,只把环境限制在win64上,所以不必担心int64的问题
    return strprintf("%l", n);
}

8. berkeley db 版本为6.2+后的修改兼容 (使用4.7或4.8版本则无需考虑)

文件db.cpp 第18行

DbEnv dbenv(0);
// =>
DbEnv dbenv((u_int32_t)0);

警告

源码中出现的警告多半是因为符号隐式转换的问题,这个根据自己需要修改就行。

六、运行

以上的部分只是解决在编译期出现的问题,真正的大头是需要让bitcoin能够运行起来(惨····)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多