Skip to content

Commit

Permalink
Add Checkpoints (#448)
Browse files Browse the repository at this point in the history
* Init CheckPoint

* Impl VerifyCheckPoint

* Fix Log bug

* Impl findPreviousCheckPointBlock

* Verify Block Checkpoint

* Fix FindPreviousCheckPointBlock bug

* Refactor

* commet checkpoints vector

* Find Previous CheckPoint Block

* Add Checkpoints from  primary chain

* Add Check checkpoint timestamp

* Fix testnet checkpoint bug

* Only primary block need to check

* Fix FindPreviousCheckPoint bug

* Fix cppckeck

* Remove unused class member

* Add CNetChannel::Config() for checkpoints

* Add checkpoints candidate tool written in Python (#352)

* Completed checkpoints candidate tool using python

* Add new Check points

* Improve verifycheckpoint performane
  • Loading branch information
AlexiaChen committed Apr 30, 2020
1 parent 38f1821 commit fab8fbc
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 2 deletions.
37 changes: 37 additions & 0 deletions src/bigbang/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,34 @@ class ICoreProtocol : public xengine::IBase

class IBlockChain : public xengine::IBase
{
public:
class CCheckPoint
{
public:
CCheckPoint()
: nHeight(-1)
{
}
CCheckPoint(int nHeightIn, const uint256& nBlockHashIn)
: nHeight(nHeightIn), nBlockHash(nBlockHashIn)
{
}
CCheckPoint(const CCheckPoint& point)
: nHeight(point.nHeight), nBlockHash(point.nBlockHash)
{
}
CCheckPoint& operator=(const CCheckPoint& point)
{
nHeight = point.nHeight;
nBlockHash = point.nBlockHash;
return *this;
}

public:
int nHeight;
uint256 nBlockHash;
};

public:
IBlockChain()
: IBase("blockchain") {}
Expand Down Expand Up @@ -100,6 +128,15 @@ class IBlockChain : public xengine::IBase
virtual bool GetBlockLocator(const uint256& hashFork, CBlockLocator& locator, uint256& hashDepth, int nIncStep) = 0;
virtual bool GetBlockInv(const uint256& hashFork, const CBlockLocator& locator, std::vector<uint256>& vBlockHash, std::size_t nMaxCount) = 0;
virtual bool ListForkUnspent(const uint256& hashFork, const CDestination& dest, uint32 nMax, std::vector<CTxUnspent>& vUnspent) = 0;

///////////// CheckPoints /////////////////////
virtual bool HasCheckPoints() const = 0;
virtual bool GetCheckPointByHeight(int nHeight, CCheckPoint& point) = 0;
virtual std::vector<CCheckPoint> CheckPoints() const = 0;
virtual CCheckPoint LatestCheckPoint() const = 0;
virtual bool VerifyCheckPoint(int nHeight, const uint256& nBlockHash) = 0;
virtual bool FindPreviousCheckPointBlock(CBlock& block) = 0;

virtual bool ListForkUnspentBatch(const uint256& hashFork, uint32 nMax, std::map<CDestination, std::vector<CTxUnspent>>& mapUnspent) = 0;
virtual bool GetVotes(const CDestination& destDelegate, int64& nVotes) = 0;
virtual bool ListDelegate(uint32 nCount, std::multimap<int64, CDestination>& mapVotes) = 0;
Expand Down
130 changes: 130 additions & 0 deletions src/bigbang/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ bool CBlockChain::HandleInitialize()
return false;
}

InitCheckPoints();

return true;
}

Expand Down Expand Up @@ -84,6 +86,17 @@ bool CBlockChain::HandleInvoke()
}
}

// Check local block compared to checkpoint
if (Config()->nMagicNum == MAINNET_MAGICNUM)
{
CBlock block;
if (!FindPreviousCheckPointBlock(block))
{
StdError("BlockChain", "Find CheckPoint Error when the node starting, you should purge data(bigbang -purge) to resync blockchain");
return false;
}
}

return true;
}

Expand Down Expand Up @@ -1440,4 +1453,121 @@ bool CBlockChain::VerifyBlockCertTx(const CBlock& block)
return true;
}

