Miguel Freitas
11 years ago
13 changed files with 374 additions and 3 deletions
@ -0,0 +1,254 @@ |
|||||||
|
// Copyright (c) 2014 Miguel Freitas
|
||||||
|
// TODO: write description for the soft checkpoint
|
||||||
|
// More info:
|
||||||
|
// https://groups.google.com/forum/#!topic/twister-dev/tH3HlVQ_wmo
|
||||||
|
|
||||||
|
#include <boost/assign/list_of.hpp> // for 'map_list_of()' |
||||||
|
#include <boost/assign.hpp> |
||||||
|
#include <boost/foreach.hpp> |
||||||
|
|
||||||
|
#include "softcheckpoint.h" |
||||||
|
|
||||||
|
#include "main.h" |
||||||
|
#include "checkpoints.h" |
||||||
|
#include "uint256.h" |
||||||
|
#include "script.h" |
||||||
|
#include "init.h" |
||||||
|
#include "twister.h" |
||||||
|
|
||||||
|
#define dbgprintf OutputDebugStringF |
||||||
|
//#define dbgprintf(...) // no debug printf
|
||||||
|
|
||||||
|
namespace SoftCheckpoints |
||||||
|
{ |
||||||
|
bool fEnabled = true; |
||||||
|
CCriticalSection cs_softCP; |
||||||
|
|
||||||
|
typedef std::pair<int, uint256> Checkpoint; // height, hash
|
||||||
|
typedef std::map<std::string, std::string> CPSigMap; // user, sign
|
||||||
|
typedef std::pair<std::string, std::string> CPSigPair; // user, sign
|
||||||
|
|
||||||
|
static Checkpoint lastSoftCP; |
||||||
|
static CPSigMap lastSoftCPSigs; |
||||||
|
|
||||||
|
static std::map<Checkpoint, CPSigMap> nextCandidates; |
||||||
|
static std::map<Checkpoint, CPSigMap> uncheckedCandidates; |
||||||
|
|
||||||
|
static std::set<std::string> uniqueUsersList = |
||||||
|
boost::assign::list_of |
||||||
|
("mf1")("mf1a")("mf2")("mf2a")("mf3"); |
||||||
|
|
||||||
|
|
||||||
|
void SetSoftCPBestChain() { |
||||||
|
// requires cs_main and cs_softCP locked (in this order)
|
||||||
|
if( !fEnabled ) |
||||||
|
return; |
||||||
|
|
||||||
|
if( lastSoftCP.first && mapBlockIndex.count(lastSoftCP.second) && |
||||||
|
!mapBlockIndex[lastSoftCP.second]->IsInMainChain() ) { |
||||||
|
dbgprintf("SoftCheckpoints::SetSoftCPBestChain: lastSoftCP %d not in main chain\n", lastSoftCP.first); |
||||||
|
|
||||||
|
// ? use !mapOrphanBlocks.count() ?
|
||||||
|
|
||||||
|
CBlockIndex *pindex = mapBlockIndex[lastSoftCP.second]; |
||||||
|
while( pindex && !pindex->IsInMainChain() ) { |
||||||
|
pindex = pindex->pprev; |
||||||
|
} |
||||||
|
|
||||||
|
if( !pindex ) { |
||||||
|
dbgprintf("SoftCheckpoints::SetSoftCPBestChain: lastSoftCP %d currently orphaned? strange.\n", lastSoftCP.first); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
dbgprintf("SoftCheckpoints::SetSoftCPBestChain: trying SetBestChain with lastSoftCP %d\n", lastSoftCP.first); |
||||||
|
CValidationState state; |
||||||
|
if (!SetBestChain(state, mapBlockIndex[lastSoftCP.second])) { |
||||||
|
dbgprintf("SoftCheckpoints::lastSoftCPUpdated: SetBestChain failed\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void LastSoftCPUpdated() { |
||||||
|
//LOCK(cs_main); // FIXME: not needed if called from ProcessMessage. check. wrong mutex order is bad.
|
||||||
|
SetSoftCPBestChain(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string CPtoString(Checkpoint &cp) { |
||||||
|
CScript cs = CScript() << cp.first << cp.second; |
||||||
|
return std::string((const char *)cs.data(), cs.size()); |
||||||
|
} |
||||||
|
|
||||||
|
void NewBlockAccepted() { |
||||||
|
LOCK(cs_softCP); |
||||||
|
SetSoftCPBestChain(); |
||||||
|
|
||||||
|
if( (nBestHeight % SOFT_CHECKPOINT_PERIOD) == 0 && |
||||||
|
nBestHeight > Checkpoints::GetHighestCheckpoint() && |
||||||
|
nBestHeight > lastSoftCP.first + SOFT_CHECKPOINT_PERIOD && |
||||||
|
!fImporting && !fReindex) { |
||||||
|
LOCK(pwalletMain->cs_wallet); |
||||||
|
BOOST_FOREACH(const PAIRTYPE(CKeyID, CKeyMetadata)& item, pwalletMain->mapKeyMetadata) |
||||||
|
{ |
||||||
|
const std::string &username = item.second.username; |
||||||
|
if(uniqueUsersList.count(username)) { |
||||||
|
int height = nBestHeight - SOFT_CHECKPOINT_PERIOD; |
||||||
|
dbgprintf("SoftCheckpoints::NewBlockAccepted: user '%s' will vote for %d\n", |
||||||
|
username.c_str(), height); |
||||||
|
|
||||||
|
CBlockIndex* pblockindex = FindBlockByHeight(height); |
||||||
|
assert( pblockindex ); |
||||||
|
|
||||||
|
Checkpoint cpPair = std::make_pair(height, *pblockindex->phashBlock); |
||||||
|
std::string dataToSign = CPtoString(cpPair); |
||||||
|
|
||||||
|
std::string sign = createSignature(dataToSign, username); |
||||||
|
|
||||||
|
if( sign.size() ) { |
||||||
|
if( CastVoteSoftCheckpoint(height, *pblockindex->phashBlock, username, sign) ) { |
||||||
|
dbgprintf("SoftCheckpoints::NewBlockAccepted: relaying our own vote\n"); |
||||||
|
CSoftCheckpoint cp; |
||||||
|
cp.nHeight = height; |
||||||
|
cp.blockHash = *pblockindex->phashBlock; |
||||||
|
cp.vchUsername = std::vector<char>(username.begin(), username.end()); |
||||||
|
cp.vchSign = std::vector<char>(sign.begin(), sign.end()); |
||||||
|
SoftCheckpoints::RelayCP(cp, NULL); |
||||||
|
} else { |
||||||
|
dbgprintf("SoftCheckpoints::NewBlockAccepted: CastVoteSoftCheckpoint failed for our own vote!\n"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
dbgprintf("SoftCheckpoints::NewBlockAccepted: createSignature failed for user '%s'\n", |
||||||
|
username.c_str()); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool CastVerifiedVote(Checkpoint &cp, const std::string &username, const std::string &sign) { |
||||||
|
if( cp.first == lastSoftCP.first ) { |
||||||
|
if( lastSoftCPSigs.count(username) ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVerifiedVote: '%s' already voted for lastSoftCP %d\n", username.c_str(), cp.first); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if( cp != lastSoftCP ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVerifiedVote: '%s' voted for a different hash than lastSoftCP %d\n", username.c_str(), cp.first); |
||||||
|
return false; |
||||||
|
} |
||||||
|
dbgprintf("SoftCheckpoints::CastVerifiedVote: new vote for lastSoftCP %d by '%s'\n", cp.first, username.c_str()); |
||||||
|
lastSoftCPSigs[username] = sign; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if( nextCandidates.count(cp) && nextCandidates[cp].count(username) ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVerifiedVote: '%s' already voted for candidate %d\n", username.c_str(), cp.first); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
nextCandidates[cp][username] = sign; |
||||||
|
if( nextCandidates[cp].size() > uniqueUsersList.size() / 2) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVerifiedVote: new soft checkpoint %d wins!\n", cp.first); |
||||||
|
lastSoftCP = cp; |
||||||
|
lastSoftCPSigs = nextCandidates[cp]; |
||||||
|
nextCandidates.clear(); |
||||||
|
LastSoftCPUpdated(); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// returns true if vote is to be restransmitted
|
||||||
|
bool CastVoteSoftCheckpoint(int height, const uint256 &hash, const std::string &username, const std::string &sign) { |
||||||
|
LOCK(cs_softCP); |
||||||
|
|
||||||
|
if( (height % SOFT_CHECKPOINT_PERIOD) != 0 ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: height %d not multiple of SOFT_CHECKPOINT_PERIOD\n", height); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int hardCheckPointHeight = Checkpoints::GetHighestCheckpoint(); |
||||||
|
|
||||||
|
if( height < hardCheckPointHeight ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: height %d < hard checkpoint %d\n", height, hardCheckPointHeight); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( height < lastSoftCP.first ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: height %d < soft checkpoint %d\n", height, lastSoftCP.first); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( !uniqueUsersList.count(username) ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: username '%s' not accepted\n", username.c_str()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
Checkpoint cp = std::make_pair(height, hash); |
||||||
|
|
||||||
|
if( nBestHeight < hardCheckPointHeight ) { |
||||||
|
// still downloading blocks, we can't check signatures yet
|
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: vote for %d by '%s' added to unchecked\n", height, username.c_str()); |
||||||
|
uncheckedCandidates[cp][username] = sign; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if( !verifySignature( CPtoString(cp), username, sign) ) { |
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: invalid signature by '%s'\n", username.c_str()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
dbgprintf("SoftCheckpoints::CastVoteSoftCheckpoint: signature by '%s' verified, casting vote\n", username.c_str()); |
||||||
|
return CastVerifiedVote( cp, username, sign ); |
||||||
|
} |
||||||
|
|
||||||
|
bool CheckBlock(int nHeight, const uint256& hash) { |
||||||
|
LOCK(cs_softCP); |
||||||
|
if (!fEnabled) |
||||||
|
return true; |
||||||
|
|
||||||
|
if (!lastSoftCP.first || nHeight != lastSoftCP.first) |
||||||
|
return true; |
||||||
|
dbgprintf("SoftCheckpoints::CheckBlock: height %d isOk=%d\n", nHeight, hash == lastSoftCP.second); |
||||||
|
return hash == lastSoftCP.second; |
||||||
|
} |
||||||
|
|
||||||
|
void RelayCP(const CSoftCheckpoint& cp, CNode* pfrom) { |
||||||
|
LOCK(cs_vNodes); |
||||||
|
dbgprintf("SoftCheckpoints::RelayCP: relaying softCP height %d\n", cp.nHeight); |
||||||
|
BOOST_FOREACH(CNode* pnode, vNodes) { |
||||||
|
if(pnode == pfrom) |
||||||
|
continue; |
||||||
|
if (pnode->nVersion >= SOFT_CHECKPOINT_VERSION) { |
||||||
|
dbgprintf("SoftCheckpoints::RelayCP: pushMessage to %s\n", pnode->addr.ToString().c_str()); |
||||||
|
pnode->PushMessage("cp", cp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if( pfrom && lastSoftCP.first == cp.nHeight ) { |
||||||
|
if( !mapBlockIndex.count(lastSoftCP.second) ) { |
||||||
|
dbgprintf("SoftCheckpoints::RelayCP: requesting block height %d from node\n", cp.nHeight); |
||||||
|
PushGetBlocks(pfrom, pindexBest, cp.blockHash); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void RelayLastCPToNode(CNode* pnode) { |
||||||
|
LOCK(cs_softCP); |
||||||
|
if (!lastSoftCP.first) |
||||||
|
return; |
||||||
|
|
||||||
|
if (pnode->nVersion >= SOFT_CHECKPOINT_VERSION) { |
||||||
|
dbgprintf("SoftCheckpoints::RelayToLastCP: relaying lastSoftCP height %d (size %zd) to %s\n", |
||||||
|
lastSoftCP.first, lastSoftCPSigs.size(), pnode->addr.ToString().c_str()); |
||||||
|
|
||||||
|
BOOST_FOREACH(const CPSigMap::value_type& i, lastSoftCPSigs) { |
||||||
|
CSoftCheckpoint cp; |
||||||
|
|
||||||
|
cp.nHeight = lastSoftCP.first; |
||||||
|
cp.blockHash = lastSoftCP.second; |
||||||
|
cp.vchUsername = std::vector<char>(i.first.begin(), i.first.end()); |
||||||
|
cp.vchSign = std::vector<char>(i.second.begin(), i.second.end()); |
||||||
|
pnode->PushMessage("cp", cp); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
// Copyright (c) 2014 Miguel Freitas
|
||||||
|
|
||||||
|
#ifndef SOFT_CHECKPOINT_H |
||||||
|
#define SOFT_CHECKPOINT_H |
||||||
|
|
||||||
|
#define SOFT_CHECKPOINT_PERIOD 6 |
||||||
|
|
||||||
|
#include "serialize.h" |
||||||
|
#include "net.h" |
||||||
|
#include "uint256.h" |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
class CSoftCheckpoint; |
||||||
|
|
||||||
|
/** Block-chain checkpoints are compiled-in sanity checks.
|
||||||
|
* They are updated every release or three. |
||||||
|
*/ |
||||||
|
namespace SoftCheckpoints |
||||||
|
{ |
||||||
|
extern bool fEnabled; |
||||||
|
|
||||||
|
// Returns true if block passes checkpoint checks
|
||||||
|
bool CheckBlock(int nHeight, const uint256& hash); |
||||||
|
|
||||||
|
void NewBlockAccepted(); |
||||||
|
|
||||||
|
// returns true if vote is to be restransmitted
|
||||||
|
bool CastVoteSoftCheckpoint(int height, const uint256 &hash, const std::string &username, const std::string &sign); |
||||||
|
|
||||||
|
void RelayCP(const CSoftCheckpoint& cp, CNode* pfrom); |
||||||
|
|
||||||
|
void RelayLastCPToNode(CNode* pnode); |
||||||
|
} |
||||||
|
|
||||||
|
class CSoftCheckpoint |
||||||
|
{ |
||||||
|
public: |
||||||
|
int nHeight; |
||||||
|
uint256 blockHash; |
||||||
|
std::vector<char> vchUsername; |
||||||
|
std::vector<char> vchSign; |
||||||
|
|
||||||
|
CSoftCheckpoint() |
||||||
|
{ |
||||||
|
SetNull(); |
||||||
|
} |
||||||
|
|
||||||
|
IMPLEMENT_SERIALIZE |
||||||
|
( |
||||||
|
READWRITE(nHeight); |
||||||
|
READWRITE(blockHash); |
||||||
|
READWRITE(vchUsername); |
||||||
|
READWRITE(vchSign); |
||||||
|
) |
||||||
|
|
||||||
|
void SetNull() |
||||||
|
{ |
||||||
|
nHeight = 0; |
||||||
|
blockHash = uint256(); |
||||||
|
vchUsername.clear(); |
||||||
|
vchSign.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsNull() const |
||||||
|
{ |
||||||
|
return !nHeight; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue