Browse Source

Merge pull request #2115 from forrestv/getblocktemplate_allfees

Provide fee data for all txs in RPC getblocktemplate response
0.8
Pieter Wuille 12 years ago
parent
commit
45a1ec51b1
  1. 28
      src/main.cpp
  2. 11
      src/main.h
  3. 41
      src/rpcmining.cpp
  4. 45
      src/test/miner_tests.cpp

28
src/main.cpp

@ -3732,12 +3732,13 @@ public:
} }
}; };
CBlock* CreateNewBlock(CReserveKey& reservekey) CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
{ {
// Create new block // Create new block
auto_ptr<CBlock> pblock(new CBlock()); auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
if (!pblock.get()) if(!pblocktemplate.get())
return NULL; return NULL;
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// Create coinbase tx // Create coinbase tx
CTransaction txNew; CTransaction txNew;
@ -3748,6 +3749,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Add our coinbase tx as first transaction // Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew); pblock->vtx.push_back(txNew);
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
// Largest block you're willing to create: // Largest block you're willing to create:
unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
@ -3925,6 +3928,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Added // Added
pblock->vtx.push_back(tx); pblock->vtx.push_back(tx);
pblocktemplate->vTxFees.push_back(nTxFees);
pblocktemplate->vTxSigOps.push_back(nTxSigOps);
nBlockSize += nTxSize; nBlockSize += nTxSize;
++nBlockTx; ++nBlockTx;
nBlockSigOps += nTxSigOps; nBlockSigOps += nTxSigOps;
@ -3959,13 +3964,15 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
pblocktemplate->vTxFees[0] = -nFees;
// Fill in header // Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->UpdateTime(pindexPrev); pblock->UpdateTime(pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
pblock->nNonce = 0; pblock->nNonce = 0;
pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0;
pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount();
CBlockIndex indexDummy(*pblock); CBlockIndex indexDummy(*pblock);
indexDummy.pprev = pindexPrev; indexDummy.pprev = pindexPrev;
@ -3975,7 +3982,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
} }
return pblock.release(); return pblocktemplate.release();
} }
@ -4118,10 +4125,11 @@ void static BitcoinMiner(CWallet *pwallet)
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest; CBlockIndex* pindexPrev = pindexBest;
auto_ptr<CBlock> pblock(CreateNewBlock(reservekey)); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey));
if (!pblock.get()) if (!pblocktemplate.get())
return; return;
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); CBlock *pblock = &pblocktemplate->block;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
@ -4134,7 +4142,7 @@ void static BitcoinMiner(CWallet *pwallet)
char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); FormatHashBuffers(pblock, pmidstate, pdata, phash1);
unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8);
@ -4170,7 +4178,7 @@ void static BitcoinMiner(CWallet *pwallet)
assert(hash == pblock->GetHash()); assert(hash == pblock->GetHash());
SetThreadPriority(THREAD_PRIORITY_NORMAL); SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock.get(), *pwalletMain, reservekey); CheckWork(pblock, *pwalletMain, reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST); SetThreadPriority(THREAD_PRIORITY_LOWEST);
break; break;
} }

11
src/main.h

@ -108,6 +108,8 @@ class CTxUndo;
class CCoinsView; class CCoinsView;
class CCoinsViewCache; class CCoinsViewCache;
struct CBlockTemplate;
/** Register a wallet to receive updates from core */ /** Register a wallet to receive updates from core */
void RegisterWallet(CWallet* pwalletIn); void RegisterWallet(CWallet* pwalletIn);
/** Unregister a wallet from core */ /** Unregister a wallet from core */
@ -139,7 +141,7 @@ void ThreadImport(void *parg);
/** Run the miner threads */ /** Run the miner threads */
void GenerateBitcoins(bool fGenerate, CWallet* pwallet); void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
/** Generate a new block, without valid proof-of-work */ /** Generate a new block, without valid proof-of-work */
CBlock* CreateNewBlock(CReserveKey& reservekey); CBlockTemplate* CreateNewBlock(CReserveKey& reservekey);
/** Modify the extranonce in a block */ /** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
/** Do mining precalculation */ /** Do mining precalculation */
@ -1975,4 +1977,11 @@ extern CCoinsViewCache *pcoinsTip;
/** Global variable that points to the active block tree (protected by cs_main) */ /** Global variable that points to the active block tree (protected by cs_main) */
extern CBlockTreeDB *pblocktree; extern CBlockTreeDB *pblocktree;
struct CBlockTemplate
{
CBlock block;
std::vector<int64_t> vTxFees;
std::vector<int64_t> vTxSigOps;
};
#endif #endif

