new getwork

git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@189 1a98c847-1fd6-4fd8-948a-caf3550aa51b
This commit is contained in:
s_nakamoto
2010-11-23 19:16:36 +00:00
parent e4ff4e6898
commit 776d0f3459
10 changed files with 467 additions and 252 deletions

524
main.cpp
View File

@@ -159,7 +159,7 @@ bool AddToWallet(const CWalletTx& wtxIn)
if (txout.scriptPubKey == scriptDefaultKey)
{
CWalletDB walletdb;
vchDefaultKey = walletdb.GetKeyFromKeyPool();
vchDefaultKey = GetKeyFromKeyPool();
walletdb.WriteDefaultKey(vchDefaultKey);
walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
}
@@ -1557,31 +1557,25 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
// Preliminary checks
if (!pblock->CheckBlock())
{
delete pblock;
return error("ProcessBlock() : CheckBlock FAILED");
}
// If don't already have its previous block, shunt it off to holding area until we get it
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
mapOrphanBlocks.insert(make_pair(hash, pblock));
mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
CBlock* pblock2 = new CBlock(*pblock);
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
// Ask this guy to fill in what we're missing
if (pfrom)
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock));
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
return true;
}
// Store to disk
if (!pblock->AcceptBlock())
{
delete pblock;
return error("ProcessBlock() : AcceptBlock FAILED");
}
delete pblock;
// Recursively process any orphan blocks that depended on this one
vector<uint256> vWorkQueue;
@@ -2504,7 +2498,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_BLOCK, pblock->GetHash());
pfrom->AddInventoryKnown(inv);
if (ProcessBlock(pfrom, pblock.release()))
if (ProcessBlock(pfrom, pblock.get()))
mapAlreadyAskedFor.erase(inv);
}
@@ -2549,7 +2543,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Keep giving the same key to the same ip until they use it
if (!mapReuseKey.count(pfrom->addr.ip))
mapReuseKey[pfrom->addr.ip] = CWalletDB().GetKeyFromKeyPool();
mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool();
// Send back approval of order and pubkey to use
CScript scriptPubKey;
@@ -3000,19 +2994,19 @@ inline void SHA256Transform(void* pstate, void* pinput, const void* pinit)
// ScanHash scans nonces looking for a hash with at least some zero bits.
// It operates on big endian data. Caller does the byte reversing.
// All input buffers are 16-byte aligned. nNonce is usually preserved
// between calls, but periodically or if nNonce is above 0xff000000,
// between calls, but periodically or if nNonce is 0xffff0000 or above,
// the block is rebuilt and nNonce starts over at zero.
//
unsigned int ScanHash_CryptoPP(char* pmidstate, char* pblock, char* phash1, char* phash, unsigned int& nHashesDone)
unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
{
unsigned int& nNonce = *(unsigned int*)(pblock + 12);
unsigned int& nNonce = *(unsigned int*)(pdata + 12);
for (;;)
{
// Crypto++ SHA-256
// Hash pblock using pmidstate as the starting state into
// Hash pdata using pmidstate as the starting state into
// preformatted buffer phash1, then hash phash1 into phash
nNonce++;
SHA256Transform(phash1, pblock, pmidstate);
SHA256Transform(phash1, pdata, pmidstate);
SHA256Transform(phash, phash1, pSHA256InitState);
// Return the nonce if the hash has at least some zero bits,
@@ -3055,6 +3049,262 @@ public:
};
CBlock* CreateNewBlock(CReserveKey& reservekey)
{
CBlockIndex* pindexPrev = pindexBest;
// Create new block
auto_ptr<CBlock> pblock(new CBlock());
if (!pblock.get())
return NULL;
// Create coinbase tx
CTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);
// Collect memory pool transactions into the block
int64 nFees = 0;
CRITICAL_BLOCK(cs_main)
CRITICAL_BLOCK(cs_mapTransactions)
{
CTxDB txdb("r");
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
map<uint256, vector<COrphan*> > mapDependers;
multimap<double, CTransaction*> mapPriority;
for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi)
{
CTransaction& tx = (*mi).second;
if (tx.IsCoinBase() || !tx.IsFinal())
continue;
COrphan* porphan = NULL;
double dPriority = 0;
foreach(const CTxIn& txin, tx.vin)
{
// Read prev transaction
CTransaction txPrev;
CTxIndex txindex;
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
{
// Has to wait for dependencies
if (!porphan)
{
// Use list for automatic deletion
vOrphan.push_back(COrphan(&tx));
porphan = &vOrphan.back();
}
mapDependers[txin.prevout.hash].push_back(porphan);
porphan->setDependsOn.insert(txin.prevout.hash);
continue;
}
int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
// Read block header
int nConf = 0;
CBlock block;
if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
{
map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(block.GetHash());
if (it != mapBlockIndex.end())
{
CBlockIndex* pindex = (*it).second;
if (pindex->IsInMainChain())
nConf = 1 + nBestHeight - pindex->nHeight;
}
}
dPriority += (double)nValueIn * nConf;
if (fDebug && mapArgs.count("-printpriority"))
printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
}
// Priority is sum(valuein * age) / txsize
dPriority /= ::GetSerializeSize(tx, SER_NETWORK);
if (porphan)
porphan->dPriority = dPriority;
else
mapPriority.insert(make_pair(-dPriority, &(*mi).second));
if (fDebug && mapArgs.count("-printpriority"))
{
printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
if (porphan)
porphan->print();
printf("\n");
}
}
// Collect transactions into block
map<uint256, CTxIndex> mapTestPool;
uint64 nBlockSize = 1000;
int nBlockSigOps = 100;
while (!mapPriority.empty())
{
// Take highest priority transaction off priority queue
double dPriority = -(*mapPriority.begin()).first;
CTransaction& tx = *(*mapPriority.begin()).second;
mapPriority.erase(mapPriority.begin());
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue;
int nTxSigOps = tx.GetSigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Transaction fee required depends on block size
bool fAllowFree = (nBlockSize + nTxSize < 4000 || dPriority > COIN * 144 / 250);
int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree);
// Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
continue;
swap(mapTestPool, mapTestPoolTmp);
// Added
pblock->vtx.push_back(tx);
nBlockSize += nTxSize;
nBlockSigOps += nTxSigOps;
// Add transactions that depend on this one to the priority queue
uint256 hash = tx.GetHash();
if (mapDependers.count(hash))
{
foreach(COrphan* porphan, mapDependers[hash])
{
if (!porphan->setDependsOn.empty())
{
porphan->setDependsOn.erase(hash);
if (porphan->setDependsOn.empty())
mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
}
}
}
}
}
pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
pblock->nBits = GetNextWorkRequired(pindexPrev);
pblock->nNonce = 0;
return pblock.release();
}
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime)
{
// Update nExtraNonce
int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1)
{
nExtraNonce = 1;
nPrevTime = nNow;
}
pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce);
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
{
//
// Prebuild hash buffers
//
struct
{
struct unnamed2
{
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
}
block;
unsigned char pchPadding0[64];
uint256 hash1;
unsigned char pchPadding1[64];
}
tmp;
memset(&tmp, 0, sizeof(tmp));
tmp.block.nVersion = pblock->nVersion;
tmp.block.hashPrevBlock = pblock->hashPrevBlock;
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
tmp.block.nTime = pblock->nTime;
tmp.block.nBits = pblock->nBits;
tmp.block.nNonce = pblock->nNonce;
FormatHashBlocks(&tmp.block, sizeof(tmp.block));
FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
// Byte swap all the input buffer
for (int i = 0; i < sizeof(tmp)/4; i++)
((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
// Precalc the first half of the first hash, which stays constant
SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
memcpy(pdata, &tmp.block, 128);
memcpy(phash1, &tmp.hash1, 64);
}
bool CheckWork(CBlock* pblock, CReserveKey& reservekey)
{
uint256 hash = pblock->GetHash();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
if (hash > hashTarget)
return false;
//// debug print
printf("BitcoinMiner:\n");
printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
pblock->print();
printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
// Found a solution
CRITICAL_BLOCK(cs_main)
{
if (pblock->hashPrevBlock != hashBestChain)
return error("BitcoinMiner : generated block is stale");
// Remove key from key pool
reservekey.KeepKey();
// Track how many getdata requests this block gets
CRITICAL_BLOCK(cs_mapRequestCount)
mapRequestCount[pblock->GetHash()] = 0;
// Process this block the same as if we had received it from another node
if (!ProcessBlock(NULL, pblock))
return error("BitcoinMiner : ProcessBlock, block not accepted");
}
Sleep(2000);
return true;
}
void BitcoinMiner()
{
@@ -3064,9 +3314,11 @@ void BitcoinMiner()
if (mapArgs.count("-4way"))
f4WaySSE2 = (mapArgs["-4way"] != "0");
// Each thread has its own key and counter
CReserveKey reservekey;
unsigned int nExtraNonce = 0;
int64 nPrevTime = 0;
while (fGenerateBitcoins)
{
if (AffinityBugWorkaround(ThreadBitcoinMiner))
@@ -3082,210 +3334,32 @@ void BitcoinMiner()
return;
}
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
unsigned int nBits = GetNextWorkRequired(pindexPrev);
//
// Create coinbase tx
//
CTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1)
{
nExtraNonce = 1;
nPrevTime = nNow;
}
txNew.vin[0].scriptSig << nBits << CBigNum(nExtraNonce);
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
//
// Create new block
//
auto_ptr<CBlock> pblock(new CBlock());
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
auto_ptr<CBlock> pblock(CreateNewBlock(reservekey));
if (!pblock.get())
return;
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime);
// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);
// Collect memory pool transactions into the block
int64 nFees = 0;
CRITICAL_BLOCK(cs_main)
CRITICAL_BLOCK(cs_mapTransactions)
{
CTxDB txdb("r");
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
map<uint256, vector<COrphan*> > mapDependers;
multimap<double, CTransaction*> mapPriority;
for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi)
{
CTransaction& tx = (*mi).second;
if (tx.IsCoinBase() || !tx.IsFinal())
continue;
COrphan* porphan = NULL;
double dPriority = 0;
foreach(const CTxIn& txin, tx.vin)
{
// Read prev transaction
CTransaction txPrev;
CTxIndex txindex;
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
{
// Has to wait for dependencies
if (!porphan)
{
// Use list for automatic deletion
vOrphan.push_back(COrphan(&tx));
porphan = &vOrphan.back();
}
mapDependers[txin.prevout.hash].push_back(porphan);
porphan->setDependsOn.insert(txin.prevout.hash);
continue;
}
int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
// Read block header
int nConf = 0;
CBlock block;
if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
{
map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(block.GetHash());
if (it != mapBlockIndex.end())
{
CBlockIndex* pindex = (*it).second;
if (pindex->IsInMainChain())
nConf = 1 + nBestHeight - pindex->nHeight;
}
}
dPriority += (double)nValueIn * nConf;
if (fDebug && mapArgs.count("-printpriority"))
printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
}
// Priority is sum(valuein * age) / txsize
dPriority /= ::GetSerializeSize(tx, SER_NETWORK);
if (porphan)
porphan->dPriority = dPriority;
else
mapPriority.insert(make_pair(-dPriority, &(*mi).second));
if (fDebug && mapArgs.count("-printpriority"))
{
printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
if (porphan)
porphan->print();
printf("\n");
}
}
// Collect transactions into block
map<uint256, CTxIndex> mapTestPool;
uint64 nBlockSize = 1000;
int nBlockSigOps = 100;
while (!mapPriority.empty())
{
// Take highest priority transaction off priority queue
double dPriority = -(*mapPriority.begin()).first;
CTransaction& tx = *(*mapPriority.begin()).second;
mapPriority.erase(mapPriority.begin());
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue;
int nTxSigOps = tx.GetSigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Transaction fee required depends on block size
bool fAllowFree = (nBlockSize + nTxSize < 4000 || dPriority > COIN * 144 / 250);
int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree);
// Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
continue;
swap(mapTestPool, mapTestPoolTmp);
// Added
pblock->vtx.push_back(tx);
nBlockSize += nTxSize;
nBlockSigOps += nTxSigOps;
// Add transactions that depend on this one to the priority queue
uint256 hash = tx.GetHash();
if (mapDependers.count(hash))
{
foreach(COrphan* porphan, mapDependers[hash])
{
if (!porphan->setDependsOn.empty())
{
porphan->setDependsOn.erase(hash);
if (porphan->setDependsOn.empty())
mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
}
}
}
}
}
pblock->nBits = nBits;
pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
//
// Prebuild hash buffer
// Prebuild hash buffers
//
struct tmpworkspace
{
struct unnamed2
{
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
}
block;
unsigned char pchPadding0[64];
uint256 hash1;
unsigned char pchPadding1[64];
};
char tmpbuf[sizeof(tmpworkspace)+16];
tmpworkspace& tmp = *(tmpworkspace*)alignup<16>(tmpbuf);
char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf);
char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
tmp.block.nVersion = pblock->nVersion;
tmp.block.hashPrevBlock = pblock->hashPrevBlock = pindexPrev->GetBlockHash();
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot = pblock->BuildMerkleTree();
tmp.block.nTime = pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
tmp.block.nBits = pblock->nBits = nBits;
tmp.block.nNonce = pblock->nNonce = 0;
FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1);
unsigned int nBlocks0 = FormatHashBlocks(&tmp.block, sizeof(tmp.block));
unsigned int nBlocks1 = FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
// Byte swap all the input buffer
for (int i = 0; i < sizeof(tmp)/4; i++)
((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
// Precalc the first half of the first hash, which stays constant
uint256 midstatebuf[2];
uint256& midstate = *alignup<16>(midstatebuf);
SHA256Transform(&midstate, &tmp.block, pSHA256InitState);
unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
//
@@ -3303,11 +3377,11 @@ void BitcoinMiner()
#ifdef FOURWAYSSE2
if (f4WaySSE2)
// tcatm's 4-way 128-bit SSE2 SHA-256
nNonceFound = ScanHash_4WaySSE2((char*)&midstate, (char*)&tmp.block + 64, (char*)&tmp.hash1, (char*)&hash, nHashesDone);
nNonceFound = ScanHash_4WaySSE2(pmidstate, pdata + 64, phash1, (char*)&hash, nHashesDone);
else
#endif
// Crypto++ SHA-256
nNonceFound = ScanHash_CryptoPP((char*)&midstate, (char*)&tmp.block + 64, (char*)&tmp.hash1, (char*)&hash, nHashesDone);
nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, (char*)&hash, nHashesDone);
// Check if something found
if (nNonceFound != -1)
@@ -3321,33 +3395,9 @@ void BitcoinMiner()
pblock->nNonce = ByteReverse(nNonceFound);
assert(hash == pblock->GetHash());
//// debug print
printf("BitcoinMiner:\n");
printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
pblock->print();
printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CRITICAL_BLOCK(cs_main)
{
if (pindexPrev == pindexBest)
{
// Remove key from key pool
reservekey.KeepKey();
// Track how many getdata requests this block gets
CRITICAL_BLOCK(cs_mapRequestCount)
mapRequestCount[pblock->GetHash()] = 0;
// Process this block the same as if we had received it from another node
if (!ProcessBlock(NULL, pblock.release()))
printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n");
}
}
CheckWork(pblock.get(), reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
Sleep(500);
break;
}
}
@@ -3393,7 +3443,7 @@ void BitcoinMiner()
return;
if (vNodes.empty())
break;
if (tmp.block.nNonce >= 0xff000000)
if (nBlockNonce >= 0xffff0000)
break;
if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
@@ -3402,7 +3452,7 @@ void BitcoinMiner()
// Update nTime every few seconds
pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
tmp.block.nTime = ByteReverse(pblock->nTime);
nBlockTime = ByteReverse(pblock->nTime);
}
}
}