密码学专题训练
实 验 报 告
实验名称 实验三 区块链专题
一、实验目的
1.理解分布式存储的相关概念和原理;
2.理解传统分布式系统存在的弊端和安全威胁;
3.了解基本的共识机制和共识算法;
4.理解并掌握Hash函数设计思想和应用场景;
5.理解并掌握公钥密码体制在保护身份信息中的作用;
6.了解区块链当前存在的技术瓶颈。
二、实验环境
1.PC计算机 Windows 11
2.DEV
- 实验原理
分布式存储是一种将数据分散存储在多个节点上的存储方式,每个节点都可以独立地存储和访问数据。这种分布式的存储方式可以提供更高的可靠性、可扩展性和性能。
分布式存储的原理是将数据切分成多个块或对象,并分布在多个存储节点上。每个节点都可以存储一部分数据,并提供数据的读写操作。通过将数据分布在多个节点上,分布式存储系统可以实现数据的冗余备份和自动恢复,从而提供高可用性和数据的可靠性。分布式存储系统通常具有以下特点:
可扩展性:分布式存储系统可以根据需求动态地扩展存储容量和性能。通过添加新的存储节点,系统可以线性地扩展存储能力,以适应不断增长的数据量和访问需求。
高可用性:由于数据被冗余备份在多个节点上,当某个节点发生故障时,系统可以自动将数据从其他节点恢复到新的节点上,保证数据的可用性和可靠性。
高性能:分布式存储系统可以通过并行处理和负载均衡来提供高性能的数据访问。数据可以并行地从多个节点读取或写入,从而提高数据的访问速度。
弹性和容错性:分布式存储系统具有弹性和容错性,可以适应节点故障、网络故障和其他异常情况。系统可以自动检测和修复故障,保证数据的完整性和可用性。
数据一致性:分布式存储系统通常提供一致性模型,确保数据在多个节点之间的一致性。系统可以通过复制、副本和数据同步机制来实现数据的一致性。
传统分布式系统是指由多台计算机通过网络连接,协同完成共同的任务的系统。传统分布式系统相比于集中式系统,具有一些优点,如可扩展性、高可用性、资源共享等。但是,传统分布式系统也面临着一些挑战和问题,主要有以下几个方面:
通信开销:传统分布式系统需要通过网络进行数据的传输和同步,这会增加通信的延迟和成本,影响系统的性能和响应速度。同时,网络的不可靠性也会导致数据的丢失、重复、乱序等问题,给系统的一致性和正确性带来困难。
数据一致性:传统分布式系统中,数据被分散存储在多个节点上,每个节点都可以对数据进行读写操作。这就需要保证数据在多个节点之间的一致性,即不同节点上的数据应该是相同的或者等价的。但是,由于网络的延迟和故障,以及节点的并发操作,数据的一致性很难实现,需要采用复杂的一致性协议和算法,如两阶段提交、Paxos、Raft等。
事务管理:传统分布式系统中,事务是指一组需要原子性、一致性、隔离性和持久性的操作。事务管理是指保证事务的正确执行和提交的过程。在分布式系统中,事务管理比较复杂,因为涉及到多个节点的协调和同步,以及网络的不确定性和故障的处理。传统分布式系统需要实现分布式事务的机制,如分布式锁、分布式日志、分布式恢复等。
安全性:传统分布式系统中,数据和服务需要在不同的节点和网络之间进行传输和访问,这就存在着数据的安全性和保密性的风险。例如,数据可能被窃取、篡改、伪造等,服务可能被拒绝、攻击、滥用等。传统分布式系统需要实现数据和服务的安全保护的机制,如加密、认证、授权、审计等。
共识机制是分布式系统中实现一致性的重要机制。在分布式系统中,多个节点需要共同达成一致的决策,例如在区块链系统中确定下一个区块的生成者、在分布式数据库中确定数据更新的先后顺序等。
共识算法是实现共识机制的具体方法,不同的共识算法有不同的特点和适用场景。常见的共识算法包括:
工作量证明(Proof of Work,PoW):是比特币等区块链系统采用的共识算法,它要求节点通过解决复杂的数学难题来竞争记账权,从而保证网络的安全性和去中心化。
权益证明(Proof of Stake,PoS):是一种替代PoW的共识算法,它要求节点根据自己持有的代币数量或者抵押的代币数量来参与记账,从而减少能源消耗和算力竞争。
委托权益证明(Delegated Proof of Stake,DPoS):是一种改进的PoS共识算法,它要求节点通过投票选举出一定数量的超级节点来负责记账,从而提高网络的效率和稳定性。
实用拜占庭容错算法(Practical Byzantine Fault Tolerance,PBFT):是一种基于投票的共识算法,它要求节点通过多轮的消息交换来达成一致,从而支持容错故障节点和恶意节点。
Raft算法:是一种简化的PBFT算法,它要求节点通过选举出一个领导者来负责日志复制和提交,从而实现数据的一致性。
比特币(Bitcoin)是一种去中心化的数字货币,由一个人或一群人在2008年使用化名中本聪(Satoshi Nakamoto)的身份提出。它的设计允许用户在没有中央权威机构,比如银行或政府的情况下,进行点对点的交易。
区块链(Blockchain)
区块链是比特币的底层技术,它是一个公开的分布式账本,记录着所有比特币交易的历史。每个区块包含一系列交易,并且每个新区块都会被添加到链上,形成一系列的区块链。区块链的这种设计确保了交易记录不能被修改,因为任何对旧区块的更改都需要重新计算后续所有区块的复杂加密算法。
挖矿(Mining)
比特币网络依赖于“挖矿”过程来确认交易并添加新的比特币到系统中。挖矿是一个竞争性的过程,矿工使用强大的计算能力解决复杂的数学难题以赢得新区块的创建权。每当一个新区块被创建,矿工会得到一定数量的比特币作为奖励,这是比特币进入流通的方式。
去中心化
比特币是去中心化的,没有中央服务器或管理机构。每个在比特币网络上的节点(计算机)都拥有一个完整的区块链副本,这使得比特币网络对任何单点故障具有极高的抵抗力。
四、实验核心步骤及代码
本次实验,我决定通过阅读比特币源码,学习区块链的相关知识。
节点将新交易收集到一个块中,将它们散列到哈希树中,并扫描nonce值以使块的哈希值满足工作量证明要求。当他们解决工作量证明时,他们将区块广播给每个人,并将该区块添加到区块链中。区块中的第一笔交易是一笔特殊的交易,它创造了一个由创造者拥有的新硬币
#define foreach BOOST_FOREACH
class CBlockIndex;
class CDiskBlockIndex;
const uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
CBlockIndex* pindexBest = NULL;
CBlockIndex* pindexGenesisBlock = NULL;
map<uint256, CBlockIndex*> mapBlockIndex;
unsigned int nTransactionsUpdated = 0;
int nBestHeight = -1;
uint256 hashBestChain = 0;
string GetAppDir()
{string strDir;
if (!strSetDataDir.empty())
{ strDir = strSetDataDir; }
else if (getenv("APPDATA"))
{strDir = strprintf("%s\\Bitcoin", getenv("APPDATA")); }
else if (getenv("USERPROFILE"))
{string strAppData = strprintf("%s\\Application Data", getenv("USERPROFILE"));
static bool fMkdirDone;
if (!fMkdirDone)
{ fMkdirDone = true;
_mkdir(strAppData.c_str()); }
strDir = strprintf("%s\\Bitcoin", strAppData.c_str()); }
else
{ return "."; }
static bool fMkdirDone;
if (!fMkdirDone)
{ fMkdirDone = true;
_mkdir(strDir.c_str()); }
return strDir;}
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{ if (nFile == -1)
return NULL;
FILE* file = fopen(strprintf("%s\\blk%04d.dat", GetAppDir().c_str(), nFile).c_str(), pszMode);
if (!file)
return NULL;
if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
{ if (fseek(file, nBlockPos, SEEK_SET) != 0)
{ fclose(file);
return NULL; } }
return file;}
class CBlock
{public:
// header
int nVersion; //版本号
uint256 hashPrevBlock; //前一区块的哈希值
uint256 hashMerkleRoot; //Merkle根
unsigned int nTime; //时间戳
unsigned int nBits; //难度目标
unsigned int nNonce; //随机数
// network and disk
vector<CTransaction> vtx; //交易
// memory only
mutable vector<uint256> vMerkleTree;
//构造函数
CBlock()
{ SetNull(); }
//存储时需要序列化
IMPLEMENT_SERIALIZE
(READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
// ConnectBlock depends on vtx being last so it can calculate offset
if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
READWRITE(vtx);
else if (fRead)
const_cast<CBlock*>(this)->vtx.clear(); )
//初始化值置0
void SetNull()
{ nVersion = 1;
hashPrevBlock = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
vtx.clear();
vMerkleTree.clear(); }
//得到区块头的哈希值
uint256 GetHash() const
{return Hash(BEGIN(nVersion), END(nNonce)); }
//生成MerkleTree
uint256 BuildMerkleTree() const
{ vMerkleTree.clear();
foreach(const CTransaction& tx, vtx)
vMerkleTree.push_back(tx.GetHash());
int j = 0;
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{ for (int i = 0; i < nSize; i += 2)
{int i2 = min(i+1, nSize-1);
vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); }
j += nSize; }
return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); }
//开始区块数据存储部分
//得到区块存储的文件夹
string GetAppDir()
{ string strDir;
if (!strSetDataDir.empty())
{strDir = strSetDataDir; }
else if (getenv("APPDATA"))
{strDir = strprintf("%s\\Bitcoin", getenv("APPDATA"));}
else if (getenv("USERPROFILE"))
{ string strAppData = strprintf("%s\\Application Data", getenv("USERPROFILE"));
static bool fMkdirDone;
if (!fMkdirDone)
{fMkdirDone = true;
_mkdir(strAppData.c_str());
}strDir = strprintf("%s\\Bitcoin", strAppData.c_str());
}
else
{return ".";}
static bool fMkdirDone;
if (!fMkdirDone)
{ fMkdirDone = true;
_mkdir(strDir.c_str());
} return strDir;}
//打开区块文件
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{ if (nFile == -1)
return NULL;
FILE* file = fopen(strprintf("%s\\blk%04d.dat", GetAppDir().c_str(), nFile).c_str(), pszMode);
if (!file)
return NULL;
if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
{ if (fseek(file, nBlockPos, SEEK_SET) != 0)
{ fclose(file);
return NULL; }}
return file;}
//打开区块文件,追加
FILE* AppendBlockFile(unsigned int& nFileRet)
{nFileRet = 0;
loop
{FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
if (!file)
return NULL;
if (fseek(file, 0, SEEK_END) != 0)
return NULL;
// FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
if (ftell(file) < 0x7F000000 - MAX_SIZE)
{ nFileRet = nCurrentBlockFile;
return file; }
fclose(file);
nCurrentBlockFile++; }}
//区块数据存储到文件中
bool WriteToDisk(bool fWriteTransactions, unsigned int& nFileRet, unsigned int& nBlockPosRet)
{ // Open history file to append
CAutoFile fileout = AppendBlockFile(nFileRet);
if (!fileout)
return error("CBlock::WriteToDisk() : AppendBlockFile failed");
if (!fWriteTransactions)
fileout.nType |= SER_BLOCKHEADERONLY;
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(pchMessageStart) << nSize;
// Write block
nBlockPosRet = ftell(fileout);
if (nBlockPosRet == -1)
return error("CBlock::WriteToDisk() : ftell failed");
fileout << *this;
return true;
}
//打印区块数据信息
void print() const
{printf("\nBlock Info is:\n");
printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
GetHash().ToString().substr(0,14).c_str(),
nVersion,
hashPrevBlock.ToString().substr(0,14).c_str(),
hashMerkleRoot.ToString().substr(0,6).c_str(),
nTime, nBits, nNonce,
vtx.size() );
printf("\nTransaction Info is:\n");
for (int i = 0; i < vtx.size(); i++)
{ printf(" ");
vtx[i].print();}
printf("\nMerkleTree Info is:\n");
printf(" vMerkleTree: ");
for (int i = 0; i < vMerkleTree.size(); i++)
{printf("%s ", vMerkleTree[i].ToString().substr(0,6).c_str());
}
printf("\n");
}
int64 GetBlockValue(int64 nFees) const;
bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions);
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
bool CheckBlock() const;
bool AcceptBlock();
//调试数据库存储
bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
template<typename K, typename T>
bool Write(const K& key, const T& value, bool fOverwrite=true)
{// Key
CDataStream ssKey(SER_DISK);
ssKey.reserve(1000);
ssKey << key;
//Dbt datKey(&ssKey[0], ssKey.size());
// Value
CDataStream ssValue(SER_DISK);
ssValue.reserve(10000);
ssValue << value;
//Dbt datValue(&ssValue[0], ssValue.size());
// Write
//int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
// Clear memory in case it was a private key
//memset(datKey.get_data(), 0, datKey.get_size());
//写入文件-测试数据
// Open history file to append
unsigned int nFileRet = 1;
bool fWriteTransactions = true;
CAutoFile fileout = AppendBlockFile(nFileRet);
if (!fileout)
return error("CBlock::WriteBlockIndexToDisk() : AppendBlockFile failed");
if (!fWriteTransactions)
fileout.nType |= SER_BLOCKHEADERONLY;
// Write index header
unsigned int nSize = ssKey.size() + ssValue.size();
fileout << FLATDATA(pchMessageStart) << nSize;
// Write block
unsigned int nBlockPosRet = ftell(fileout);
if (nBlockPosRet == -1)
return error("CBlock::WriteIndexToDisk() : ftell failed");
fileout << ssKey << ssValue;
return true;
}
bool WriteHashBestChain(uint256 hashBestChain)
{
return Write(string("hashBestChain"), hashBestChain);
}
};
//区块链是一个树状结构,从创世区块位于根,每个区块可能有多个候选区块作为下一个区块。Pprey和pnext通过/main/最长链链接一条路径。一个blockindex可能有多个pprev指向它,但pnext只指向最长的分支,或者如果区块不是最长链的一部分,pnext将为空
class CBlockIndex
{public:
CBlockIndex* pprev; //前一区块索引指针
CBlockIndex* pnext; //后一区块索引指针
//block info
const uint256* phashBlock; //区块哈希值
unsigned int nFile; //区块文件名
unsigned int nBlockPos; //区块在文件中的偏移位置
int nHeight; //区块高度
// block header //区块头
int nVersion; //版本号
uint256 hashMerkleRoot; //Merkle根
unsigned int nTime; //时间戳
unsigned int nBits; //难度目标值
unsigned int nNonce; //随机数
CBlockIndex()
{ phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = 0;
nBlockPos = 0;
nHeight = 0;
nVersion = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
}
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block)
{ phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0;
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
}
uint256 GetBlockHash() const
{ return *phashBlock;
}
bool IsInMainChain() const
{ return (pnext || this == pindexBest);
}
bool EraseBlockFromDisk()
{ //Open history file
CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+");
if (!fileout)
return false;
// Overwrite with empty null block
CBlock block;
block.SetNull();
fileout << block;
return true;
}
enum { nMedianTimeSpan=11 };
int64 GetMedianTimePast() const
{unsigned int pmedian[nMedianTimeSpan];
unsigned int* pbegin = &pmedian[nMedianTimeSpan];
unsigned int* pend = &pmedian[nMedianTimeSpan];
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
*(--pbegin) = pindex->nTime;
sort(pbegin, pend);
return pbegin[(pend - pbegin)/2]; }
int64 GetMedianTime() const
{ const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan/2; i++)
{ if (!pindex->pnext)
return nTime;
pindex = pindex->pnext; }
return pindex->GetMedianTimePast(); }
string ToString() const
{return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, pnext, nFile, nBlockPos, nHeight,
hashMerkleRoot.ToString().substr(0,6).c_str(),
GetBlockHash().ToString().substr(0,14).c_str()); }
void print() const
{ printf("%s\n", ToString().c_str());}};
// Used to marshal pointers into hashes for db storage.
class CDiskBlockIndex : public CBlockIndex
{public:
uint256 hashPrev; //前一区块的哈希值
uint256 hashNext; //后一区块的哈希值
CDiskBlockIndex()
{ hashPrev = 0;
hashNext = 0;}
explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex)
{hashPrev = (pprev ? pprev->GetBlockHash() : 0);
hashNext = (pnext ? pnext->GetBlockHash() : 0);
cout << "3-####this is hashPrev string:" << hashPrev.ToString().c_str() << endl;
cout << "4-####this is hashNext string:" << hashNext.ToString().c_str() << endl;
}
IMPLEMENT_SERIALIZE
(if (!(nType & SER_GETHASH))
READWRITE(nVersion);
READWRITE(hashNext);
READWRITE(nFile);
READWRITE(nBlockPos);
READWRITE(nHeight);
// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce); )
uint256 GetBlockHash() const
{CBlock block;
block.nVersion = nVersion;
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block.GetHash(); }
string ToString() const
{string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
GetBlockHash().ToString().c_str(),
hashPrev.ToString().c_str(),
hashNext.ToString().c_str());
return str; }
void print() const
{ printf("%s\n", ToString().c_str());}};
bool CBlock :: AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
{// Check for duplicate
uint256 hash = GetHash();
cout<<"\n1-####this is hash string:"<<hash.GetHex().c_str()<<endl;
if (mapBlockIndex.count(hash))
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,14).c_str());
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{ pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;}
cout << "2-####this is hash string:" << pindexNew->ToString().c_str() << endl;
CDiskBlockIndex tmpDiskBlockIndex = CDiskBlockIndex(pindexNew);
cout << "5-#### this is tmpDiskBlockIndex string:" << tmpDiskBlockIndex.ToString().c_str() << endl;
bool writeIndex = WriteBlockIndex(tmpDiskBlockIndex); //调试数据库存储
// New best
if (pindexNew->nHeight > nBestHeight)
{if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
{ pindexGenesisBlock = pindexNew;
WriteHashBestChain(hash); }
else if (hashPrevBlock == hashBestChain)
{// Adding to current best branch:DB }
else
{// New best branch:DB }
// New best link
hashBestChain = hash;
pindexBest = pindexNew;
nBestHeight = pindexBest->nHeight;
nTransactionsUpdated++;
printf("\nAddToBlockIndex: new best=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);}
// Relay wallet transactions that haven't gotten in yet
if (pindexNew == pindexBest)
{ //RelayWalletTransactions(); }
return true;
}
//调试区块索引写入数据库
bool CBlock :: WriteBlockIndex(const CDiskBlockIndex& blockindex)
{
return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
}
整体结构
CBlockHeader类定义了比特币区块头部的结构,包括:
nVersion:区块的版本号,用于追踪协议升级。
hashPrevBlock:链中前一个区块的256位哈希值。
hashMerkleRoot:这个区块中包含的交易的Merkle树的根哈希。
nTime:区块的时间戳。
nBits:目标难度的紧凑表示形式。
nNonce:用于工作量证明算法的一个随机数。
CBlock类继承自CBlockHeader,增加了构成区块内容的交易列表(vtx)。
还包括一个只在内存中使用的布尔标志fChecked,用来表示是否已经检查了区块的有效性。
CBlockLocator结构是一种在区块链中有效定位区块的方式。它包含了一个区块哈希列表(vHave),这些哈希在朝向创世区块的方向上是指数级间隔的。这使得节点能够快速传达它们的状态,并且与另一个节点的区块链找到一个共同的点。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
//交易-输入-前一个输出:交易哈希值、输出索引
class COutPoint
{
public:
uint256 hash;
unsigned int n;
COutPoint()
{
SetNull();
}
COutPoint(uint256 hashIn, unsigned int nIn)
{
hash = hashIn; n = nIn;
}
IMPLEMENT_SERIALIZE(READWRITE(FLATDATA(*this));)
void SetNull()
{
hash = 0; n = -1;
}
bool IsNull() const
{
return (hash == 0 && n == -1);
}
string ToString() const
{
return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,6).c_str(), n);
}
void print() const
{
printf("%s\n", ToString().c_str());
}
};
//交易-输入:前一个输出、解锁脚本、序列号
// An input of a transaction. It contains the location of the previous
// transaction's output that it claims and a signature that matches the
// output's public key.
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
unsigned int nSequence;
CTxIn()
{
nSequence = UINT_MAX;
}
IMPLEMENT_SERIALIZE
(
READWRITE(prevout);
READWRITE(scriptSig);
READWRITE(nSequence);
)
string ToString() const
{
string str;
str += strprintf("CTxIn(");
str += prevout.ToString();
if (prevout.IsNull())
str += strprintf(", coinbase %s", HexStr(scriptSig.begin(), scriptSig.end(), false).c_str());
else
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
if (nSequence != UINT_MAX)
str += strprintf(", nSequence=%u", nSequence);
str += ")";
return str;
}
void print() const
{
printf("%s\n", ToString().c_str());
}
};
//交易-输出:coinbase值、锁定脚本
// An output of a transaction. It contains the public key that the next input
// must be able to sign with to claim it.
class CTxOut
{
public:
int64 nValue;
CScript scriptPubKey;
public:
CTxOut()
{
SetNull();
}
IMPLEMENT_SERIALIZE
(
READWRITE(nValue);
READWRITE(scriptPubKey);
)
void SetNull()
{
nValue = -1;
scriptPubKey.clear();
}
bool IsNull()
{
return (nValue == -1);
}
uint256 GetHash() const
{
return SerializeHash(*this);
}
string ToString() const
{
if (scriptPubKey.size() < 6)
return "CTxOut(error)";
return strprintf("CTxOut(nValue=%I64d.%08I64d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,24).c_str());
}
void print() const
{
printf("%s\n", ToString().c_str());
}
};
//交易:版本号、输入、输出、锁定时间
// The basic transaction that is broadcasted on the network and contained in
// blocks. A transaction can contain multiple inputs and outputs.
class CTransaction
{
public:
int nVersion;
vector<CTxIn> vin;
vector<CTxOut> vout;
int nLockTime;
CTransaction()
{
SetNull();
}
IMPLEMENT_SERIALIZE
(
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
)
void SetNull()
{
nVersion = 1;
vin.clear();
vout.clear();
nLockTime = 0;
}
bool IsNull() const
{
return (vin.empty() && vout.empty());
}
//得到交易的哈希值
uint256 GetHash() const
{
return SerializeHash(*this);
}
//交易字符串
string ToString() const
{
string str;
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
GetHash().ToString().substr(0,6).c_str(),
nVersion,
vin.size(),
vout.size(),
nLockTime);
for (int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
return str;
}
//打印交易详情
void print() const
{
printf("%s", ToString().c_str());
}
};
区块结构分析
区块是通过前后链接构成区块链的一种容器数据结构。它由描述区块主要信息的区块头和包含若干交易数据的区块共同组成。区块头是80字节,而平均每个交易至少是250字节,而且平均每个区块至少包含超过500个交易。所以,一个区块是比区块头大好多的数据体。这也是比特币验证交易是否存在采用Merkcle树的原因。
区块标识符
BlockHash 区块哈希值,是通过SHA256算法对区块头信息进行哈希得到的,这个值必须满足POW的DifficultyTarget,该区块才被认为有效。同时,也是区块的唯一标识符,可以通过通过bitcoin-cli根据BlockHash查询区块信息(文章开头我们就使用过)
BlockHeight 区块高度,是用来标示区块在区块链中的位置。创世区块高度为0,每一个加在后面的区块,区块高度递增1。可以通过bitcoin-cli根据高度查询区块哈希值(文章开头我们就使用过)
注:BlockHeight并不是唯一标识符,当区块链出现临时分叉时,会有两个区块的高度值相同的情况。
类 COutPoint
这个类定义了一个输出点(outpoint),它唯一地标识了前一个交易中的特定输出,包括一个交易哈希(Txid hash)和一个索引(uint32_t n),指向它的输出向量(vout)。
类 CTxIn
这个类表示一个交易输入,包括对前一个输出的引用(COutPoint prevout),一个签名脚本(CScript scriptSig),它提供满足引用输出的scriptPubKey所需的数据,一个序列号(uint32_t nSequence),以及一个可选的见证脚本(CScriptWitness scriptWitness),用于隔离见证(segregated witness)交易。
类 CTxOut
这个类表示一个交易输出,包含一个以聪(satoshi)为单位的值(CAmount nValue)和一个公钥脚本(CScript scriptPubKey),指定了必须满足才能花费输出的条件。
结构体 CMutableTransaction
这个结构体被声明但在提供的代码片段中没有定义。它可能表示交易的一个可变版本,可以在最终确定前进行修改。
结构体 TransactionSerParams
这个结构体用于指定序列化参数,特别是在序列化过程中是否允许证人数据。
序列化/反序列化函数
UnserializeTransaction 和 SerializeTransaction 模板定义了如何将交易类型序列化和反序列化为字节流。这些函数处理基本和扩展的交易序列化格式,其中扩展格式包括隔离见证数据。
辅助函数 CalculateOutputValue
这个模板函数通过累加交易的 vout 向量中每个 CTxOut 对象的 nValue 来计算交易中所有输出的总值。
类 CTransaction
这个类代表了广播在比特币网络上的基础交易结构。一个交易可以包含多个输入和输出。
成员变量
vin:一个 CTxIn 类型的向量,表示交易的输入。
vout:一个 CTxOut 类型的向量,表示交易的输出。
nVersion:交易数据格式的版本号。
nLockTime:交易的锁定时间,指定了交易不能被打包进区块链的最早时间。
私有成员变量
m_has_witness:标记交易是否包含隔离见证(SegWit)数据。
hash:交易的ID(txid),代表交易的唯一标识符。
m_witness_hash:如果交易包含隔离见证数据,这个变量则代表交易的隔离见证ID(wtxid)。
成员函数
CTransaction():构造函数,用于从一个 CMutableTransaction 对象创建一个不可变的 CTransaction 对象。
Serialize():一个模板函数,用于将交易序列化为字节流。
Deserialize():一个反序列化构造函数,用于从字节流中重建一个不可变的交易对象。
IsNull():判断交易是否为空,即没有输入和输出。
GetHash() 和 GetWitnessHash():获取交易的标识符。
GetValueOut():计算交易输出的总金额。
GetTotalSize():获取整个交易的大小,包括见证数据。
IsCoinBase():检查这个交易是否是一个coinbase交易,即区块奖励交易。
ToString():生成交易的字符串表示。
HasWitness():检查交易是否包含隔离见证数据。
结构体 CMutableTransaction
这是 CTransaction 的一个可变版本。它允许修改交易,通常用于构建一个新交易。
成员函数
CMutableTransaction():构造函数,用来创建和修改可变交易。
Serialize() 和 Unserialize():序列化和反序列化函数。
GetHash():计算并返回交易的哈希值。
HasWitness():检查交易是否包含隔离见证数据。
CTxMemPool:
这是内存池的主要类,用于管理待确认的交易。它包括了很多成员函数,用于添加、移除、获取交易等操作。
CTxMemPool 类中包含了多个索引容器(boost::multi_index_container),以便根据不同的标准对内存池中的交易进行排序和访问。这些标准包括交易的哈希、交易的费率等。
这个类还包含了一些用于处理交易的限制和计算的方法,例如计算交易的祖先和后代、检查交易的大小限制等。
TxMempoolInfo:
这是一个结构体,用于存储内存池中的交易的信息,包括交易本身、进入内存池的时间、交易的费用、交易的虚拟大小等信息。
CCoinsViewMemPool:
这个类实现了 CCoinsView 接口,可以将内存池中的交易带入视图中。它不会检查内存池交易的支出,而是提供对所有未花费的基础 CCoinsView 中的硬币以及来自任何内存池交易的输出的访问。
该类还可以用于跟踪由正在验证的交易创建的硬币,这些硬币只是临时存储在 m_temp_added 中,无法刷新到后端。
mempoolentry_txid 和 mempoolentry_wtxid:
这两个结构体分别用于从 CTxMemPoolEntry 或 CTransactionRef 中提取交易哈希和见证交易哈希。
#include "block_test.h"
int main()
{
// Genesis Block:
// GetHash() = 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
// hashMerkleRoot = 0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
// txNew.vin[0].scriptSig = 486604799 4 0x736B6E616220726F662074756F6C69616220646E6F63657320666F206B6E697262206E6F20726F6C6C65636E61684320393030322F6E614A2F33302073656D695420656854
// txNew.vout[0].nValue = 5000000000
// txNew.vout[0].scriptPubKey = 0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704 OP_CHECKSIG
// block.nVersion = 1
// block.nTime = 1231006505
// block.nBits = 0x1d00ffff
// block.nNonce = 2083236893
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
// CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
// CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
// vMerkleTree: 4a5e1e
//区块、交易
CBlock block;
CTransaction txNew;
const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
//新交易赋值
txNew.vin.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout.resize(1);
txNew.vout[0].nValue = 50 * COIN;
txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG;
//交易加入到区块中
block.vtx.push_back(txNew);
//区块头
block.nVersion = 1;
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nTime = 1231006505;
block.nBits = 0x1d00ffff;
block.nNonce = 2083236893;
printf("Genesis Block GetHash is:%s\n\n", block.GetHash().ToString().c_str());
printf("Genesis Block's hash is:%s\n\n", hashGenesisBlock.ToString().c_str());
block.print();
assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
assert(block.GetHash() == hashGenesisBlock);
// Start new block file-创世区块存储
unsigned int nFile;
unsigned int nBlockPos;
if (!block.WriteToDisk(!fClient, nFile, nBlockPos))
{
return error("block-Genesis : writing genesis block to disk failed");
}
else
{
printf("block-Genesis ok!\n");
}
//创世区块索引存储
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
return 0;
}
e此代码的最后部分定义了一个 DeploymentHeight 函数,用于根据传入的BuriedDeployment枚举值返回对应的激活高度。例如,如果传入DEPLOYMENT_CSV,函数返回的是CSVHeight的值。
这些参数和结构是构建和维护比特币核心软件中共识规则的基础,开发者和网络参与者会根据这些规则来验证交易和区块的有效性,确保网络的稳定运行。
- 实验总结
通过阅读比特币源码,我深入了解了区块链的底层技术和比特币的基本原理,对比特币的去中心化、加密机制、挖矿过程以及交易确认等方面有了更深入的理解。同时,通过实际代码的阅读,对比特币网络中的数据结构、功能模块和共识规则有了更为直观和深入的认识。
区块链是一种利用密码学和分布式共识机制实现数据的去中心化、安全和不可篡改的技术。区块链技术在金融、政务、供应链、物联网等领域有着广泛的应用前景,但也面临着一些技术挑战和难题,主要有以下几个方面:
扩展性:区块链的扩展性是指系统能够处理的交易数量和速度。由于区块链的分布式和去中心化的特性,每个节点都需要存储和验证所有的交易和区块,这就限制了区块链的吞吐量和响应时间。这会增加网络的负担和分叉的风险。另一些解决方案是通过分层扩展的方式,将部分交易从主链上转移到侧链或者状态通道上,从而减轻主链的压力,但这也会牺牲一定的安全性和去中心化程度。
隐私性:区块链的隐私性是指系统能够保护用户的身份和交易信息不被泄露或者滥用。由于区块链的公开和透明的特性,每个节点都可以查看所有的交易和账户信息,这可能会暴露用户的个人隐私和商业机密。例如,通过分析区块链上的交易数据,可以推断出用户的消费习惯、资产状况、社交关系等敏感信息。为了提高区块链的隐私性,有些解决方案是通过加密或者混淆的方式,对用户的身份或者交易的内容进行隐藏或者掩盖,但这也会增加计算的复杂度和成本。另一些解决方案是通过零知识证明或者同态加密等技术,实现在不泄露具体信息的情况下进行验证或者计算,但这也需要更高的密码学水平和安全保障。
能源效率:区块链的能源效率是指系统能够以较低的能源消耗实现数据的共识和存储。由于区块链的去中心化和安全的特性,每个节点都需要参与到共识机制和数据同步的过程中,这就需要大量的计算能力和电力消耗。例如,比特币的工作量证明(PoW)共识机制,要求节点通过解决复杂的数学难题来竞争记账权,这就导致了大量的算力浪费和能源消耗。据估计,比特币的年度能耗已经超过了一些国家的总能耗,这不仅增加了区块链的运行成本,还对环境造成了负面影响。为了提高区块链的能源效率,有些解决方案是通过改变共识机制,从工作量证明(PoW)转向权益证明(PoS)或者委托权益证明(DPoS)等,这样可以减少算力的竞争和能源的消耗,但这也会增加中心化的风险和攻击的可能性。
法律法规:区块链的法律法规是指系统能够符合不同国家和地区的法律法规要求。由于区块链的跨境和去中心化的特性,每个节点都可以跨越国界和地域进行交易和协作,这就涉及到不同的法律法规和监管标准。例如,区块链上的数字货币和代币,在不同的国家和地区有着不同的定义和认可度,有些国家和地区对其进行了合法化和规范化,有些国家和地区对其进行了禁止和打击,这就给区块链的发展和应用带来了不确定性和风险。为了适应区块链的法律法规,有些解决方案是通过建立沙箱机制,为区块链的创新和试验提供一定的空间和便利,但这也需要区块链的参与者和监管者之间进行充分的沟通和协调。
标签:专题,return,int,str,const,区块,block From: https://www.cnblogs.com/maqun/p/18521661