mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
Merge #6996: Add preciousblock RPC
5805ac8
Add preciousblock tests (Pieter Wuille)5127c4f
Add preciousblock RPC (Pieter Wuille)
This commit is contained in:
parent
b02e461fae
commit
6e871c0c2e
@ -151,6 +151,7 @@ testScripts = [
|
|||||||
'invalidtxrequest.py', # NOTE: needs dash_hash to pass
|
'invalidtxrequest.py', # NOTE: needs dash_hash to pass
|
||||||
'abandonconflict.py',
|
'abandonconflict.py',
|
||||||
'p2p-versionbits-warning.py',
|
'p2p-versionbits-warning.py',
|
||||||
|
'preciousblock.py',
|
||||||
'importprunedfunds.py',
|
'importprunedfunds.py',
|
||||||
'signmessages.py',
|
'signmessages.py',
|
||||||
]
|
]
|
||||||
|
116
qa/rpc-tests/preciousblock.py
Executable file
116
qa/rpc-tests/preciousblock.py
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test PreciousBlock code
|
||||||
|
#
|
||||||
|
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import *
|
||||||
|
|
||||||
|
def unidirectional_node_sync_via_rpc(node_src, node_dest):
|
||||||
|
blocks_to_copy = []
|
||||||
|
blockhash = node_src.getbestblockhash()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
assert(len(node_dest.getblock(blockhash, False)) > 0)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
blocks_to_copy.append(blockhash)
|
||||||
|
blockhash = node_src.getblockheader(blockhash, True)['previousblockhash']
|
||||||
|
blocks_to_copy.reverse()
|
||||||
|
for blockhash in blocks_to_copy:
|
||||||
|
blockdata = node_src.getblock(blockhash, False)
|
||||||
|
assert(node_dest.submitblock(blockdata) in (None, 'inconclusive'))
|
||||||
|
|
||||||
|
def node_sync_via_rpc(nodes):
|
||||||
|
for node_src in nodes:
|
||||||
|
for node_dest in nodes:
|
||||||
|
if node_src is node_dest:
|
||||||
|
continue
|
||||||
|
unidirectional_node_sync_via_rpc(node_src, node_dest)
|
||||||
|
|
||||||
|
class PreciousTest(BitcoinTestFramework):
|
||||||
|
def setup_chain(self):
|
||||||
|
print("Initializing test directory "+self.options.tmpdir)
|
||||||
|
initialize_chain_clean(self.options.tmpdir, 3)
|
||||||
|
|
||||||
|
def setup_network(self):
|
||||||
|
self.nodes = []
|
||||||
|
self.is_network_split = False
|
||||||
|
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"]))
|
||||||
|
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"]))
|
||||||
|
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"]))
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
print("Ensure submitblock can in principle reorg to a competing chain")
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
assert(self.nodes[0].getblockcount() == 1)
|
||||||
|
(hashY, hashZ) = self.nodes[1].generate(2)
|
||||||
|
assert(self.nodes[1].getblockcount() == 2)
|
||||||
|
node_sync_via_rpc(self.nodes[0:3])
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashZ)
|
||||||
|
|
||||||
|
print("Mine blocks A-B-C on Node 0")
|
||||||
|
(hashA, hashB, hashC) = self.nodes[0].generate(3)
|
||||||
|
assert(self.nodes[0].getblockcount() == 5)
|
||||||
|
print("Mine competing blocks E-F-G on Node 1")
|
||||||
|
(hashE, hashF, hashG) = self.nodes[1].generate(3)
|
||||||
|
assert(self.nodes[1].getblockcount() == 5)
|
||||||
|
assert(hashC != hashG)
|
||||||
|
print("Connect nodes and check no reorg occurs")
|
||||||
|
# Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
|
||||||
|
node_sync_via_rpc(self.nodes[0:2])
|
||||||
|
connect_nodes_bi(self.nodes,0,1)
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashC)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashG)
|
||||||
|
print("Make Node0 prefer block G")
|
||||||
|
self.nodes[0].preciousblock(hashG)
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashG)
|
||||||
|
print("Make Node0 prefer block C again")
|
||||||
|
self.nodes[0].preciousblock(hashC)
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashC)
|
||||||
|
print("Make Node1 prefer block C")
|
||||||
|
self.nodes[1].preciousblock(hashC)
|
||||||
|
sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashC)
|
||||||
|
print("Make Node1 prefer block G again")
|
||||||
|
self.nodes[1].preciousblock(hashG)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashG)
|
||||||
|
print("Make Node0 prefer block G again")
|
||||||
|
self.nodes[0].preciousblock(hashG)
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashG)
|
||||||
|
print("Make Node1 prefer block C again")
|
||||||
|
self.nodes[1].preciousblock(hashC)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashC)
|
||||||
|
print("Mine another block (E-F-G-)H on Node 0 and reorg Node 1")
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
assert(self.nodes[0].getblockcount() == 6)
|
||||||
|
sync_blocks(self.nodes[0:2])
|
||||||
|
hashH = self.nodes[0].getbestblockhash()
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashH)
|
||||||
|
print("Node1 should not be able to prefer block C anymore")
|
||||||
|
self.nodes[1].preciousblock(hashC)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashH)
|
||||||
|
print("Mine competing blocks I-J-K-L on Node 2")
|
||||||
|
self.nodes[2].generate(4)
|
||||||
|
assert(self.nodes[2].getblockcount() == 6)
|
||||||
|
hashL = self.nodes[2].getbestblockhash()
|
||||||
|
print("Connect nodes and check no reorg occurs")
|
||||||
|
node_sync_via_rpc(self.nodes[0:3])
|
||||||
|
connect_nodes_bi(self.nodes,1,2)
|
||||||
|
connect_nodes_bi(self.nodes,0,2)
|
||||||
|
assert(self.nodes[0].getbestblockhash() == hashH)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashH)
|
||||||
|
assert(self.nodes[2].getbestblockhash() == hashL)
|
||||||
|
print("Make Node1 prefer block L")
|
||||||
|
self.nodes[1].preciousblock(hashL)
|
||||||
|
assert(self.nodes[1].getbestblockhash() == hashL)
|
||||||
|
print("Make Node2 prefer block H")
|
||||||
|
self.nodes[2].preciousblock(hashH)
|
||||||
|
assert(self.nodes[2].getbestblockhash() == hashH)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
PreciousTest().main()
|
@ -147,6 +147,16 @@ def sync_blocks(rpc_connections, wait=1, timeout=60):
|
|||||||
maxheight = max(heights)
|
maxheight = max(heights)
|
||||||
raise AssertionError("Block sync failed")
|
raise AssertionError("Block sync failed")
|
||||||
|
|
||||||
|
def sync_chain(rpc_connections, wait=1):
|
||||||
|
"""
|
||||||
|
Wait until everybody has the same best block
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
counts = [ x.getbestblockhash() for x in rpc_connections ]
|
||||||
|
if counts == [ counts[0] ]*len(counts):
|
||||||
|
break
|
||||||
|
time.sleep(wait)
|
||||||
|
|
||||||
def sync_mempools(rpc_connections, wait=1, timeout=60):
|
def sync_mempools(rpc_connections, wait=1, timeout=60):
|
||||||
"""
|
"""
|
||||||
Wait until everybody has the same transactions in their memory
|
Wait until everybody has the same transactions in their memory
|
||||||
|
@ -198,7 +198,7 @@ public:
|
|||||||
unsigned int nNonce;
|
unsigned int nNonce;
|
||||||
|
|
||||||
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
|
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
|
||||||
uint32_t nSequenceId;
|
int32_t nSequenceId;
|
||||||
|
|
||||||
void SetNull()
|
void SetNull()
|
||||||
{
|
{
|
||||||
|
@ -1410,6 +1410,44 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp)
|
|||||||
return mempoolInfoToJSON();
|
return mempoolInfoToJSON();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue preciousblock(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"preciousblock \"hash\"\n"
|
||||||
|
"\nTreats a block as if it were received before others with the same work.\n"
|
||||||
|
"\nA later preciousblock call can override the effect of an earlier one.\n"
|
||||||
|
"\nThe effects of preciousblock are not retained across restarts.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. hash (string, required) the hash of the block to mark as precious\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("preciousblock", "\"blockhash\"")
|
||||||
|
+ HelpExampleRpc("preciousblock", "\"blockhash\"")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string strHash = params[0].get_str();
|
||||||
|
uint256 hash(uint256S(strHash));
|
||||||
|
CBlockIndex* pblockindex;
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
if (mapBlockIndex.count(hash) == 0)
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||||
|
|
||||||
|
pblockindex = mapBlockIndex[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
CValidationState state;
|
||||||
|
PreciousBlock(state, Params(), pblockindex);
|
||||||
|
|
||||||
|
if (!state.IsValid()) {
|
||||||
|
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
return NullUniValue;
|
||||||
|
}
|
||||||
|
|
||||||
UniValue invalidateblock(const UniValue& params, bool fHelp)
|
UniValue invalidateblock(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
@ -1507,6 +1545,8 @@ static const CRPCCommand commands[] =
|
|||||||
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
|
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
|
||||||
{ "blockchain", "verifychain", &verifychain, true },
|
{ "blockchain", "verifychain", &verifychain, true },
|
||||||
|
|
||||||
|
{ "blockchain", "preciousblock", &preciousblock, true },
|
||||||
|
|
||||||
/* Not shown in help */
|
/* Not shown in help */
|
||||||
{ "hidden", "invalidateblock", &invalidateblock, true },
|
{ "hidden", "invalidateblock", &invalidateblock, true },
|
||||||
{ "hidden", "reconsiderblock", &reconsiderblock, true },
|
{ "hidden", "reconsiderblock", &reconsiderblock, true },
|
||||||
|
@ -170,7 +170,11 @@ namespace {
|
|||||||
*/
|
*/
|
||||||
CCriticalSection cs_nBlockSequenceId;
|
CCriticalSection cs_nBlockSequenceId;
|
||||||
/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
|
/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */
|
||||||
uint32_t nBlockSequenceId = 1;
|
int32_t nBlockSequenceId = 1;
|
||||||
|
/** Decreasing counter (used by subsequent preciousblock calls). */
|
||||||
|
int32_t nBlockReverseSequenceId = -1;
|
||||||
|
/** chainwork for the last block that preciousblock has been applied to. */
|
||||||
|
arith_uint256 nLastPreciousChainwork = 0;
|
||||||
|
|
||||||
/** Dirty block index entries. */
|
/** Dirty block index entries. */
|
||||||
set<CBlockIndex*> setDirtyBlockIndex;
|
set<CBlockIndex*> setDirtyBlockIndex;
|
||||||
@ -2924,6 +2928,36 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
if (pindex->nChainWork < chainActive.Tip()->nChainWork) {
|
||||||
|
// Nothing to do, this block is not at the tip.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) {
|
||||||
|
// The chain has been extended since the last call, reset the counter.
|
||||||
|
nBlockReverseSequenceId = -1;
|
||||||
|
}
|
||||||
|
nLastPreciousChainwork = chainActive.Tip()->nChainWork;
|
||||||
|
setBlockIndexCandidates.erase(pindex);
|
||||||
|
pindex->nSequenceId = nBlockReverseSequenceId;
|
||||||
|
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
|
||||||
|
// We can't keep reducing the counter if somebody really wants to
|
||||||
|
// call preciousblock 2**31-1 times on the same set of tips...
|
||||||
|
nBlockReverseSequenceId--;
|
||||||
|
}
|
||||||
|
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
|
||||||
|
setBlockIndexCandidates.insert(pindex);
|
||||||
|
PruneBlockIndexCandidates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActivateBestChain(state, params);
|
||||||
|
}
|
||||||
|
|
||||||
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
|
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
@ -4254,7 +4288,7 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
|
|||||||
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
|
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
|
||||||
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
|
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
|
||||||
}
|
}
|
||||||
if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked
|
if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
||||||
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
||||||
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
||||||
if (!fHavePruned) {
|
if (!fHavePruned) {
|
||||||
|
@ -492,6 +492,9 @@ public:
|
|||||||
/** Find the last common block between the parameter chain and a locator. */
|
/** Find the last common block between the parameter chain and a locator. */
|
||||||
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
|
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
|
||||||
|
|
||||||
|
/** Mark a block as precious and reorganize. */
|
||||||
|
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
|
||||||
|
|
||||||
/** Mark a block as invalid. */
|
/** Mark a block as invalid. */
|
||||||
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
|
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user