diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1a73873ae1988..16af311d07a88 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -194,7 +194,8 @@ class CMainParams : public CChainParams { consensus.V19Height = 1899072; // 0000000000000015e32e73052d663626327004c81c5c22cb8b42c361015c0eae consensus.V20Height = 1987776; // 000000000000001bf41cff06b76780050682ca29e61a91c391893d4745579777 consensus.MN_RRHeight = 2128896; // 0000000000000009a9696da93d3807eb14eb00a4ff449206d689156a21b27f26 - consensus.MinBIP9WarningHeight = 2128896 + 2016; // mn_rr activation height + miner confirmation window + consensus.WithdrawalsHeight = 2201472; // 00000000000000210518749e17c00b035a2a4982c906236c28c41ea2231bf7ef + consensus.MinBIP9WarningHeight = 2201472 + 2016; // withdrawals activation height + miner confirmation window consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20 consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes @@ -209,15 +210,6 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].bit = 11; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nStartTime = 1728864000; // October 14, 2024 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nTimeout = 1760400000; // October 14, 2025 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 4032; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 3226; // 80% of 4032 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 2420; // 60% of 4032 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].useEHF = true; - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000a39050764808db046f5c"); // 2216986 @@ -392,7 +384,8 @@ class CTestNetParams : public CChainParams { consensus.V19Height = 850100; // 000004728b8ff2a16b9d4eebb0fd61eeffadc9c7fe4b0ec0b5a739869401ab5b consensus.V20Height = 905100; // 0000020c5e0f86f385cbf8e90210de9a9fd63633f01433bf47a6b3227a2851fd consensus.MN_RRHeight = 1066900; // 000000d05d445958a9a4ad6bdc0f4bfb25af124b2326060703373ff2d3b397e9 - consensus.MinBIP9WarningHeight = 1066900 + 2016; // mn_rr activation height + miner confirmation window + consensus.WithdrawalsHeight = 1148500; // 000000212a6fec2ee2af040c6d7a176360b154cbaa998888170cfd9ae7dd632d + consensus.MinBIP9WarningHeight = 1148500 + 2016; // withdrawals activation height + miner confirmation window consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20 consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes @@ -407,15 +400,6 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].bit = 11; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nStartTime = 1728864000; // October 14, 2024 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 80; // 80% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 60; // 60% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].useEHF = true; - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000031f769ba78b4bee"); // 1189000 @@ -565,7 +549,8 @@ class CDevNetParams : public CChainParams { consensus.V19Height = 2; // V19 activated immediately on devnet consensus.V20Height = 2; // V20 activated immediately on devnet consensus.MN_RRHeight = 2; // MN_RR activated immediately on devnet - consensus.MinBIP9WarningHeight = 2 + 2016; // v19 activation height + miner confirmation window + consensus.WithdrawalsHeight = 2; // withdrawals activated immediately on devnet + consensus.MinBIP9WarningHeight = 2 + 2016; // withdrawals activation height + miner confirmation window consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes @@ -580,15 +565,6 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].bit = 11; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nStartTime = 1704067200; // January 1, 2024 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 120; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 80; // 80% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 60; // 60% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].useEHF = true; - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -801,6 +777,7 @@ class CRegTestParams : public CChainParams { consensus.V19Height = 1; // Always active unless overriden consensus.V20Height = 900; consensus.MN_RRHeight = 900; + consensus.WithdrawalsHeight = 1; // Always active unless overriden consensus.MinBIP9WarningHeight = 0; consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day @@ -817,15 +794,6 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].bit = 11; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 200; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 200 / 5 * 4; // 80% of window size - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 200 / 5 * 3; // 60% of window size - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].useEHF = true; - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index 472a3233050e9..3fedf1c69eaff 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -34,12 +34,12 @@ enum BuriedDeployment : int16_t { DEPLOYMENT_V19, DEPLOYMENT_V20, DEPLOYMENT_MN_RR, + DEPLOYMENT_WITHDRAWALS, }; -constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_MN_RR; } +constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_WITHDRAWALS; } enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, - DEPLOYMENT_WITHDRAWALS, // Deployment of Fix for quorum selection for withdrawals // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; @@ -147,6 +147,8 @@ struct Params { int V20Height; /** Block height at which MN_RR (Deployment of Masternode Reward Location Reallocation) becomes active */ int MN_RRHeight; + /** Block height at which WITHDRAWALS (Deployment of quorum fix and higher limits for withdrawals) becomes active */ + int WithdrawalsHeight; /** Don't warn about unknown BIP 9 activations below this height. * This prevents us from warning about the CSV and DIP activations. */ int MinBIP9WarningHeight; @@ -214,6 +216,8 @@ struct Params { return V20Height; case DEPLOYMENT_MN_RR: return MN_RRHeight; + case DEPLOYMENT_WITHDRAWALS: + return WithdrawalsHeight; } // no default case, so the compiler can warn about missing cases return std::numeric_limits::max(); } diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 4cf7e8ef979b0..76e4cc2a0db41 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -11,10 +11,6 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/ "testdummy", /*.gbt_force =*/ true, }, - { - /*.name =*/"withdrawals", - /*.gbt_force =*/true, - }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) @@ -49,6 +45,8 @@ std::string DeploymentName(Consensus::BuriedDeployment dep) return "v20"; case Consensus::DEPLOYMENT_MN_RR: return "mn_rr"; + case Consensus::DEPLOYMENT_WITHDRAWALS: + return "withdrawals"; } // no default case, so the compiler can warn about missing cases return ""; } diff --git a/src/evo/assetlocktx.cpp b/src/evo/assetlocktx.cpp index d8f2c502b6042..be06d06599973 100644 --- a/src/evo/assetlocktx.cpp +++ b/src/evo/assetlocktx.cpp @@ -120,11 +120,8 @@ bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint const auto& llmq_params_opt = Params().GetLLMQ(llmqType); assert(llmq_params_opt.has_value()); - // We check two quorums before DEPLOYMENT_WITHDRAWALS activation - // and "all active quorums + 1 the latest inactive" after activation. - const int quorums_to_scan = DeploymentActiveAt(*pindexTip, Params().GetConsensus(), Consensus::DEPLOYMENT_WITHDRAWALS) - ? (llmq_params_opt->signingActiveQuorumCount + 1) - : 2; + // We check all active quorums + 1 the latest inactive + const int quorums_to_scan = llmq_params_opt->signingActiveQuorumCount + 1; const auto quorums = qman.ScanQuorums(llmqType, pindexTip, quorums_to_scan); if (bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == quorumHash; }); !isActive) { diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index c16950eba1f17..4df54d63df2f2 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -181,18 +181,10 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_null LimitAmountLow) { - currentLimit = std::max(LimitAmountLow, locked / 10) - latelyUnlocked; - if (currentLimit < 0) currentLimit = 0; - } - currentLimit = std::min(currentLimit, LimitAmountHigh - latelyUnlocked); - } + currentLimit = std::min(currentLimit, LimitAmount - latelyUnlocked); + if (currentLimit < 0) currentLimit = 0; if (currentLimit != 0 || latelyUnlocked > 0 || locked > 0) { LogPrint(BCLog::CREDITPOOL, /* Continued */ diff --git a/src/evo/creditpool.h b/src/evo/creditpool.h index c27179edaa953..602a243839011 100644 --- a/src/evo/creditpool.h +++ b/src/evo/creditpool.h @@ -116,9 +116,7 @@ class CCreditPoolManager static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day public: - static constexpr CAmount LimitAmountLow = 100 * COIN; - static constexpr CAmount LimitAmountHigh = 1000 * COIN; - static constexpr CAmount LimitAmountV22 = 2000 * COIN; + static constexpr CAmount LimitAmount = 2000 * COIN; explicit CCreditPoolManager(CEvoDB& _evoDb); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 4509606d4286d..d71590edc739f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1538,12 +1538,12 @@ RPCHelpMan getblockchaininfo() Consensus::DEPLOYMENT_BRR, Consensus::DEPLOYMENT_V19, Consensus::DEPLOYMENT_V20, - Consensus::DEPLOYMENT_MN_RR }) { + Consensus::DEPLOYMENT_MN_RR, + Consensus::DEPLOYMENT_WITHDRAWALS, + }) { SoftForkDescPushBack(&tip, softforks, consensusParams, deploy); } - for (auto ehf_deploy : { /* sorted by activation block */ - Consensus::DEPLOYMENT_WITHDRAWALS, - Consensus::DEPLOYMENT_TESTDUMMY }) { + for (auto ehf_deploy : { Consensus::DEPLOYMENT_TESTDUMMY, }) { SoftForkDescPushBack(&tip, ehfSignals, softforks, consensusParams, ehf_deploy); } obj.pushKV("softforks", softforks); diff --git a/test/functional/feature_asset_locks.py b/test/functional/feature_asset_locks.py index cb2da9885ee67..8e79d91db9731 100755 --- a/test/functional/feature_asset_locks.py +++ b/test/functional/feature_asset_locks.py @@ -48,13 +48,15 @@ tiny_amount = int(Decimal("0.0007") * COIN) blocks_in_one_day = 100 HEIGHT_DIFF_EXPIRING = 48 +WITHDRAWAL_LIMIT = 2000 * COIN class AssetLocksTest(DashTestFramework): def set_test_params(self): self.set_dash_test_params(2, 0, [[ "-whitelist=127.0.0.1", "-llmqtestinstantsenddip0024=llmq_test_instantsend", - "-testactivationheight=mn_rr@1400", + "-testactivationheight=v20@400", + "-testactivationheight=mn_rr@950", ]] * 2, evo_count=2) def skip_test_if_missing_module(self): @@ -102,7 +104,7 @@ def create_assetunlock(self, index, withdrawal, pubkey=None, fee=tiny_amount): request_id = hash256(request_id_buf)[::-1].hex() height = node_wallet.getblockcount() - self.log.info(f"Creating asset unlock: {llmq_type_test} {request_id}") + self.log.info(f"Creating asset unlock: index={index} {request_id}") quorumHash = mninfo[0].node.quorum("selectquorum", llmq_type_test, request_id)["quorumHash"] self.log.info(f"Used quorum hash: {quorumHash}") unlockTx_payload = CAssetUnlockTx( @@ -248,8 +250,9 @@ def run_test(self): self.set_sporks() - self.activate_v20(expected_activation_height=900) + self.activate_v20(expected_activation_height=400) self.log.info("Activated v20 at height:" + str(node.getblockcount())) + assert softfork_active(node_wallet, 'withdrawals') for _ in range(2): self.dynamically_add_masternode(evo=True) @@ -266,7 +269,7 @@ def run_test(self): self.test_asset_unlocks(node_wallet, node, pubkey) self.test_withdrawal_limits(node_wallet, node, pubkey) self.test_mn_rr(node_wallet, node, pubkey) - self.test_withdrawal_fork(node_wallet, node, pubkey) + self.test_old_quorums(node_wallet, node, pubkey) def test_asset_locks(self, node_wallet, node, pubkey): @@ -320,17 +323,16 @@ def test_asset_locks(self, node_wallet, node, pubkey): extra_lock_tx = self.create_assetlock(coin, COIN, pubkey) 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() - + self.generate(node, 1) self.validate_credit_pool_balance(locked_1) - def test_asset_unlocks(self, node_wallet, node, pubkey): self.log.info("Testing asset unlock...") + locked = self.get_credit_pool_balance() + + self.mine_quorum_2_nodes() self.log.info("Generating several txes by same quorum....") - locked = self.get_credit_pool_balance() self.validate_credit_pool_balance(locked) asset_unlock_tx = self.create_assetunlock(101, COIN, pubkey) @@ -352,33 +354,35 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): result_expected={'allowed': False, 'reject-reason' : 'max-fee-exceeded'}) self.check_mempool_result(tx=asset_unlock_tx_zero_fee, result_expected={'allowed': False, 'reject-reason' : 'bad-txns-assetunlock-fee-outofrange'}) - # not-verified is a correct faiure from mempool. Mempool knows nothing about CreditPool indexes and he just report that signature is not validated + # not-verified is a correct error message when adding to mempool because Mempool knows nothing about CreditPool and indexes. + # but the signature is invalid so far as we changed index without re-signing it by quorum self.check_mempool_result(tx=asset_unlock_tx_duplicate_index, result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-not-verified'}) - self.log.info("Validating that we calculate payload hash correctly: ask quorum forcely by message hash...") + self.log.info("Validating payload hash calculation by using hard-coded message hash") asset_unlock_tx_payload = CAssetUnlockTx() asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload)) - assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].node.quorum("selectquorum", llmq_type_test, 'e6c7a809d79f78ea85b72d5df7e9bd592aecf151e679d6e976b74f053a7f9056')["quorumHash"], 16)) - self.log.info("Test no IS for asset unlock...") - self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0) - self.wait_for_sporks_same() txid = self.send_tx(asset_unlock_tx) - assert_equal(node.getmempoolentry(txid)['fees']['base'], Decimal("0.0007")) - is_id = node_wallet.sendtoaddress(node_wallet.getnewaddress(), 1) - for node in self.nodes: - self.wait_for_instantlock(is_id, node) - + self.log.info("Test RPC getassetunlockstatuses part I") tip = self.nodes[0].getblockcount() indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"]) assert_equal([{'index': 101, 'status': 'mempooled'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_no_height) indexes_statuses_height = self.nodes[0].getassetunlockstatuses(["101", "102", "300"], tip) assert_equal([{'index': 101, 'status': 'unknown'}, {'index': 102, 'status': 'unknown'}, {'index': 300, 'status': 'unknown'}], indexes_statuses_height) + self.log.info("Test no IS for asset unlock...") + self.nodes[0].sporkupdate("SPORK_2_INSTANTSEND_ENABLED", 0) + self.wait_for_sporks_same() + + assert_equal(node.getmempoolentry(txid)['fees']['base'], Decimal("0.0007")) + is_id = node_wallet.sendtoaddress(node_wallet.getnewaddress(), 1) + for node in self.nodes: + self.wait_for_instantlock(is_id, node) + rawtx = node.getrawtransaction(txid, 1) rawtx_is = node.getrawtransaction(is_id, 1) @@ -388,7 +392,7 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): assert_equal(rawtx_is["chainlock"], False) assert not "confirmations" in rawtx assert not "confirmations" in rawtx_is - self.log.info("Disable back IS") + self.log.info("Reset IS spork") self.set_sporks() assert "assetUnlockTx" in node.getrawtransaction(txid, 1) @@ -424,35 +428,24 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): self.generate(node, 1) self.validate_credit_pool_balance(locked - 2 * COIN) - self.log.info("Generating many blocks to make quorum far behind (even still active)...") + self.log.info(f"Generate {HEIGHT_DIFF_EXPIRING} blocks to expire tx by height (even quorum is still active)") self.generate_batch(too_late_height - node.getblockcount() - 1) self.check_mempool_result(tx=asset_unlock_tx_too_late, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) self.generate(node, 1) self.check_mempool_result(tx=asset_unlock_tx_too_late, 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() - 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, - result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-too-old-quorum'}) - block_to_reconsider = node.getbestblockhash() self.log.info("Test block invalidation with asset unlock tx...") for inode in self.nodes: inode.invalidateblock(block_asset_unlock) self.validate_credit_pool_balance(locked) - self.generate_batch(50) + self.generate_batch(30) self.validate_credit_pool_balance(locked) for inode in self.nodes: inode.reconsiderblock(block_to_reconsider) self.validate_credit_pool_balance(locked - 2 * COIN) - self.log.info("Forcibly mining asset_unlock_tx_too_late and ensure block is invalid") - assert not softfork_active(self.nodes[0], 'withdrawals') - self.create_and_check_block([asset_unlock_tx_too_late], expected_error = "bad-assetunlock-too-old-quorum") - self.generate(node, 1) self.validate_credit_pool_balance(locked - 2 * COIN) @@ -463,23 +456,22 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): def test_withdrawal_limits(self, node_wallet, node, pubkey): - self.log.info("Testing withdrawal limits before v22 'withdrawal fork'...") - assert not softfork_active(node_wallet, 'withdrawals') + self.log.info("Testing withdrawal limits") self.log.info("Too big withdrawal is expected to not be mined") - asset_unlock_tx_full = self.create_assetunlock(201, 1 + self.get_credit_pool_balance(), pubkey) + asset_unlock_tx_over = self.create_assetunlock(201, 1 + self.get_credit_pool_balance(), pubkey) self.log.info("Checking that transaction with exceeding amount accepted by mempool...") # Mempool doesn't know about the size of the credit pool - self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) + self.check_mempool_result(tx=asset_unlock_tx_over, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) - txid_in_block = self.send_tx(asset_unlock_tx_full) + txid_in_block = self.send_tx(asset_unlock_tx_over) self.generate(node, 1) self.ensure_tx_is_not_mined(txid_in_block) - self.log.info("Forcibly mine asset_unlock_tx_full and ensure block is invalid...") - self.create_and_check_block([asset_unlock_tx_full], expected_error = "failed-creditpool-unlock-too-much") + self.log.info("Forcibly mine too big withdrawal (more than limit) and ensure block is invalid...") + self.create_and_check_block([asset_unlock_tx_over], expected_error = "failed-creditpool-unlock-too-much") self.mempool_size += 1 asset_unlock_tx_full = self.create_assetunlock(301, self.get_credit_pool_balance(), pubkey) @@ -514,7 +506,7 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): total = self.get_credit_pool_balance() coins = node_wallet.listunspent() - while total <= 10_901 * COIN: + while total <= 6_000 * COIN: coin = coins.pop() to_lock = int(coin['amount'] * COIN) - tiny_amount if to_lock > 99 * COIN: @@ -522,17 +514,16 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): total += to_lock tx = self.create_assetlock(coin, to_lock, pubkey) self.send_tx_simple(tx) - self.log.info(f"Collecting coins in pool... Collected {total}/{10_901 * COIN}") + self.log.info(f"Collecting coins in pool... Collected {total}/{6_000 * COIN}") self.sync_mempools() self.generate(node, 1) credit_pool_balance_1 = self.get_credit_pool_balance() - assert_greater_than(credit_pool_balance_1, 10_901 * COIN) - limit_amount_1 = 1000 * COIN + assert_greater_than(credit_pool_balance_1, 6_000 * COIN) self.log.info("Create 5 transactions and make sure that only 4 of them can be mined") - self.log.info("because their sum is bigger than the hard-limit (1000)") + self.log.info("because their sum is bigger than the hard-limit (2000)") # take most of limit by one big tx for faster testing and # create several tiny withdrawal with exactly 1 *invalid* / causes spend above limit tx - withdrawals = [600 * COIN, 100 * COIN, 100 * COIN, 100 * COIN - 10000, 100 * COIN + 10001] + withdrawals = [1600 * COIN, 100 * COIN, 100 * COIN, 100 * COIN - 10000, 100 * COIN + 10001] amount_to_withdraw_1 = sum(withdrawals) index = 400 for next_amount in withdrawals: @@ -547,13 +538,11 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): new_total = self.get_credit_pool_balance() amount_actually_withdrawn = total - new_total - self.log.info("Testing that we tried to withdraw more than we could") - assert_greater_than(amount_to_withdraw_1, amount_actually_withdrawn) - self.log.info("Checking that we tried to withdraw more than the hard-limit (1000)") - assert_greater_than(amount_to_withdraw_1, limit_amount_1) + self.log.info("Checking that we tried to withdraw more than the hard-limit (2000)") + assert_greater_than(amount_to_withdraw_1, WITHDRAWAL_LIMIT) self.log.info("Checking we didn't actually withdraw more than allowed by the limit") - assert_greater_than_or_equal(limit_amount_1, amount_actually_withdrawn) - assert_equal(amount_actually_withdrawn, 900 * COIN + 10001) + assert_greater_than_or_equal(WITHDRAWAL_LIMIT, amount_actually_withdrawn) + assert_equal(amount_actually_withdrawn, 1900 * COIN + 10001) self.generate(node, 1) self.log.info("Checking that exactly 1 tx stayed in mempool...") @@ -562,8 +551,8 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): assert_equal(new_total, self.get_credit_pool_balance()) pending_txid = node.getrawmempool()[0] - amount_to_withdraw_2 = limit_amount_1 - amount_actually_withdrawn - self.log.info(f"We can still consume {Decimal(str(amount_to_withdraw_2 / COIN))} before we hit the hard-limit (1000)") + amount_to_withdraw_2 = WITHDRAWAL_LIMIT - amount_actually_withdrawn + self.log.info(f"We can still consume {Decimal(str(amount_to_withdraw_2 / COIN))} before we hit the hard-limit (2000)") index += 1 asset_unlock_tx = self.create_assetunlock(index, amount_to_withdraw_2, pubkey) self.send_tx_simple(asset_unlock_tx) @@ -571,7 +560,7 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): self.generate(node, 1) new_total = self.get_credit_pool_balance() amount_actually_withdrawn = total - new_total - assert_equal(limit_amount_1, amount_actually_withdrawn) + assert_equal(WITHDRAWAL_LIMIT, amount_actually_withdrawn) self.log.info("Checking that exactly the same tx as before stayed in mempool and it's the only one...") self.mempool_size = 1 @@ -586,22 +575,24 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): self.check_mempool_size() self.log.info("Creating new asset-unlock tx. It should be mined exactly 1 block after") - credit_pool_balance_2 = self.get_credit_pool_balance() - limit_amount_2 = credit_pool_balance_2 // 10 - index += 1 - asset_unlock_tx = self.create_assetunlock(index, limit_amount_2, pubkey) - self.send_tx(asset_unlock_tx) + asset_unlock_tx = self.create_assetunlock(520, WITHDRAWAL_LIMIT + 1, pubkey) + txid_in_block = self.send_tx(asset_unlock_tx) self.generate(node, 1) - assert_equal(new_total, self.get_credit_pool_balance()) + self.ensure_tx_is_not_mined(txid_in_block) + + asset_unlock_tx = self.create_assetunlock(521, WITHDRAWAL_LIMIT, pubkey) + txid_in_block = self.send_tx(asset_unlock_tx) self.generate(node, 1) - new_total -= limit_amount_2 - assert_equal(new_total, self.get_credit_pool_balance()) - self.log.info("Trying to withdraw more... expecting to fail") - index += 1 - asset_unlock_tx = self.create_assetunlock(index, COIN, pubkey) - self.send_tx(asset_unlock_tx) + block = node.getblock(node.getbestblockhash()) + assert txid_in_block in block['tx'] + new_total -= WITHDRAWAL_LIMIT + + asset_unlock_tx = self.create_assetunlock(522, COIN, pubkey) + txid_in_block = self.send_tx(asset_unlock_tx) self.generate(node, 1) + self.ensure_tx_is_not_mined(txid_in_block) + self.log.info("Test RPC getassetunlockstatuses part II") tip = self.nodes[0].getblockcount() indexes_statuses_no_height = self.nodes[0].getassetunlockstatuses(["101", "102", "103"]) assert_equal([{'index': 101, 'status': 'mined'}, {'index': 102, 'status': 'mined'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_no_height) @@ -609,19 +600,16 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): assert_equal([{'index': 101, 'status': 'chainlocked'}, {'index': 102, 'status': 'chainlocked'}, {'index': 103, 'status': 'unknown'}], indexes_statuses_height) - self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...") + self.log.info(f"Generate {HEIGHT_DIFF_EXPIRING} blocks to be sure that expired txes are removed from mempool") self.generate_batch(HEIGHT_DIFF_EXPIRING) - self.log.info("Checking that credit pool is not changed...") assert_equal(new_total, self.get_credit_pool_balance()) self.check_mempool_size() - assert not softfork_active(node_wallet, 'withdrawals') def test_mn_rr(self, node_wallet, node, pubkey): - self.log.info(node_wallet.getblockcount()) self.log.info("Activate mn_rr...") locked = self.get_credit_pool_balance() - self.activate_mn_rr(expected_activation_height=1400) + self.activate_mn_rr(expected_activation_height=950) self.log.info(f'mn-rr height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}') assert_equal(locked, self.get_credit_pool_balance()) @@ -633,7 +621,7 @@ def test_mn_rr(self, node_wallet, node, pubkey): all_mn_rewards = platform_reward + owner_reward + operator_reward assert_equal(all_mn_rewards, bt['coinbasevalue'] * 3 // 4) # 75/25 mn/miner reward split assert_equal(platform_reward, all_mn_rewards * 375 // 1000) # 0.375 platform share - assert_equal(platform_reward, 57741807) + assert_equal(platform_reward, 90147655) assert_equal(locked, self.get_credit_pool_balance()) self.generate(node, 1) locked += platform_reward @@ -646,12 +634,10 @@ def test_mn_rr(self, node_wallet, node, pubkey): self.generate(node, 1) assert_equal(locked, self.get_credit_pool_balance()) - def test_withdrawal_fork(self, node_wallet, node, pubkey): - self.log.info("Testing asset unlock after 'withdrawal' activation...") - assert softfork_active(node_wallet, 'withdrawals') - self.log.info(f'post-withdrawals height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}') - + def test_old_quorums(self, node_wallet, node, pubkey): index = 501 + quorumHash_str = "" + while index < 511: self.log.info(f"Generating new Asset Unlock tx, index={index}...") asset_unlock_tx = self.create_assetunlock(index, COIN, pubkey) @@ -664,43 +650,27 @@ def test_withdrawal_fork(self, node_wallet, node, pubkey): quorumHash_str = format(asset_unlock_tx_payload.quorumHash, '064x') assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] - 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() - self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) + if quorumHash_str != node_wallet.quorum('list')['llmq_test_platform'][-1]: + self.log.info("The quorum for this msg-hash is not last one in the list. Try again!") + index += 1 + else: + break - 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() - assert quorumHash_str not in node_wallet.quorum('list')['llmq_test_platform'] + assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] + self.log.info("Generate one more quorum to make signing quorum inactive but still valid") + 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(): - self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) - break - else: - self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-too-late'}) - self.log.info("Asset Unlock tx expired, let's try again...") - index += 1 + assert asset_unlock_tx_payload.requestedHeight + HEIGHT_DIFF_EXPIRING > node_wallet.getblockcount() + 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 becomes too old") 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) - txid_in_block = self.send_tx(asset_unlock_tx) - self.generate(node, 1) - self.ensure_tx_is_not_mined(txid_in_block) - - asset_unlock_tx = self.create_assetunlock(521, 2000 * COIN, pubkey) - txid_in_block = self.send_tx(asset_unlock_tx) - self.generate(node, 1) - block = node.getblock(node.getbestblockhash()) - assert txid_in_block in block['tx'] + self.log.info("Forcibly mining asset_unlock_tx_too_late and ensure block is invalid") + self.create_and_check_block([asset_unlock_tx], expected_error = "bad-assetunlock-too-old-quorum") - asset_unlock_tx = self.create_assetunlock(522, COIN, pubkey) - txid_in_block = self.send_tx(asset_unlock_tx) - self.generate(node, 1) - self.ensure_tx_is_not_mined(txid_in_block) if __name__ == '__main__': AssetLocksTest().main() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index c28585c61f99a..730371b0c59c8 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -214,17 +214,7 @@ def _test_getblockchaininfo(self): 'v19': { 'type': 'buried', 'active': True, 'height': 15}, 'v20': { 'type': 'buried', 'active': False, 'height': 901}, 'mn_rr': { 'type': 'buried', 'active': False, 'height': 902}, - 'withdrawals': { - 'type': 'bip9', - 'bip9': { - 'status': 'defined', - 'start_time': 0, - 'timeout': 9223372036854775807, # "withdrawals" does not have a timeout so is set to the max int64 value - 'since': 0, - 'min_activation_height': 0, - 'ehf': True - }, - 'active': False}, + 'withdrawals': { 'type': 'buried', 'active': True, 'height': 1}, 'testdummy': { 'type': 'bip9', 'bip9': {