diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 29c491f4ec..c5ff41ba50 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -140,6 +140,7 @@ class CMainParams : public CChainParams { consensus.FortCanningEpilogueHeight = 2257500; // Sep 22nd, 2022. consensus.GrandCentralHeight = 2479000; // Dec 8th, 2022. consensus.GrandCentralEpilogueHeight = 2574000; // Jan 10th, 2023. + consensus.NextNetworkUpgradeHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -396,6 +397,7 @@ class CTestNetParams : public CChainParams { consensus.FortCanningEpilogueHeight = 1244000; consensus.GrandCentralHeight = 1366000; consensus.GrandCentralEpilogueHeight = 1438200; + consensus.NextNetworkUpgradeHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -608,6 +610,7 @@ class CDevNetParams : public CChainParams { consensus.FortCanningEpilogueHeight = 1244000; consensus.GrandCentralHeight = 1366000; consensus.GrandCentralEpilogueHeight = 1438200; + consensus.NextNetworkUpgradeHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 5 * 60; // 5 min == 10 blocks @@ -821,6 +824,7 @@ class CRegTestParams : public CChainParams { consensus.FortCanningEpilogueHeight = 10000000; consensus.GrandCentralHeight = 10000000; consensus.GrandCentralEpilogueHeight = 10000000; + consensus.NextNetworkUpgradeHeight = 10000000; consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -1062,7 +1066,8 @@ void SetupCommonArgActivationParams(Consensus::Params &consensus) { UpdateHeightValidation("Fort Canning Great World", "-greatworldheight", consensus.FortCanningGreatWorldHeight); UpdateHeightValidation("Fort Canning Epilogue", "-fortcanningepilogueheight", consensus.FortCanningEpilogueHeight); UpdateHeightValidation("Grand Central", "-grandcentralheight", consensus.GrandCentralHeight); - UpdateHeightValidation("Grand Central Next", "-grandcentralepilogueheight", consensus.GrandCentralEpilogueHeight); + UpdateHeightValidation("Grand Central Epilogue", "-grandcentralepilogueheight", consensus.GrandCentralEpilogueHeight); + UpdateHeightValidation("Next Network Upgrade", "-nextnetworkupgradeheight", consensus.NextNetworkUpgradeHeight); if (gArgs.GetBoolArg("-simulatemainnet", false)) { consensus.pos.nTargetTimespan = 5 * 60; // 5 min == 10 blocks diff --git a/src/consensus/params.h b/src/consensus/params.h index db52e964f5..bbb82af2b2 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -101,6 +101,7 @@ struct Params { int FortCanningEpilogueHeight; int GrandCentralHeight; int GrandCentralEpilogueHeight; + int NextNetworkUpgradeHeight; /** Foundation share after AMK, normalized to COIN = 100% */ CAmount foundationShareDFIP1; diff --git a/src/init.cpp b/src/init.cpp index f27be70b62..8117c15854 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -505,7 +505,8 @@ void SetupServerArgs() gArgs.AddArg("-greatworldheight", "Alias for Fort Canning Great World fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-fortcanningepilogueheight", "Alias for Fort Canning Epilogue fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-grandcentralheight", "Grand Central fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - gArgs.AddArg("-grandcentralepilogueheight", "Grand Central Next fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-grandcentralepilogueheight", "Grand Central Epilogue fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-nextnetworkupgradeheight", "Next NEtwork Upgrade fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-jellyfish_regtest", "Configure the regtest network for jellyfish testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-regtest-skip-loan-collateral-validation", "Skip loan collateral check for jellyfish testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-regtest-minttoken-simulate-mainnet", "Simulate mainnet for minttokens on regtest - default behavior on regtest is to allow anyone to mint mintable tokens for ease of testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); diff --git a/test/functional/feature_on_chain_government_voting_scenarios.py b/test/functional/feature_on_chain_government_voting_scenarios.py new file mode 100755 index 0000000000..aa09fb6757 --- /dev/null +++ b/test/functional/feature_on_chain_government_voting_scenarios.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) DeFi Blockchain Developers +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. +"""Test OCG voting scenarios""" + +from test_framework.test_framework import DefiTestFramework +from test_framework.util import ( + assert_equal, +) + +APPROVAL_THRESHOLD=50 +QUORUM=50 +VOTING_PERIOD=10 + +class OCGVotingScenarionTest(DefiTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [ + ['-jellyfish_regtest=1', '-dummypos=0', '-txnotokens=0', '-amkheight=50', '-bayfrontheight=51', '-eunosheight=80', '-fortcanningheight=82', '-fortcanninghillheight=84', '-fortcanningroadheight=86', '-fortcanningcrunchheight=88', '-fortcanningspringheight=90', '-fortcanninggreatworldheight=94', '-grandcentralheight=101', '-simulatemainnet=1'], + ] + + def setup_masternodes(self, nMasternodes = 19): + self.nodes[0].mns = [] + operatorAddresses = [] + + for _ in range(nMasternodes): + address = self.nodes[0].getnewaddress('', 'legacy') + self.nodes[0].mns.append(self.nodes[0].createmasternode(address)) + operatorAddresses.append(address) + self.nodes[0].generate(1) + + self.nodes[0].generate(20) # Enables all MNs + self.sync_blocks(timeout=120) + + # restart node with masternode_operator addresses to be able to mint with every MNs + self.restart_node(0, self.nodes[0].extra_args + ['-masternode_operator={}'.format(address) for address in operatorAddresses]) + + # Mint with every MNs to meet voting eligibility criteria + for address in operatorAddresses: + self.nodes[0].generatetoaddress(1, address) + + def setup(self): + # Generate chain + self.nodes[0].generate(100) + self.sync_blocks(timeout=120) + + self.setup_masternodes() + + # activate on-chain governance + self.nodes[0].setgov({"ATTRIBUTES":{ + 'v0/params/feature/gov':'true', + 'v0/gov/proposals/voting_period': '{}'.format(VOTING_PERIOD), + }}) + self.nodes[0].generate(1) + + def test_vote_on_cfp(self, yesVote, noVote, neutralVote, expectedStatus): + height = self.nodes[0].getblockcount() + + # Create address for CFP + address = self.nodes[0].getnewaddress() + context = "" + title = "Create test community fund proposal" + amount = 100 + + # Create CFP + propId = self.nodes[0].creategovcfp({"title": title, "context": context, "amount": amount, "cycles": 1, "payoutAddress": address}) + self.nodes[0].generate(1) + + mnIterator = iter(self.nodes[0].mns) + + for _ in range(yesVote): + mnId = next(mnIterator) + self.nodes[0].votegov(propId, mnId, 'yes') + + for _ in range(noVote): + mnId = next(mnIterator) + self.nodes[0].votegov(propId, mnId, 'no') + + for _ in range(neutralVote): + mnId = next(mnIterator) + self.nodes[0].votegov(propId, mnId, 'neutral') + + self.nodes[0].generate(1) + + self.nodes[0].generate(VOTING_PERIOD * 2) + proposal = self.nodes[0].getgovproposal(propId) + + assert_equal(proposal['status'], expectedStatus) + + self.rollback_to(height) + + def test_scenario_below_approval_threshold(self, expectedStatus): + self.test_vote_on_cfp(yesVote=4, noVote=6, neutralVote=2, expectedStatus=expectedStatus) + + def test_scenario_at_approval_threshold(self, expectedStatus): + self.test_vote_on_cfp(yesVote=8, noVote=8, neutralVote=0, expectedStatus=expectedStatus) + + def test_scenario_above_approval_threshold(self, expectedStatus): + self.test_vote_on_cfp(yesVote=10, noVote=6, neutralVote=2, expectedStatus=expectedStatus) + + def test_scenario_below_quorum(self, expectedStatus): + self.test_vote_on_cfp(yesVote=6, noVote=2, neutralVote=1, expectedStatus=expectedStatus) + + def test_scenario_at_quorum(self, expectedStatus): + self.test_vote_on_cfp(yesVote=6, noVote=2, neutralVote=2, expectedStatus=expectedStatus) + + def test_scenario_above_quorum(self, expectedStatus): + self.test_vote_on_cfp(yesVote=6, noVote=3, neutralVote=2, expectedStatus=expectedStatus) + + def test_scenario_high_neutral_vote(self, expectedStatus): + self.test_vote_on_cfp(yesVote=8, noVote=3, neutralVote=5, expectedStatus=expectedStatus) + + def test_scenario_only_yes_and_neutral(self, expectedStatus): + self.test_vote_on_cfp(yesVote=8, noVote=0, neutralVote=8, expectedStatus=expectedStatus) + + def test_scenario_66_6_percent_approval_full_yes_votes(self, expectedStatus): + self.test_vote_on_cfp(yesVote=len(self.nodes[0].mns), noVote=0, neutralVote=0, expectedStatus=expectedStatus) + + def test_scenario_66_6_percent_approval_full_no_votes(self, expectedStatus): + self.test_vote_on_cfp(yesVote=0, noVote=len(self.nodes[0].mns), neutralVote=0, expectedStatus=expectedStatus) + + def test_scenario_66_6_percent_approval_full_neutral_votes(self, expectedStatus): + self.test_vote_on_cfp(yesVote=0, noVote=0, neutralVote=len(self.nodes[0].mns), expectedStatus=expectedStatus) + + def scenarios_test(self): + self.nodes[0].setgov({"ATTRIBUTES":{ + 'v0/gov/proposals/cfp_approval_threshold':'{}%'.format(APPROVAL_THRESHOLD), + }}) + self.nodes[0].generate(1) + + self.test_scenario_below_approval_threshold(expectedStatus='Rejected') + self.test_scenario_at_approval_threshold(expectedStatus='Rejected') + self.test_scenario_above_approval_threshold(expectedStatus='Completed') + + self.nodes[0].setgov({"ATTRIBUTES":{ + 'v0/gov/proposals/quorum':'{}%'.format(QUORUM), + }}) + self.nodes[0].generate(1) + + self.test_scenario_below_quorum(expectedStatus='Rejected') + self.test_scenario_at_quorum(expectedStatus='Rejected') + self.test_scenario_above_quorum(expectedStatus='Completed') + + # Currently marked as Rejected as neutral votes are incorrectly counted as no + # Should assert that it's Completed once https://github.com/DeFiCh/ain/issues/1704 is fixed + self.test_scenario_high_neutral_vote(expectedStatus='Rejected') + + # Currently marked as Rejected as neutral votes are incorrectly counted as no + # Should assert that it's Completed once https://github.com/DeFiCh/ain/issues/1704 is fixed + self.test_scenario_only_yes_and_neutral(expectedStatus='Rejected') + + self.nodes[0].setgov({"ATTRIBUTES":{ + 'v0/gov/proposals/cfp_approval_threshold':'{}%'.format(66.6), + }}) + self.nodes[0].generate(1) + + self.test_scenario_66_6_percent_approval_full_yes_votes(expectedStatus="Completed") + self.test_scenario_66_6_percent_approval_full_no_votes(expectedStatus="Rejected") + self.test_scenario_66_6_percent_approval_full_neutral_votes(expectedStatus="Rejected") + + def run_test(self): + + self.setup() + + self.scenarios_test() + +if __name__ == '__main__': + OCGVotingScenarionTest().main () diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 2c75c87fa1..af1fdbfaaf 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -303,6 +303,7 @@ 'feature_on_chain_government.py', 'feature_on_chain_government_voting_period_alignment.py', 'feature_on_chain_government_fee_distribution.py', + 'feature_on_chain_government_voting_scenarios.py', 'rpc_listgovproposals.py', 'rpc_help.py', 'feature_help.py',