41
src/rpcmining.cpp

@ -103,7 +103,7 @@ Value getwork(const Array& params, bool fHelp)
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t; typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
static mapNewBlock_t mapNewBlock; // FIXME: thread safety static mapNewBlock_t mapNewBlock; // FIXME: thread safety
static vector<CBlock*> vNewBlock; static vector<CBlockTemplate*> vNewBlockTemplate;
static CReserveKey reservekey(pwalletMain); static CReserveKey reservekey(pwalletMain);
if (params.size() == 0) if (params.size() == 0)
@ -112,7 +112,7 @@ Value getwork(const Array& params, bool fHelp)
static unsigned int nTransactionsUpdatedLast; static unsigned int nTransactionsUpdatedLast;
static CBlockIndex* pindexPrev; static CBlockIndex* pindexPrev;
static int64 nStart; static int64 nStart;
static CBlock* pblock; static CBlockTemplate* pblocktemplate;
if (pindexPrev != pindexBest || if (pindexPrev != pindexBest ||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
{ {
@ -120,9 +120,9 @@ Value getwork(const Array& params, bool fHelp)
{ {
// Deallocate old blocks since they're obsolete now // Deallocate old blocks since they're obsolete now
mapNewBlock.clear(); mapNewBlock.clear();
BOOST_FOREACH(CBlock* pblock, vNewBlock) BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate)
delete pblock; delete pblocktemplate;
vNewBlock.clear(); vNewBlockTemplate.clear();
} }
// Clear pindexPrev so future getworks make a new block, despite any failures from here on // Clear pindexPrev so future getworks make a new block, despite any failures from here on
@ -134,14 +134,15 @@ Value getwork(const Array& params, bool fHelp)
nStart = GetTime(); nStart = GetTime();
// Create new block // Create new block
pblock = CreateNewBlock(reservekey); pblocktemplate = CreateNewBlock(reservekey);
if (!pblock) if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
vNewBlock.push_back(pblock); vNewBlockTemplate.push_back(pblocktemplate);
// Need to update only after we know CreateNewBlock succeeded // Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew; pindexPrev = pindexPrevNew;
} }
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
// Update nTime // Update nTime
pblock->UpdateTime(pindexPrev); pblock->UpdateTime(pindexPrev);
@ -248,7 +249,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
static unsigned int nTransactionsUpdatedLast; static unsigned int nTransactionsUpdatedLast;
static CBlockIndex* pindexPrev; static CBlockIndex* pindexPrev;
static int64 nStart; static int64 nStart;
static CBlock* pblock; static CBlockTemplate* pblocktemplate;
if (pindexPrev != pindexBest || if (pindexPrev != pindexBest ||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{ {
@ -261,18 +262,19 @@ Value getblocktemplate(const Array& params, bool fHelp)
nStart = GetTime(); nStart = GetTime();
// Create new block // Create new block
if(pblock) if(pblocktemplate)
{ {
delete pblock; delete pblocktemplate;
pblock = NULL; pblocktemplate = NULL;
} }
pblock = CreateNewBlock(reservekey); pblocktemplate = CreateNewBlock(reservekey);
if (!pblock) if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
// Need to update only after we know CreateNewBlock succeeded // Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew; pindexPrev = pindexPrevNew;
} }
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
// Update nTime // Update nTime
pblock->UpdateTime(pindexPrev); pblock->UpdateTime(pindexPrev);
@ -281,7 +283,6 @@ Value getblocktemplate(const Array& params, bool fHelp)
Array transactions; Array transactions;
map<uint256, int64_t> setTxIndex; map<uint256, int64_t> setTxIndex;
int i = 0; int i = 0;
CCoinsViewCache &view = *pcoinsTip;
BOOST_FOREACH (CTransaction& tx, pblock->vtx) BOOST_FOREACH (CTransaction& tx, pblock->vtx)
{ {
uint256 txHash = tx.GetHash(); uint256 txHash = tx.GetHash();
@ -306,13 +307,9 @@ Value getblocktemplate(const Array& params, bool fHelp)
} }
entry.push_back(Pair("depends", deps)); entry.push_back(Pair("depends", deps));
int64_t nSigOps = tx.GetLegacySigOpCount(); int index_in_template = &tx - pblock->vtx.data();
if (tx.HaveInputs(view)) entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
{ entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(view) - tx.GetValueOut())));
nSigOps += tx.GetP2SHSigOpCount(view);
}
entry.push_back(Pair("sigops", nSigOps));
transactions.push_back(entry); transactions.push_back(entry);
} }

