mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge #6430: feat: dynamically register MN in functional tests without IS
37fbdee1d9
feat: do not mine extra quorum in feature_dip3_v19.py (Konstantin Akimov)70ba007f1a
feat: do not mine extra quorum in feature_llmq_evo.py (Konstantin Akimov)129385d349
feat: remove regular masternodes from feature_asset_locks tests (Konstantin Akimov)8ea45bbf69
fix: make helper get_merkle_root works with no masternodes (Konstantin Akimov)7e0c2ca5a5
fix: make dynamic masternode register even without is-quorum (Konstantin Akimov) Pull request description: This PR has important fixes to implement "single-node quorum" feature. ## Issue being fixed or feature implemented Without IS-quorum you can not dynamically add a new MN or Evo node. For evo nodes adding it dynamically is the only way. ## What was done? Fixed a function that dynamically adds a masternode without Instant Send quorums; use it in a functional test feature_asset_locks.py As side effect it improves performance of functional tests which do not wait more IS lock significantly; for feature_asset_locks.py it gave ~20 seconds per run. ## How Has This Been Tested? See updates in `feature_asset_locks.py`, `feature_llmq_evo.py`, `feature_dip3_v19.py` ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK37fbdee1d9
PastaPastaPasta: utACK37fbdee1d9
Tree-SHA512: 2da036ccd9842946f20450606621108fa8cd8fae31927ac2d1fc5f02bf9c2f6955a67e23ef76141a02299510c8cbf206fcd4947b0ec41b776fe80f70947afc14
This commit is contained in:
commit
f6edb8a85c
@ -49,11 +49,11 @@ HEIGHT_DIFF_EXPIRING = 48
|
||||
|
||||
class AssetLocksTest(DashTestFramework):
|
||||
def set_test_params(self):
|
||||
self.set_dash_test_params(4, 2, [[
|
||||
self.set_dash_test_params(2, 0, [[
|
||||
"-whitelist=127.0.0.1",
|
||||
"-llmqtestinstantsenddip0024=llmq_test_instantsend",
|
||||
"-testactivationheight=mn_rr@1400",
|
||||
]] * 4, evo_count=2)
|
||||
]] * 2, evo_count=2)
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
@ -231,15 +231,14 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.log.info(f"Generating batch of blocks {count} left")
|
||||
batch = min(50, count)
|
||||
count -= batch
|
||||
self.bump_mocktime(batch)
|
||||
self.bump_mocktime(10 * 60 + 1)
|
||||
self.generate(self.nodes[1], batch)
|
||||
|
||||
# This functional test intentionally setup only 2 MN and only 2 Evo nodes
|
||||
# to ensure that corner case of quorum with minimum amount of nodes as possible
|
||||
# does not cause any issues in Dash Core
|
||||
def mine_quorum_2_nodes(self, llmq_type_name, llmq_type):
|
||||
self.mine_quorum(llmq_type_name=llmq_type_name, expected_members=2, expected_connections=1, expected_contributions=2, expected_commitments=2, llmq_type=llmq_type)
|
||||
|
||||
def mine_quorum_2_nodes(self):
|
||||
self.mine_quorum(llmq_type_name='llmq_test_platform', expected_members=2, expected_connections=1, expected_contributions=2, expected_commitments=2, llmq_type=106)
|
||||
|
||||
def run_test(self):
|
||||
node_wallet = self.nodes[0]
|
||||
@ -250,17 +249,9 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.activate_v20(expected_activation_height=900)
|
||||
self.log.info("Activated v20 at height:" + str(node.getblockcount()))
|
||||
|
||||
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)
|
||||
self.wait_for_sporks_same()
|
||||
|
||||
self.mine_quorum_2_nodes(llmq_type_name='llmq_test_instantsend', llmq_type=104)
|
||||
|
||||
for _ in range(2):
|
||||
self.dynamically_add_masternode(evo=True)
|
||||
self.generate(node, 8, sync_fun=lambda: self.sync_blocks())
|
||||
|
||||
self.set_sporks()
|
||||
self.generate(node, 1)
|
||||
self.mempool_size = 0
|
||||
|
||||
key = ECKey()
|
||||
@ -328,7 +319,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.create_and_check_block([extra_lock_tx], expected_error = 'bad-cbtx-assetlocked-amount')
|
||||
|
||||
self.log.info("Mine a quorum...")
|
||||
self.mine_quorum_2_nodes(llmq_type_name='llmq_test_platform', llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
|
||||
self.validate_credit_pool_balance(locked_1)
|
||||
|
||||
@ -350,6 +341,10 @@ class AssetLocksTest(DashTestFramework):
|
||||
asset_unlock_tx_duplicate_index.vout[0].nValue += COIN
|
||||
too_late_height = node.getblockcount() + HEIGHT_DIFF_EXPIRING
|
||||
|
||||
self.log.info("Mine block to empty mempool")
|
||||
self.bump_mocktime(10 * 60 + 1)
|
||||
self.generate(self.nodes[0], 1)
|
||||
|
||||
self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
|
||||
self.check_mempool_result(tx=asset_unlock_tx_too_big_fee,
|
||||
result_expected={'allowed': False, 'reject-reason' : 'max-fee-exceeded'})
|
||||
@ -417,7 +412,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
reason = "double copy")
|
||||
|
||||
self.log.info("Mining next quorum to check tx 'asset_unlock_tx_late' is still valid...")
|
||||
self.mine_quorum_2_nodes(llmq_type_name='llmq_test_platform', llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
self.log.info("Checking credit pool amount is same...")
|
||||
self.validate_credit_pool_balance(locked - 1 * COIN)
|
||||
self.check_mempool_result(tx=asset_unlock_tx_late, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
|
||||
@ -435,7 +430,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-too-late'})
|
||||
|
||||
self.log.info("Checking that two quorums later it is too late because quorum is not active...")
|
||||
self.mine_quorum_2_nodes(llmq_type_name='llmq_test_platform', llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
self.log.info("Expecting new reject-reason...")
|
||||
assert not softfork_active(self.nodes[0], 'withdrawals')
|
||||
self.check_mempool_result(tx=asset_unlock_tx_too_late,
|
||||
@ -513,7 +508,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
|
||||
self.log.info("Fast forward to the next day to reset all current unlock limits...")
|
||||
self.generate_batch(blocks_in_one_day)
|
||||
self.mine_quorum_2_nodes(llmq_type_name='llmq_test_platform', llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
|
||||
total = self.get_credit_pool_balance()
|
||||
coins = node_wallet.listunspent()
|
||||
@ -669,12 +664,12 @@ class AssetLocksTest(DashTestFramework):
|
||||
|
||||
while quorumHash_str != node_wallet.quorum('list')['llmq_test_platform'][-1]:
|
||||
self.log.info("Generate one more quorum until signing quorum becomes the last one in the list")
|
||||
self.mine_quorum_2_nodes(llmq_type_name="llmq_test_platform", llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
|
||||
|
||||
self.log.info("Generate one more quorum after which signing quorum is gone but Asset Unlock tx is still valid")
|
||||
assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform']
|
||||
self.mine_quorum_2_nodes(llmq_type_name="llmq_test_platform", llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
assert quorumHash_str not in node_wallet.quorum('list')['llmq_test_platform']
|
||||
|
||||
if asset_unlock_tx_payload.requestedHeight + HEIGHT_DIFF_EXPIRING > node_wallet.getblockcount():
|
||||
@ -686,7 +681,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
index += 1
|
||||
|
||||
self.log.info("Generate one more quorum after which signing quorum becomes too old")
|
||||
self.mine_quorum_2_nodes(llmq_type_name="llmq_test_platform", llmq_type=106)
|
||||
self.mine_quorum_2_nodes()
|
||||
self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': False, 'reject-reason': 'bad-assetunlock-too-old-quorum'})
|
||||
|
||||
asset_unlock_tx = self.create_assetunlock(520, 2000 * COIN + 1, pubkey)
|
||||
|
@ -75,18 +75,8 @@ class DIP3V19Test(DashTestFramework):
|
||||
self.log.info("pubkeyoperator should still be shown using legacy scheme")
|
||||
assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after)
|
||||
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount()))
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount()))
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount()))
|
||||
|
||||
self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103)
|
||||
|
||||
evo_info_0 = self.dynamically_add_masternode(evo=True, rnd=7)
|
||||
assert evo_info_0 is not None
|
||||
self.generate(self.nodes[0], 8, sync_fun=lambda: self.sync_blocks())
|
||||
|
||||
self.log.info("Checking that protxs with duplicate EvoNodes fields are rejected")
|
||||
evo_info_1 = self.dynamically_add_masternode(evo=True, rnd=7, should_be_rejected=True)
|
||||
@ -96,7 +86,6 @@ class DIP3V19Test(DashTestFramework):
|
||||
assert evo_info_2 is None
|
||||
evo_info_3 = self.dynamically_add_masternode(evo=True, rnd=9)
|
||||
assert evo_info_3 is not None
|
||||
self.generate(self.nodes[0], 8, sync_fun=lambda: self.sync_blocks())
|
||||
self.dynamically_evo_update_service(evo_info_0, 9, should_be_rejected=True)
|
||||
|
||||
revoke_protx = self.mninfo[-1].proTxHash
|
||||
@ -123,12 +112,12 @@ class DIP3V19Test(DashTestFramework):
|
||||
def test_revoke_protx(self, node_idx, revoke_protx, revoke_keyoperator):
|
||||
funds_address = self.nodes[0].getnewaddress()
|
||||
fund_txid = self.nodes[0].sendtoaddress(funds_address, 1)
|
||||
self.wait_for_instantlock(fund_txid, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1)[0]
|
||||
assert_equal(self.nodes[0].getrawtransaction(fund_txid, 1, tip)['confirmations'], 1)
|
||||
|
||||
protx_result = self.nodes[0].protx('revoke', revoke_protx, revoke_keyoperator, 1, funds_address)
|
||||
self.wait_for_instantlock(protx_result, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[0]
|
||||
assert_equal(self.nodes[0].getrawtransaction(protx_result, 1, tip)['confirmations'], 1)
|
||||
# Revoking a MN results in disconnects. Wait for disconnects to actually happen
|
||||
|
@ -76,15 +76,6 @@ class LLMQEvoNodesTest(DashTestFramework):
|
||||
self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0)
|
||||
self.wait_for_sporks_same()
|
||||
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount()))
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount()))
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount()))
|
||||
|
||||
self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103)
|
||||
|
||||
evo_protxhash_list = list()
|
||||
for i in range(self.evo_count):
|
||||
evo_info = self.dynamically_add_masternode(evo=True)
|
||||
|
@ -640,6 +640,8 @@ class CBlock(CBlockHeader):
|
||||
# Calculate the merkle root given a vector of transaction hashes
|
||||
@staticmethod
|
||||
def get_merkle_root(hashes):
|
||||
if len(hashes) == 0:
|
||||
return 0
|
||||
while len(hashes) > 1:
|
||||
newhashes = []
|
||||
for i in range(0, len(hashes), 2):
|
||||
|
@ -1313,7 +1313,7 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
collateral_amount = EVONODE_COLLATERAL if evo else MASTERNODE_COLLATERAL
|
||||
outputs = {collateral_address: collateral_amount, funds_address: 1}
|
||||
collateral_txid = self.nodes[0].sendmany("", outputs)
|
||||
self.wait_for_instantlock(collateral_txid, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1)[0]
|
||||
|
||||
rawtx = self.nodes[0].getrawtransaction(collateral_txid, 1, tip)
|
||||
@ -1334,7 +1334,7 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
else:
|
||||
protx_result = self.nodes[0].protx("register", collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True)
|
||||
|
||||
self.wait_for_instantlock(protx_result, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1)[0]
|
||||
|
||||
assert_equal(self.nodes[0].getrawtransaction(protx_result, 1, tip)['confirmations'], 1)
|
||||
@ -1356,14 +1356,14 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
platform_http_port = '%d' % (r + 2)
|
||||
|
||||
fund_txid = self.nodes[0].sendtoaddress(funds_address, 1)
|
||||
self.wait_for_instantlock(fund_txid, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1)[0]
|
||||
assert_equal(self.nodes[0].getrawtransaction(fund_txid, 1, tip)['confirmations'], 1)
|
||||
|
||||
protx_success = False
|
||||
try:
|
||||
protx_result = self.nodes[0].protx('update_service_evo', evo_info.proTxHash, evo_info.addr, evo_info.keyOperator, platform_node_id, platform_p2p_port, platform_http_port, operator_reward_address, funds_address)
|
||||
self.wait_for_instantlock(protx_result, self.nodes[0])
|
||||
self.bump_mocktime(10 * 60 + 1) # to make tx safe to include in block
|
||||
tip = self.generate(self.nodes[0], 1)[0]
|
||||
assert_equal(self.nodes[0].getrawtransaction(protx_result, 1, tip)['confirmations'], 1)
|
||||
self.log.info("Updated EvoNode %s: platformNodeID=%s, platformP2PPort=%s, platformHTTPPort=%s" % (evo_info.proTxHash, platform_node_id, platform_p2p_port, platform_http_port))
|
||||
|
Loading…
Reference in New Issue
Block a user