From 2291b6a7ccce26afd83ec00c2d7fdb5f2984ea92 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 27 Oct 2021 11:30:03 -0400 Subject: [PATCH] rpc: cache aux block per scriptPubKey in createauxblock - RPC caching source cherry-picked from: btccom@f4b613b2 - Adds addl test scenarios to createauxblock.py tests Allows pool operators to run multiple sub-pools with different target addresses from a single dogecoind instance. Without this enhancement, subsequent calls to createauxblock with differing addresses ignore the address given and instead just return the block containing the address that initially triggered generation of the cached block. This can quickly lead to unpredictable results as race scenarios between sub-pools come into play. Note that, like with getauxblock, the cache only resets on aux block creation, not submission, so submitauxblock will accept multiple submissions at the same height until createauxblock is called, resulting in chaintip forks. Co-Authored-By: leezhen --- qa/rpc-tests/createauxblock.py | 31 ++++++++++++++++++++++++++++--- src/rpc/mining.cpp | 19 ++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/qa/rpc-tests/createauxblock.py b/qa/rpc-tests/createauxblock.py index e4c02aea5..2c82c85ef 100644 --- a/qa/rpc-tests/createauxblock.py +++ b/qa/rpc-tests/createauxblock.py @@ -72,8 +72,7 @@ class CreateAuxBlockTest(BitcoinTestFramework): auxblock3 = self.nodes[0].createauxblock(dummy_addr2) # ... must give another block because the coinbase recipient differs ... - # TODO: Need to cache per address on createauxblock for this to work - # assert auxblock3["hash"] != auxblock["hash"] + assert auxblock3["hash"] != auxblock["hash"] # ... but must have retained the same parameterization otherwise assert_equal(auxblock["coinbasevalue"], auxblock3["coinbasevalue"]) @@ -133,8 +132,10 @@ class CreateAuxBlockTest(BitcoinTestFramework): # check the mined block and transaction self.check_mined_block(auxblock, apow, dummy_p2pkh_addr, Decimal("500000"), txid) - # Mine to a p2sh address + # Mine to a p2sh address while having multiple cached aux block templates + auxblock1 = self.nodes[0].createauxblock(dummy_p2pkh_addr) auxblock2 = self.nodes[0].createauxblock(dummy_p2sh_addr) + auxblock3 = self.nodes[0].createauxblock(dummy_addr2) reversedTarget = auxpow.reverseHex(auxblock2["target"]) apow = auxpow.computeAuxpowWithChainId(auxblock2["hash"], reversedTarget, "98", True) res = self.nodes[0].submitauxblock(auxblock2["hash"], apow) @@ -145,6 +146,30 @@ class CreateAuxBlockTest(BitcoinTestFramework): # check the mined block self.check_mined_block(auxblock2, apow, dummy_p2sh_addr, Decimal("500000")) + # Solve the first p2pkh template before requesting a new auxblock + # this succeeds but creates a chaintip fork + reversedTarget = auxpow.reverseHex(auxblock1["target"]) + apow = auxpow.computeAuxpowWithChainId(auxblock1["hash"], reversedTarget, "98", True) + res = self.nodes[0].submitauxblock(auxblock1["hash"], apow) + assert res + + chaintips = self.nodes[0].getchaintips() + tipsFound = 0; + for ct in chaintips: + if ct["hash"] in [ auxblock1["hash"], auxblock2["hash"] ]: + tipsFound += 1 + assert_equal(tipsFound, 2) + + # Solve the last p2pkh template after requesting a new auxblock - this fails + self.nodes[0].createauxblock(dummy_p2pkh_addr) + reversedTarget = auxpow.reverseHex(auxblock3["target"]) + apow = auxpow.computeAuxpowWithChainId(auxblock3["hash"], reversedTarget, "98", True) + try: + self.nodes[0].submitauxblock(auxblock3["hash"], apow) + raise AssertionError("Outdated blockhash accepted") + except JSONRPCException as exc: + assert_equal(exc.error["code"], -8) + def check_mined_block(self, auxblock, apow, addr, min_value, txid=None): # Call getblock and verify the auxpow field. data = self.nodes[1].getblock(auxblock["hash"]) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 08da3fa13..4e52734a9 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1008,16 +1008,27 @@ static UniValue AuxMiningCreateBlock(const CScript& scriptPubKey) static unsigned nTransactionsUpdatedLast; static const CBlockIndex* pindexPrev = nullptr; static uint64_t nStart; - static CBlock* pblock = nullptr; + static std::map curBlocks; static unsigned nExtraNonce = 0; // Dogecoin: Never mine witness tx const bool fMineWitnessTx = false; - // Update block + /* Search for cached blocks with given scriptPubKey and assign it to pBlock + * if we find a match. This allows for creating multiple aux templates with + * a single dogecoind instance, for example when a pool runs multiple sub- + * pools with different payout strategies. + */ + CBlock* pblock = nullptr; + CScriptID scriptID (scriptPubKey); + auto iter = curBlocks.find(scriptID); + if (iter != curBlocks.end()) pblock = iter->second; + { LOCK(cs_main); - if (pindexPrev != chainActive.Tip() + + // Update block + if (pblock == nullptr || pindexPrev != chainActive.Tip() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { @@ -1026,6 +1037,7 @@ static UniValue AuxMiningCreateBlock(const CScript& scriptPubKey) // Clear old blocks since they're obsolete now. mapNewBlock.clear(); vNewBlockTemplate.clear(); + curBlocks.clear(); pblock = nullptr; } @@ -1046,6 +1058,7 @@ static UniValue AuxMiningCreateBlock(const CScript& scriptPubKey) // Save pblock = &newBlock->block; + curBlocks[scriptID] = pblock; mapNewBlock[pblock->GetHash()] = pblock; vNewBlockTemplate.push_back(std::move(newBlock)); }