Support auxillary proof of work.

Rebased from 1.7.1 into 1.7.2, moved AuxPoW checks from AcceptBlock()
into AcceptBlockHeader()
This commit is contained in:
Nell Hardcastle 2014-04-11 00:01:49 -07:00 committed by Patrick Lodder
parent e87673bd38
commit 8808f237aa
9 changed files with 250 additions and 73 deletions

View file

@ -29,6 +29,7 @@ DIST_SUBDIRS = . qt test
BITCOIN_CORE_H = \
addrman.h \
alert.h \
auxpow.h \
allocators.h \
base58.h bignum.h \
bloom.h \
@ -93,6 +94,7 @@ version.o: obj/build.h
libbitcoin_server_a_SOURCES = \
addrman.cpp \
alert.cpp \
auxpow.cpp \
bloom.cpp \
checkpoints.cpp \
coins.cpp \

View file

@ -4,9 +4,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "core.h"
#include "auxpow.h"
#include "util.h"
int GetOurChainID()
{
return 0x0000;
}
std::string COutPoint::ToString() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);

View file

@ -17,10 +17,34 @@
#include <stdint.h>
class CTransaction;
class CAuxPow;
template <typename Stream>
int ReadWriteAuxPow(Stream& s, const boost::shared_ptr<CAuxPow>& auxpow, int nType, int nVersion, CSerActionSerialize ser_action);
template <typename Stream>
int ReadWriteAuxPow(Stream& s, boost::shared_ptr<CAuxPow>& auxpow, int nType, int nVersion, CSerActionUnserialize ser_action);
template <typename Stream>
int ReadWriteAuxPow(Stream& s, const boost::shared_ptr<CAuxPow>& auxpow, int nType, int nVersion, CSerActionGetSerializeSize ser_action);
enum
{
// primary version
BLOCK_VERSION_DEFAULT = (1 << 0),
// modifiers
BLOCK_VERSION_AUXPOW = (1 << 8),
// bits allocated for chain ID
BLOCK_VERSION_CHAIN_START = (1 << 16),
BLOCK_VERSION_CHAIN_END = (1 << 30),
};
/** No amount larger than this (in satoshi) is valid */
static const int64_t MAX_MONEY = 10000000000 * COIN; // Dogecoin: maximum of 100B coins (given some randomness), max transaction 10,000,000,000
inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
int GetOurChainID();
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
@ -350,6 +374,7 @@ public:
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
boost::shared_ptr<CAuxPow> auxpow;
CBlockHeader()
{
@ -365,11 +390,27 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
nSerSize += ReadWriteAuxPow(s, auxpow, nType, nVersion, ser_action);
)
int GetChainID() const
{
return nVersion / BLOCK_VERSION_CHAIN_START;
}
uint256 GetPoWHash() const
{
uint256 thash;
scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
return thash;
}
void SetAuxPow(CAuxPow* pow);
void SetNull()
{
nVersion = CBlockHeader::CURRENT_VERSION;
nVersion = CBlockHeader::CURRENT_VERSION | (GetOurChainID() * BLOCK_VERSION_CHAIN_START);
hashPrevBlock = 0;
hashMerkleRoot = 0;
nTime = 0;
@ -395,6 +436,8 @@ public:
{
return (int64_t)nTime;
}
bool CheckProofOfWork(int nHeight) const;
};

View file

