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 <jasper.li@bitmain.com>
This commit is contained in:
Patrick 2021-10-27 11:30:03 -04:00
parent e90d4437a2
commit 2291b6a7cc
No known key found for this signature in database
GPG Key ID: 7C523F5FBABE80E7
2 changed files with 44 additions and 6 deletions

View File

@ -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"])

View File

@ -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<CScriptID, CBlock*> 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));
}