void CBlockChain::InitCheckPoints()
{

if (Config()->nMagicNum == MAINNET_MAGICNUM)
{
vecCheckPoints.assign(
{ { 0, uint256("00000000b0a9be545f022309e148894d1e1c853ccac3ef04cb6f5e5c70f41a70") },
{ 100, uint256("000000649ec479bb9944fb85905822cb707eb2e5f42a5d58e598603b642e225d") },
{ 1000, uint256("000003e86cc97e8b16aaa92216a66c2797c977a239bbd1a12476bad68580be73") },
{ 2000, uint256("000007d07acd442c737152d0cd9d8e99b6f0177781323ccbe20407664e01da8f") },
{ 5000, uint256("00001388dbb69842b373352462b869126b9fe912b4d86becbb3ad2bf1d897840") },
{ 10000, uint256("00002710c3f3cd6c931f568169c645e97744943e02b0135aae4fcb3139c0fa6f") },
{ 16000, uint256("00003e807c1e13c95e8601d7e870a1e13bc708eddad137a49ba6c0628ce901df") },
{ 23000, uint256("000059d889977b9d0cd3d3fa149aa4c6e9c9da08c05c016cb800d52b2ecb620c") },
{ 31000, uint256("000079188913bbe13cb3ff76df2ba2f9d2180854750ab9a37dc8d197668d2215") },
{ 40000, uint256("00009c40c22952179a522909e8bec05617817952f3b9aebd1d1e096413fead5b") },
{ 50000, uint256("0000c3506e5e7fae59bee39965fb45e284f86c993958e5ce682566810832e7e8") },
{ 70000, uint256("000111701e15e979b4633e45b762067c6369e6f0ca8284094f6ce476b10f50de") } });
}

for (const auto& point : vecCheckPoints)
{
mapCheckPoints.insert(std::make_pair(point.nHeight, point));
}
}

bool CBlockChain::HasCheckPoints() const
{
return mapCheckPoints.size() > 0;
}

bool CBlockChain::GetCheckPointByHeight(int nHeight, CCheckPoint& point)
{
if (mapCheckPoints.count(nHeight) == 0)
{
return false;
}
else
{
point = mapCheckPoints[nHeight];
return true;
}
}

std::vector<IBlockChain::CCheckPoint> CBlockChain::CheckPoints() const
{
return vecCheckPoints;
}

IBlockChain::CCheckPoint CBlockChain::LatestCheckPoint() const
{
if (!HasCheckPoints())
{
return CCheckPoint();
}

return vecCheckPoints.back();
}

bool CBlockChain::VerifyCheckPoint(int nHeight, const uint256& nBlockHash)
{
if (!HasCheckPoints())
{
return true;
}

CCheckPoint point;
if (!GetCheckPointByHeight(nHeight, point))
{
return true;
}

if (nBlockHash != point.nBlockHash)
{
return false;
}

Log("Verified checkpoint at height %d/block %s", point.nHeight, point.nBlockHash.ToString().c_str());

return true;
}

bool CBlockChain::FindPreviousCheckPointBlock(CBlock& block)
{
if (!HasCheckPoints())
{
return true;
}

const auto& points = CheckPoints();
int numCheckpoints = points.size();
for (int i = numCheckpoints - 1; i >= 0; i--)
{
const CCheckPoint& point = points[i];

uint256 hashBlock;
if (!GetBlockHash(pCoreProtocol->GetGenesisBlockHash(), point.nHeight, hashBlock))
{
StdTrace("BlockChain", "CheckPoint(%d, %s) doest not exists and continuely try to get previous checkpoint",
point.nHeight, point.nBlockHash.ToString().c_str());

continue;
}

if (hashBlock != point.nBlockHash)
{
StdError("BlockChain", "CheckPoint(%d, %s) does not match block hash %s",
point.nHeight, point.nBlockHash.ToString().c_str(), hashBlock.ToString().c_str());
return false;
}

return GetBlock(hashBlock, block);
}

return true;
}

} // namespace bigbang
15 changes: 14 additions & 1 deletion src/bigbang/blockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
#define BIGBANG_BLOCKCHAIN_H

#include <map>
#include <uint256.h>

#include "base.h"
#include "blockbase.h"

namespace bigbang
{

Expand Down Expand Up @@ -63,6 +63,14 @@ class CBlockChain : public IBlockChain
uint32 DPoSTimestamp(const uint256& hashPrev) override;
Errno VerifyPowBlock(const CBlock& block, bool& fLongChain) override;

///////////// CheckPoints /////////////////////
bool HasCheckPoints() const override;
bool GetCheckPointByHeight(int nHeight, CCheckPoint& point) override;
std::vector<CCheckPoint> CheckPoints() const override;
CCheckPoint LatestCheckPoint() const override;
bool VerifyCheckPoint(int nHeight, const uint256& nBlockHash) override;
bool FindPreviousCheckPointBlock(CBlock& block) override;

protected:
bool HandleInitialize() override;
void HandleDeinitialize() override;
Expand All @@ -81,13 +89,18 @@ class CBlockChain : public IBlockChain
int64& nReward, CDelegateAgreement& agreement, std::size_t& nEnrollTrust, CBlockIndex** ppIndexRef);
bool VerifyBlockCertTx(const CBlock& block);

void InitCheckPoints();

protected:
boost::shared_mutex rwAccess;
ICoreProtocol* pCoreProtocol;
ITxPool* pTxPool;
storage::CBlockBase cntrBlock;
xengine::CCache<uint256, CDelegateEnrolled> cacheEnrolled;
xengine::CCache<uint256, CDelegateAgreement> cacheAgreement;

std::map<int, CCheckPoint> mapCheckPoints;
std::vector<CCheckPoint> vecCheckPoints;
};

} // namespace bigbang
Expand Down
11 changes: 10 additions & 1 deletion src/bigbang/netchn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,11 +947,20 @@ bool CNetChannel::HandleEvent(network::CEventPeerBlock& eventBlock)
uint256& hashFork = eventBlock.hashFork;
CBlock& block = eventBlock.data;
uint256 hash = block.GetHash();

uint32 nBlockHeight = block.GetBlockHeight();
try
{
boost::recursive_mutex::scoped_lock scoped_lock(mtxSched);

if (Config()->nMagicNum == MAINNET_MAGICNUM && block.IsPrimary())
{
if (!pBlockChain->VerifyCheckPoint((int)nBlockHeight, hash))
{
StdError("NetChannel", "block at height %d does not match checkpoint hash", (int)nBlockHeight);
throw std::runtime_error("block doest not match checkpoint hash");
}
}

set<uint64> setSchedPeer, setMisbehavePeer;
CSchedule& sched = GetSchedule(hashFork);

Expand Down
5 changes: 5 additions & 0 deletions src/bigbang/netchn.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ class CNetChannel : public network::INetChannel
void InnerBroadcastBlockInv(const uint256& hashFork, const uint256& hashBlock);
void InnerSubmitCachePowBlock();

const CBasicConfig* Config()
{
return dynamic_cast<const CBasicConfig*>(xengine::IBase::Config());
}

protected:
network::CBbPeerNet* pPeerNet;
ICoreProtocol* pCoreProtocol;
Expand Down
90 changes: 90 additions & 0 deletions tools/checkpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env python

# -*- coding: utf-8 -*-

# This tool refer to btcd checkpoint candidate tool
# https://github.com/bigbangcore/BigBang/issues/343

import json
import subprocess

# btcd confirmations is 2016, but we could reduce from 2016 to 1000 confirms,
# cause we cannot compared with BTC' Hash Rate in the main network


def Constant_CheckPointConfirmations():
return 1000


def get_block_hash(height):
json_str = str(subprocess.check_output(
['bigbang', 'getblockhash', str(height)]))

block_hash_list = json.loads(json_str)
if len(block_hash_list) > 1:
return False, ''
else:
return True, block_hash_list[0]


def get_block(block_hash):
json_str = subprocess.check_output(['bigbang', 'getblock', block_hash])
return json.loads(json_str)


def get_primary_fork_height():
return int(subprocess.check_output(['bigbang', 'getforkheight']))


def get_block_type(block_dict):
return block_dict['type']


def get_block_height(block_dict):
return block_dict['height']


def get_block_timestamp(block_dict):
return block_dict['time']


def is_primary_block(block_type):
if "primary-" in str(block_type):
return True
else:
return False


def main():

# Get (last height - confirms) from best primary chain
end_height = get_primary_fork_height() - Constant_CheckPointConfirmations()

# block at least have 1000 confirms
height_list = [i for i in range(end_height)]
# gensis block timestamp
prev_block_timestamp = 1575043200

print 'check point candidate list here: '

for height in height_list:

is_success, block_hash = get_block_hash(height)
if not is_success:
continue

block_dict = get_block(block_hash)
block_type = get_block_type(block_dict)
if is_primary_block(block_type):
block_height = get_block_height(block_dict)
if block_height != height or block_height == 0:
continue
current_block_timestamp = get_block_timestamp(block_dict)
if current_block_timestamp > prev_block_timestamp:
checkpoint = (block_height, block_hash)
print checkpoint
prev_block_timestamp = current_block_timestamp


if __name__ == '__main__':
main()

0 comments on commit fab8fbc

Please sign in to comment.