@ -14,6 +14,7 @@
#include "checkpoints.h"
#include "checkqueue.h"
#include "init.h"
#include "auxpow.h"
#include "net.h"
#include "txdb.h"
#include "txmempool.h"
@ -1133,7 +1134,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
}
// Check the header
if (!CheckProofOfWork(block.GetPoWHash(), block.nBits))
if (!block.CheckProofOfWork(mapBlockIndex[block.GetHash()]->nHeight))
return error("ReadBlockFromDisk : Errors in block header");
return true;
@ -1148,6 +1149,15 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
return true;
}
void CBlockHeader::SetAuxPow(CAuxPow* pow)
{
if (pow != NULL)
nVersion |= BLOCK_VERSION_AUXPOW;
else
nVersion &= ~BLOCK_VERSION_AUXPOW;
auxpow.reset(pow);
}
uint256 static GetOrphanRoot(const uint256& hash)
{
map<uint256, COrphanBlock*>::iterator it = mapOrphanBlocks.find(hash);
@ -1543,7 +1553,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
}
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
pblocktree->WriteBlockIndex(*pindex);
setBlockIndexValid.erase(pindex);
InvalidChainFound(pindex);
}
@ -1840,7 +1850,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
{
AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
if (!CheckBlock(block, state, pindex->nHeight, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
@ -1985,8 +1995,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
CDiskBlockIndex blockindex(pindex);
if (!pblocktree->WriteBlockIndex(blockindex))
if (!pblocktree->WriteBlockIndex(*pindex))
return state.Abort(_("Failed to write block index"));
}
@ -2312,7 +2321,8 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
if (pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS))
setBlockIndexValid.insert(pindexNew);
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
/* write both the immutible data (CDiskBlockIndex) and the mutable data (BlockIndex) */
if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew))
return state.Abort(_("Failed to write block index"));
// New best?
@ -2338,6 +2348,58 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
return true;
}
// to enable merged mining:
// - set a block from which it will be enabled
// - set a unique chain ID
// each merged minable scrypt_1024_1_1_256 coin should have a different one
// (if two have the same ID, they can't be merge mined together)
int GetAuxPowStartBlock()
{
if (TestNet())
return INT_MAX; // never
else
return INT_MAX; // never
}
bool CBlockHeader::CheckProofOfWork(int nHeight) const
{
if (nHeight >= GetAuxPowStartBlock())
{
// Prevent same work from being submitted twice:
// - this block must have our chain ID
// - parent block must not have the same chain ID (see CAuxPow::Check)
// - index of this chain in chain merkle tree must be pre-determined (see CAuxPow::Check)
if (!TestNet() && nHeight != INT_MAX && GetChainID() != GetOurChainID())
return error("CheckProofOfWork() : block does not have our chain ID");
if (auxpow.get() != NULL)
{
if (!auxpow->Check(GetHash(), GetChainID()))
return error("CheckProofOfWork() : AUX POW is not valid");
// Check proof of work matches claimed amount
if (!::CheckProofOfWork(auxpow->GetParentBlockHash(), nBits))
return error("CheckProofOfWork() : AUX proof of work failed");
}
else
{
// Check proof of work matches claimed amount
if (!::CheckProofOfWork(GetPoWHash(), nBits))
return error("CheckProofOfWork() : proof of work failed");
}
}
else
{
if (auxpow.get() != NULL)
{
return error("CheckProofOfWork() : AUX POW is not allowed at this block");
}
// Check if proof of work marches claimed amount
if (!::CheckProofOfWork(GetPoWHash(), nBits))
return error("CheckProofOfWork() : proof of work failed");
}
return true;
}
bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
@ -2433,11 +2495,10 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
return true;
}
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW)
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, int nHeight, bool fCheckPOW)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(), block.nBits))
if (fCheckPOW && !block.CheckProofOfWork(nHeight))
return state.DoS(50, error("CheckBlockHeader() : proof of work failed"),
REJECT_INVALID, "high-hash");
@ -2470,12 +2531,12 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return true;
}
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
bool CheckBlock(const CBlock& block, CValidationState& state, int nHeight, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.
if (!CheckBlockHeader(block, state, fCheckPOW))
if (!CheckBlockHeader(block, state, nHeight, fCheckPOW))
return false;
// Size limits
@ -2602,14 +2663,15 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
if (!AcceptBlockHeader(block, state, &pindex))
return false;
if (!CheckBlock(block, state)) {
int nHeight = pindex->nHeight;
if (!CheckBlock(block, state, nHeight)) {
if (state.Invalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
}
return false;
}
int nHeight = pindex->nHeight;
uint256 hash = pindex->GetBlockHash();
// Check that all transactions are finalized
@ -2695,6 +2757,48 @@ int64_t CBlockIndex::GetMedianTime() const
return pindex->GetMedianTimePast();
}
std::string CBlockIndex::ToString() const
{
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, nHeight,
hashMerkleRoot.ToString().substr(0,10).c_str(),
GetBlockHash().ToString().c_str());
}
std::string CDiskBlockIndex::ToString() const
{
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s, hashParentBlock=%s)",
GetBlockHash().ToString().c_str(),
hashPrev.ToString().c_str(),
(auxpow.get() != NULL) ? auxpow->GetParentBlockHash().ToString().substr(0,20).c_str() : "-");
return str;
}
CBlockHeader CBlockIndex::GetBlockHeader() const
{
CBlockHeader block;
if (nVersion & BLOCK_VERSION_AUXPOW) {
CDiskBlockIndex diskblockindex;
// auxpow is not in memory, load CDiskBlockHeader
// from database to get it
pblocktree->ReadDiskBlockIndex(*phashBlock, diskblockindex);
block.auxpow = diskblockindex.auxpow;
}
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd)
{
AssertLockHeld(cs_main);
@ -2719,8 +2823,11 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate");
// Preliminary checks
if (!CheckBlock(*pblock, state))
if (!CheckBlock(*pblock, state, INT_MAX)) {
if (state.CorruptionPossible())
mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash));
return error("ProcessBlock() : CheckBlock FAILED");
}
// If we don't already have its previous block (with full data), shunt it off to holding area until we get it
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pblock->hashPrevBlock);
@ -3098,7 +3205,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth)
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state))
if (nCheckLevel >= 1 && !CheckBlock(block, state, pindex->nHeight))
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {

View file

@ -622,8 +622,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos);
// Context-independent validity checks
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, int nHeight, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, int nHeight, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
// Store block on disk
// if dbp is provided, the file is known to already reside on disk
@ -794,6 +794,22 @@ public:
nNonce = block.nNonce;
}
IMPLEMENT_SERIALIZE
(
/* mutable stuff goes here, immutable stuff
* has SERIALIZE functions in CDiskBlockIndex */
if (!(nType & SER_GETHASH))
READWRITE(VARINT(nVersion));
READWRITE(VARINT(nStatus));
if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO))
READWRITE(VARINT(nFile));
if (nStatus & BLOCK_HAVE_DATA)
READWRITE(VARINT(nDataPos));
if (nStatus & BLOCK_HAVE_UNDO)
READWRITE(VARINT(nUndoPos));
)
CDiskBlockPos GetBlockPos() const {
CDiskBlockPos ret;
if (nStatus & BLOCK_HAVE_DATA) {
@ -812,18 +828,7 @@ public:
return ret;
}
CBlockHeader GetBlockHeader() const
{
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
CBlockHeader GetBlockHeader() const;
uint256 GetBlockHash() const
{
@ -876,13 +881,7 @@ public:
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
unsigned int nRequired, unsigned int nToCheck);
std::string ToString() const
{
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, nHeight,
hashMerkleRoot.ToString().c_str(),
GetBlockHash().ToString().c_str());
}
std::string ToString() const; //moved code to main.cpp because new method required access to auxpow
void print() const
{
@ -921,28 +920,28 @@ class CDiskBlockIndex : public CBlockIndex
public:
uint256 hashPrev;
// if this is an aux work block
boost::shared_ptr<CAuxPow> auxpow;
CDiskBlockIndex() {
hashPrev = 0;
auxpow.reset();
}
explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) {
explicit CDiskBlockIndex(CBlockIndex* pindex, boost::shared_ptr<CAuxPow> auxpow) : CBlockIndex(*pindex) {
hashPrev = (pprev ? pprev->GetBlockHash() : 0);
this->auxpow = auxpow;
}
IMPLEMENT_SERIALIZE
(
/* immutable stuff goes here, mutable stuff
* has SERIALIZE functions in CBlockIndex */
if (!(nType & SER_GETHASH))
READWRITE(VARINT(nVersion));
READWRITE(VARINT(nHeight));
READWRITE(VARINT(nStatus));
READWRITE(VARINT(nTx));
if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO))
READWRITE(VARINT(nFile));
if (nStatus & BLOCK_HAVE_DATA)
READWRITE(VARINT(nDataPos));
if (nStatus & BLOCK_HAVE_UNDO)
READWRITE(VARINT(nUndoPos));
// block header
READWRITE(this->nVersion);
@ -951,9 +950,10 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
ReadWriteAuxPow(s, auxpow, nType, this->nVersion, ser_action);
)
uint256 GetBlockHash() const
uint256 CalcBlockHash() const
{
CBlockHeader block;
block.nVersion = nVersion;
@ -966,15 +966,7 @@ public:
}
std::string ToString() const
{
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s)",
GetBlockHash().ToString().c_str(),
hashPrev.ToString().c_str());
return str;
}
std::string ToString() const; // moved code to main.cpp
void print() const
{

View file

@ -7,6 +7,7 @@
#include "base58.h"
#include "key.h"
#include "main.h"
#include "auxpow.h"
#include "serialize.h"
#include "uint256.h"
#include "util.h"

View file

@ -9,6 +9,7 @@
#include "main.h"
#include "auxpow.h"
#include <cstdio>

View file

@ -7,6 +7,7 @@
#include "core.h"
#include "uint256.h"
#include "auxpow.h"
#include <stdint.h>
@ -68,9 +69,19 @@ bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const u
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
}
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
bool CBlockTreeDB::WriteDiskBlockIndex(const CDiskBlockIndex& diskblockindex)
{
return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
return Write(boost::tuples::make_tuple('b', *diskblockindex.phashBlock, 'a'), diskblockindex);
}
bool CBlockTreeDB::WriteBlockIndex(const CBlockIndex& blockindex)
{
return Write(boost::tuples::make_tuple('b', blockindex.GetBlockHash(), 'b'), blockindex);
}
bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blkid, CDiskBlockIndex &diskblockindex)
{
return Read(boost::tuples::make_tuple('b', blkid, 'a'), diskblockindex);
}
bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork)
@ -186,7 +197,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
leveldb::Iterator *pcursor = NewIterator();
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
ssKeySet << make_pair('b', uint256(0));
ssKeySet << boost::tuples::make_tuple('b', uint256(0), 'a'); // 'b' is the prefix for BlockIndex, 'a' sigifies the first part
uint256 hash;
char cType;
pcursor->Seek(ssKeySet.str());
// Load mapBlockIndex
@ -195,32 +208,41 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
try {
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType;
ssKey >> chType;
if (chType == 'b') {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockIndex diskindex;
ssValue >> diskindex;
ssKey >> cType;
if (cType == 'b') {
ssKey >> hash;
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue_immutable(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockIndex diskindex;
ssValue_immutable >> diskindex; // read all immutable data
// Construct immutable parts of block index objecty
CBlockIndex* pindexNew = InsertBlockIndex(hash);
assert(diskindex.CalcBlockHash() == *pindexNew->phashBlock); // paranoia check
// Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
pindexNew->nHeight = diskindex.nHeight;
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
if (!pindexNew->CheckIndex())
// CheckIndex need phashBlock to be set
diskindex.phashBlock = pindexNew->phashBlock;
if (!diskindex.CheckIndex())
return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString());
pcursor->Next(); // now we should be on the 'b' subkey
assert(pcursor->Valid());
slValue = pcursor->value();
CDataStream ssValue_mutable(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
ssValue_mutable >> *pindexNew; // read all mutable data
pcursor->Next();
} else {
break; // if shutdown requested or finished loading block index

View file

@ -51,7 +51,9 @@ private:
CBlockTreeDB(const CBlockTreeDB&);
void operator=(const CBlockTreeDB&);
public:
bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
bool WriteDiskBlockIndex(const CDiskBlockIndex& diskblockindex);
bool WriteBlockIndex(const CBlockIndex& blockindex);
bool ReadDiskBlockIndex(const uint256 &blkid, CDiskBlockIndex& diskblockindex);
bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);