45
src/test/miner_tests.cpp

@ -49,19 +49,20 @@ struct {
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{ {
CReserveKey reservekey(pwalletMain); CReserveKey reservekey(pwalletMain);
CBlock *pblock; CBlockTemplate *pblocktemplate;
CTransaction tx; CTransaction tx;
CScript script; CScript script;
uint256 hash; uint256 hash;
// Simple block creation, nothing special yet: // Simple block creation, nothing special yet:
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
// We can't make transactions until we have inputs // We can't make transactions until we have inputs
// Therefore, load 100 blocks :) // Therefore, load 100 blocks :)
std::vector<CTransaction*>txFirst; std::vector<CTransaction*>txFirst;
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
{ {
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1; pblock->nVersion = 1;
pblock->nTime = pindexBest->GetMedianTimePast()+1; pblock->nTime = pindexBest->GetMedianTimePast()+1;
pblock->vtx[0].vin[0].scriptSig = CScript(); pblock->vtx[0].vin[0].scriptSig = CScript();
@ -75,10 +76,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
assert(ProcessBlock(NULL, pblock)); assert(ProcessBlock(NULL, pblock));
pblock->hashPrevBlock = pblock->GetHash(); pblock->hashPrevBlock = pblock->GetHash();
} }
delete pblock; delete pblocktemplate;
// Just to make sure we can still make simple blocks // Just to make sure we can still make simple blocks
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
// block sigops > limit: 1000 CHECKMULTISIG + 1 // block sigops > limit: 1000 CHECKMULTISIG + 1
tx.vin.resize(1); tx.vin.resize(1);
@ -95,8 +96,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
tx.vin[0].prevout.hash = hash; tx.vin[0].prevout.hash = hash;
} }
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// block size > limit // block size > limit
@ -115,15 +116,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
tx.vin[0].prevout.hash = hash; tx.vin[0].prevout.hash = hash;
} }
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// orphan in mempool // orphan in mempool
hash = tx.GetHash(); hash = tx.GetHash();
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// child with higher priority than parent // child with higher priority than parent
@ -140,8 +141,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = 5900000000LL; tx.vout[0].nValue = 5900000000LL;
hash = tx.GetHash(); hash = tx.GetHash();
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// coinbase in mempool // coinbase in mempool
@ -151,8 +152,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = 0; tx.vout[0].nValue = 0;
hash = tx.GetHash(); hash = tx.GetHash();
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// invalid (pre-p2sh) txn in mempool // invalid (pre-p2sh) txn in mempool
@ -169,8 +170,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue -= 1000000; tx.vout[0].nValue -= 1000000;
hash = tx.GetHash(); hash = tx.GetHash();
mempool.addUnchecked(hash,tx); mempool.addUnchecked(hash,tx);
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// double spend txn pair in mempool // double spend txn pair in mempool
@ -183,18 +184,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_2; tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash(); hash = tx.GetHash();
mempool.addUnchecked(hash, tx); mempool.addUnchecked(hash, tx);
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
mempool.clear(); mempool.clear();
// subsidy changing // subsidy changing
int nHeight = pindexBest->nHeight; int nHeight = pindexBest->nHeight;
pindexBest->nHeight = 209999; pindexBest->nHeight = 209999;
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
pindexBest->nHeight = 210000; pindexBest->nHeight = 210000;
BOOST_CHECK(pblock = CreateNewBlock(reservekey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey));
delete pblock; delete pblocktemplate;
pindexBest->nHeight = nHeight; pindexBest->nHeight = nHeight;
} }

Loading…
Cancel
Save