From a342fc96ccaf8b9714f86178689562bba39c9d2e Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 25 Jan 2023 22:52:07 +0100 Subject: [PATCH] Add OCG voting scenarios --- ...re_on_chain_government_voting_scenarios.py | 168 ++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 169 insertions(+) create mode 100755 test/functional/feature_on_chain_government_voting_scenarios.py 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 00000000000..cf606fb173d --- /dev/null +++ b/test/functional/feature_on_chain_government_voting_scenarios.py @@ -0,0 +1,168 @@ +#!/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 = 30 + +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'], + ] + + def setup_masternodes(self, nMasternodes = 19): + self.nodes[0].mns = [] + + for _ in range(nMasternodes): + address = self.nodes[0].getnewaddress('', 'legacy') + mnId = self.nodes[0].createmasternode(address) + self.nodes[0].generate(1) + self.nodes[0].mns.append([mnId, self.nodes[0].getmasternode(mnId)[mnId]]) + + 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(mnData['operatorAuthAddress']) for [_, mnData] in self.nodes[0].mns]) + + 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, data] = next(mnIterator) + self.nodes[0].generatetoaddress(1, data['operatorAuthAddress']) + self.nodes[0].votegov(propId, mnId, 'yes') + + for _ in range(noVote): + [mnId, data] = next(mnIterator) + self.nodes[0].generatetoaddress(1, data['operatorAuthAddress']) + self.nodes[0].votegov(propId, mnId, 'no') + + for _ in range(neutralVote): + [mnId, data] = next(mnIterator) + self.nodes[0].generatetoaddress(1, data['operatorAuthAddress']) + self.nodes[0].votegov(propId, mnId, 'neutral') + + # Mint with every remaining MNs lest their vote are not counted in quorum + for [mnId, data] in mnIterator: + self.nodes[0].generatetoaddress(1, data['operatorAuthAddress']) + + self.nodes[0].generate(1) + + self.nodes[0].generate(VOTING_PERIOD) + proposal = self.nodes[0].getgovproposal(propId) + + assert_equal(proposal['status'], expectedStatus) + + self.rollback_to(height) + + def test_scenario_below_approval_threshold(self): + self.test_vote_on_cfp(yesVote=8, noVote=6, neutralVote=2, 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 + def test_scenario_at_approval_threshold(self): + self.test_vote_on_cfp(yesVote=8, noVote=6, neutralVote=2, expectedStatus='Rejected') + + def test_scenario_above_approval_threshold(self): + self.test_vote_on_cfp(yesVote=10, noVote=6, neutralVote=2, expectedStatus='Completed') + + def test_scenario_below_quorum(self): + self.test_vote_on_cfp(yesVote=6, noVote=2, neutralVote=1, expectedStatus='Rejected') + + def test_scenario_at_quorum(self): + self.test_vote_on_cfp(yesVote=6, noVote=2, neutralVote=2, expectedStatus='Rejected') + + def test_scenario_above_quorum(self): + self.test_vote_on_cfp(yesVote=6, noVote=3, neutralVote=2, 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 + def test_scenario_high_neutral_vote(self): + self.test_vote_on_cfp(yesVote=8, noVote=3, neutralVote=5, expectedStatus='Rejected') + + def test_scenario_66_6_percent_approval_full_yes_votes(self): + self.test_vote_on_cfp(yesVote=len(self.nodes[0].mns), noVote=0, neutralVote=0, expectedStatus='Completed') + + def test_scenario_66_6_percent_approval_full_no_votes(self): + self.test_vote_on_cfp(yesVote=0, noVote=len(self.nodes[0].mns), neutralVote=0, expectedStatus='Rejected') + + def test_scenario_66_6_percent_approval_full_neutral_votes(self): + self.test_vote_on_cfp(yesVote=0, noVote=0, neutralVote=len(self.nodes[0].mns), expectedStatus='Rejected') + + 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() + self.test_scenario_at_approval_threshold() + self.test_scenario_above_approval_threshold() + + self.nodes[0].setgov({"ATTRIBUTES":{ + 'v0/gov/proposals/quorum':'{}%'.format(QUORUM), + }}) + self.nodes[0].generate(1) + + self.test_scenario_below_quorum() + self.test_scenario_at_quorum() + self.test_scenario_above_quorum() + + self.test_scenario_high_neutral_vote() + + 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() + self.test_scenario_66_6_percent_approval_full_no_votes() + self.test_scenario_66_6_percent_approval_full_neutral_votes() + + 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 2c75c87fa12..af1fdbfaafd 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',