前言:最近区块链概念十分火热,“bitshares”、“steemit”、“EOS”、“智能合约”等相关技术大家讨论起来也都头头是道,作为一个传统的学习编程的爱好者,我希望的是区块链入门的学习秘籍,从网上发现热帖“用Python从零开始创建区块链”,看后算是入门,但是需要在Linux下利用Python完成区块链的创建,对于初学者来说如果看懂尚可,但如果想像学习C语言课程那样调试一下,类似于“Hello World”那种感觉仍然有难度。考虑到编程初学者基本都有VS开发环境下C语言的基础,我准备推出一个区块链学习入门系列文章。首先介绍windows下利用VS2015构建本地版的完整区块链(称作“微链V0.1”),然后介绍加入网络服务接口的完整区块链(称作“微链 V0.2”)……
准备工作
“微链V0.1”运行环境:Win10+VS2015
源代码下载地址:
https://github.com/laowanghai/MyMiniBlockChain.git
要求读者对VS有基本的理解,能读写基本的C/C++语言。掌握VS的调试方法,可以轻松下个断点,查看区块链数据的变化(这种感觉就是我写微链V0.1的初衷)。
什么是区块链技术
所谓区块链技术, 简称BT(Blockchain technology),也被称之为分布式账本技术,是一种互联网数据库技术,其特点是去中心化、公开透明,让每个人均可参与数据库记录。
区块链的基本原理理解起来并不难。基本概念包括:
l交易(Transaction):一次操作,导致账本状态的一次改变,如添加一条记录;
l区块(Block):记录一段时间内发生的交易和状态结果,是对当前账本状态的一次共识;
l链(Chain):由一个个区块按照发生顺序串联而成,是整个状态变化的日志记录。
如果把区块链作为一个状态机,则每次交易就是试图改变一次状态,而每次共识生成的区块,就是参与者对于区块中所有交易内容导致状态改变的结果进行确认。
更多区块链技术的介绍可以参见:华为的区块链白皮书https://static.huaweicloud.com/upload/files/pdf/20180411/20180411144924_27164.pdf
开始创建区块链
首先定义区块链的数据结构体如下:
typedefstructBlock
{
intindex;
charprevious_hash[65];
intproof;
chartimeStamp[18];
char* transactions;
Block* next;
}*pBlock;
每个区块包含属性:索引(index),时间戳(timestamp),交易记录(transactions),工作量证明以及前一个区块的Hash值。
在此基础上,定义CMiniBlockChain类:
classCMiniBlockChain
{
public:
CMiniBlockChain();
~CMiniBlockChain();
char*getStrSHA256Result(constchar* str,longlonglength,char* sha256);
Block* getBlockChain() {returnm_pMyBlock; };
BOOLinitBlockChain(char*ts);
intgetBlockLength();
BOOLgetTimeStamp(char*ts);
voidgetLastBlockStr(char**bs);
voidgetBlockStr(Block* pb,char**bs);
voidgetBlockStrForHash(Block* pb,char**bs);
Block* getLastBlock();
voidnewTransaction(intamount,char* recipient,char* sender);
BOOLnewBlock();
intproof_of_work(intlastProof);
BOOLvalid_proof(intlast_proof,intproof);
BOOLdeleteNode(intindex);
voidregisterNode(char* node);
intvalid_chain();
char* getNodeRegister() {returnm_strNodeRegister; };
BOOLcopyChain(Block* src,intlength);
private:
Block* m_pMyBlock;
char* m_pTransactionInfo;
char* m_strNodeRegister;
};
下面结合区块链的业务具体介绍代码实现。为了完整地模拟区块链工作场景,我在CMyMiniBlockChainView视图类中添加了两个成员变量:
CMiniBlockChain* m_pMiniBlockChain;
CMiniBlockChain* m_pMiniBlockChain2;
分别模拟区块链节点1和区块链节点2,并将区块链节点1数据显示在主视图中,利用停靠窗口显示区块链节点2的数据。如图所示:
需要说明的是,区块链节点1数据为了显示的美观,对其进行了排版处理,区块链节点2数据则就是用于哈希计算的字符串,这样便于读者理解。
创建创世块
voidCMyMiniBlockChainView::OnCreateWorldBlock()
{
chartimeStamp[18];
m_pMiniBlockChain->getTimeStamp(timeStamp);
m_pMiniBlockChain->initBlockChain(timeStamp);
m_pMiniBlockChain2->initBlockChain(timeStamp);
//更新区块链节点1视图
Invalidate();
UpdateWindow();
//更新区块链节点2视图
DrawChain2Info();
}
其中getTimeStamp用于获取当前时间戳(精确到微秒),Windows环境下没有现成的函数,这里直接参考doubango(Doubango 是当前世界上最好的一个基于3GPP IMS/RCS 并能用于嵌入式和桌面系统的开源框架)中的tsk_time.c 源文件,具体见代码。
initBlockChain用于创建区块链中的创世块,设置创世块的序号为1,前区块哈希值为"1",工作量证明为100,时间戳为创建时刻的时间戳。
BOOLCMiniBlockChain::initBlockChain(char*ts)
{
//创建创世块
m_pMyBlock = (Block*)malloc(sizeof(structBlock));
m_pMyBlock->index = 1;
strcpy(m_pMyBlock->previous_hash,"1");
m_pMyBlock->proof = 100;
strcpy(m_pMyBlock->timeStamp,ts);
m_pMyBlock->transactions=NULL;
m_pMyBlock->next =NULL;
returntrue;
}
新建交易
newTransaction函数用来新建一笔交易信息,主要包含交易数量,交易双方信息,其中利用UUID来记录交易双方ID,如果交易发起方为"0"字符串则代码是矿工挖矿的奖励。如果没有交易记录,则区块交易信息只记录矿工信息,否则m_pTransactionInfo变量将同时记录交易信息和矿工信息。
voidCMiniBlockChain::newTransaction(intamount,char*recipient,char*sender)
{
if(m_pTransactionInfo ==NULL)
{
m_pTransactionInfo = (char*)malloc(MAX_TRANSACTIONS_INFO_LENGTH);
sprintf(m_pTransactionInfo,"\n {\n \"amount\": %d,\n \"recipient\" : \"%s\",\n \"sender\" :\"%s\"\n }",amount,recipient,sender);
}
else
{
char* temp= (char*)malloc(strlen(m_pTransactionInfo)+1);
strcpy(temp, m_pTransactionInfo);
m_pTransactionInfo = (char*)malloc(strlen(temp)+MAX_TRANSACTIONS_INFO_LENGTH);
strcpy(m_pTransactionInfo, temp);
strcat(m_pTransactionInfo,",\n");
sprintf(temp,"\n {\n \"amount\":%d,\n \"recipient\" :\"%s\",\n \"sender\": \"%s\"\n }",amount,recipient,sender);
strcat(m_pTransactionInfo, temp);
free(temp);
}
return;
}
新建区块
newBlock用于新增区块,每个区块的产生都需要经过工作量证明,俗称挖矿,proof_of_work用于计算工作量证明,这里采用简单的工作量证明算法,即前一个区块的工作量与新建区块的工作量在一起哈希运算的前四个字符应都为0。
intCMiniBlockChain::proof_of_work(intlastProof)
{
intproof=0;
while(!valid_proof(lastProof, proof))
proof++;
returnproof;
}
valid_proof函数用于验证工作量证明是否满足要求,其中需要利用getStrSHA256Result函数计算哈希值,为了便于理解,我直接给出了一份网上的计算哈希值的源码。
BOOLCMiniBlockChain::newBlock()
{
Block* pTemp = getLastBlock();
if(pTemp ==NULL)
{
AfxMessageBox(_T("必须先创建\"创世块\",然后进行其它区块链操作!"));
returnfalse;
}
Block* qTemp = (Block*)malloc(sizeof(structBlock));
if(qTemp ==NULL)
{
AfxMessageBox(_T("Memory Leak!"));
returnfalse;
}
qTemp->index = getBlockLength()+1;
char*bs=NULL;
getLastBlockStr(&bs);
//获取上一个区块的HASH值
getStrSHA256Result(bs,strlen(bs),qTemp->previous_hash);
qTemp->proof =proof_of_work(pTemp->proof);
getTimeStamp(qTemp->timeStamp);
if(m_pTransactionInfo !=NULL)
{
qTemp->transactions = (char*)malloc(sizeof(char) *(strlen(m_pTransactionInfo) + 1));
strcpy(qTemp->transactions,m_pTransactionInfo);
}
else
qTemp->transactions =NULL;
free(m_pTransactionInfo);
m_pTransactionInfo =NULL;
qTemp->next =NULL;
pTemp->next = qTemp;
free(bs);
returntrue;
}
验证区块链
valid_chain函数通过遍历每个块验证哈希值和工作量证明验证区块链数据是否完整。
/*
:链为空
-1:链数据有错误
1:链数据完整
*/
intCMiniBlockChain::valid_chain()
{
Block* pTemp = m_pMyBlock;
if(pTemp ==NULL)
return0;
while(pTemp->next !=NULL)
{
Block* qTemp = pTemp->next;
char* bs;
charprevious_hash[65];
getBlockStrForHash(pTemp, &bs);
getStrSHA256Result(bs, strlen(bs),previous_hash);
if(_stricmp(previous_hash, qTemp->previous_hash) != 0)
return-1;
if(!valid_proof(pTemp->proof, qTemp->proof))
return-1;
pTemp = pTemp->next;
}
return1;
}
到此,我们的区块链基本功能就完成了,区块链系统应该是分布式的,为了理解方便,添加了两个成员变量模拟分布式的两个节点区块链节点1和区块链节点2。
注册节点
每个节点都需要保存一份包含网络中其它节点的记录。变量 m_strNodeRegister用于记录其他节点。
voidCMiniBlockChain::registerNode(char*node)
{
if(node!=NULL)
{
//之前注册节点为空
m_strNodeRegister = (char*)malloc(strlen(node) + 1);
strcpy(m_strNodeRegister,node);
}
elseif(strstr(m_strNodeRegister,node) ==NULL)
{
//注册节点不在已经注册节点之中
intlength = strlen(m_strNodeRegister) + 1;
char*temp = (char*)malloc(length);
strcpy(temp, m_strNodeRegister);
m_strNodeRegister = (char*)malloc(length +strlen(node) + 3);
strcpy(m_strNodeRegister, temp);
strcat(m_strNodeRegister,";");
strcat(m_strNodeRegister,node);
free(temp);
}
return;
}
例如节点1注册节点2为自己的其他同步节点。
voidCMyMiniBlockChainView::OnRegisterNode2forNode1()
{
m_pMiniBlockChain->registerNode(m_strNode2Uuid);
AfxMessageBox(_T("注册区块链节点2为区块链节点1的同步节点成功!"));
}
共识机制
采用简单的共识机制,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。
例如对于节点1解决冲突代码如下,如果有注册的同步节点,则如果同步节点的链是有效链,且比自己的链长,则将同步节点的链拷贝过来。
voidCMyMiniBlockChainView::OnConsensusForNode1()
{
//共识算法解决冲突,使用网络中最长的链作为共识
if(m_pMiniBlockChain->getNodeRegister()==NULL)
AfxMessageBox(_T("当前没有注册同步节点!"));
elseif((m_pMiniBlockChain->getBlockLength() getBlockLength())
&&(m_pMiniBlockChain2->valid_chain() == 1))
m_pMiniBlockChain->copyChain(m_pMiniBlockChain2->getBlockChain(),m_pMiniBlockChain2->getBlockLength());
Invalidate();
UpdateWindow();
return;
}
界面设计
所有操作通过菜单实现,区块链菜单下有创建创世块和两个节点的各自功能。
区块链节点1设计了模拟挖矿、模拟交易、注册区块链节点2和共识机制功能。
区块链节点2还额外实现了获取区块链数据和验证区块链数据功能。
可以通过在区块链节点2窗口中利用右键菜单进行剪切操作认为破坏区块链数据,然后再验证区块链数据。
后记
微链V0.1是本地版本的,后续将推出V0.2,即对其进行Web应用改造,实现分布式区块链。
领取专属 10元无门槛券
私享最新 技术干货