|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Copyright (c) 2023 The PIVX Core developers |
| 3 | +# Distributed under the MIT software license, see the accompanying |
| 4 | +# file COPYING or https://www.opensource.org/licenses/mit-license.php. |
| 5 | + |
| 6 | +from .util import ( |
| 7 | + assert_equal, |
| 8 | + assert_greater_than_or_equal, |
| 9 | + assert_true, |
| 10 | + satoshi_round, |
| 11 | +) |
| 12 | + |
| 13 | + |
| 14 | +class Proposal: |
| 15 | + def __init__(self, name, link, cycles, payment_addr, amount_per_cycle): |
| 16 | + self.name = name |
| 17 | + self.link = link |
| 18 | + self.cycles = cycles |
| 19 | + self.paymentAddr = payment_addr |
| 20 | + self.amountPerCycle = amount_per_cycle |
| 21 | + self.feeTxId = "" |
| 22 | + self.proposalHash = "" |
| 23 | + |
| 24 | + |
| 25 | +def get_proposal_obj(Name, URL, Hash, FeeHash, BlockStart, BlockEnd, |
| 26 | + TotalPaymentCount, RemainingPaymentCount, PaymentAddress, |
| 27 | + Ratio, Yeas, Nays, Abstains, TotalPayment, MonthlyPayment, |
| 28 | + IsEstablished, IsValid, Allotted, TotalBudgetAllotted, IsInvalidReason = ""): |
| 29 | + obj = {} |
| 30 | + obj["Name"] = Name |
| 31 | + obj["URL"] = URL |
| 32 | + obj["Hash"] = Hash |
| 33 | + obj["FeeHash"] = FeeHash |
| 34 | + obj["BlockStart"] = BlockStart |
| 35 | + obj["BlockEnd"] = BlockEnd |
| 36 | + obj["TotalPaymentCount"] = TotalPaymentCount |
| 37 | + obj["RemainingPaymentCount"] = RemainingPaymentCount |
| 38 | + obj["PaymentAddress"] = PaymentAddress |
| 39 | + obj["Ratio"] = Ratio |
| 40 | + obj["Yeas"] = Yeas |
| 41 | + obj["Nays"] = Nays |
| 42 | + obj["Abstains"] = Abstains |
| 43 | + obj["TotalPayment"] = TotalPayment |
| 44 | + obj["MonthlyPayment"] = MonthlyPayment |
| 45 | + obj["IsEstablished"] = IsEstablished |
| 46 | + obj["IsValid"] = IsValid |
| 47 | + if IsInvalidReason != "": |
| 48 | + obj["IsInvalidReason"] = IsInvalidReason |
| 49 | + obj["Allotted"] = Allotted |
| 50 | + obj["TotalBudgetAllotted"] = TotalBudgetAllotted |
| 51 | + return obj |
| 52 | + |
| 53 | + |
| 54 | +def get_proposal(prop, block_start, alloted, total_budget_alloted, positive_votes): |
| 55 | + blockEnd = block_start + prop.cycles * 145 |
| 56 | + total_payment = prop.amountPerCycle * prop.cycles |
| 57 | + return get_proposal_obj(prop.name, prop.link, prop.proposalHash, prop.feeTxId, block_start, |
| 58 | + blockEnd, prop.cycles, prop.cycles, prop.paymentAddr, 1, |
| 59 | + positive_votes, 0, 0, satoshi_round(total_payment), satoshi_round(prop.amountPerCycle), |
| 60 | + True, True, satoshi_round(alloted), satoshi_round(total_budget_alloted)) |
| 61 | + |
| 62 | + |
| 63 | +def check_mns_status_legacy(node, txhash): |
| 64 | + status = node.getmasternodestatus() |
| 65 | + assert_equal(status["txhash"], txhash) |
| 66 | + assert_equal(status["message"], "Masternode successfully started") |
| 67 | + |
| 68 | + |
| 69 | +def check_mns_status(node, txhash): |
| 70 | + status = node.getmasternodestatus() |
| 71 | + assert_equal(status["proTxHash"], txhash) |
| 72 | + assert_equal(status["dmnstate"]["PoSePenalty"], 0) |
| 73 | + assert_equal(status["status"], "Ready") |
| 74 | + |
| 75 | + |
| 76 | +def check_mn_list(node, txHashSet): |
| 77 | + # check masternode list from node |
| 78 | + mnlist = node.listmasternodes() |
| 79 | + assert_equal(len(mnlist), len(txHashSet)) |
| 80 | + foundHashes = set([mn["txhash"] for mn in mnlist if mn["txhash"] in txHashSet]) |
| 81 | + assert_equal(len(foundHashes), len(txHashSet)) |
| 82 | + |
| 83 | + |
| 84 | +def check_budget_finalization_sync(nodes, votesCount, status): |
| 85 | + for i in range(0, len(nodes)): |
| 86 | + node = nodes[i] |
| 87 | + budFin = node.mnfinalbudget("show") |
| 88 | + assert_greater_than_or_equal(len(budFin), 1) |
| 89 | + budget = budFin[next(iter(budFin))] |
| 90 | + assert_equal(budget["VoteCount"], votesCount) |
| 91 | + assert_equal(budget["Status"], status) |
| 92 | + |
| 93 | + |
| 94 | +def check_proposal_existence(nodes, proposalName, proposalHash): |
| 95 | + for node in nodes: |
| 96 | + proposals = node.getbudgetinfo(proposalName) |
| 97 | + assert (len(proposals) > 0) |
| 98 | + assert_equal(proposals[0]["Hash"], proposalHash) |
| 99 | + |
| 100 | + |
| 101 | +def check_vote_existence(nodes, proposalName, mnCollateralHash, voteType, voteValid): |
| 102 | + for i in range(0, len(nodes)): |
| 103 | + node = nodes[i] |
| 104 | + node.syncwithvalidationinterfacequeue() |
| 105 | + votesInfo = node.getbudgetvotes(proposalName) |
| 106 | + assert (len(votesInfo) > 0) |
| 107 | + found = False |
| 108 | + for voteInfo in votesInfo: |
| 109 | + if (voteInfo["mnId"].split("-")[0] == mnCollateralHash): |
| 110 | + assert_equal(voteInfo["Vote"], voteType) |
| 111 | + assert_equal(voteInfo["fValid"], voteValid) |
| 112 | + found = True |
| 113 | + assert_true(found, "Error checking vote existence in node " + str(i)) |
| 114 | + |
| 115 | + |
| 116 | +def check_budgetprojection(nodes, expected, log): |
| 117 | + for i in range(len(nodes)): |
| 118 | + assert_equal(nodes[i].getbudgetprojection(), expected) |
| 119 | + log.info("Budget projection valid for node %d" % i) |
| 120 | + |
| 121 | + |
| 122 | +def create_proposals_tx(miner, props): |
| 123 | + nextSuperBlockHeight = miner.getnextsuperblock() |
| 124 | + for entry in props: |
| 125 | + proposalFeeTxId = miner.preparebudget( |
| 126 | + entry.name, |
| 127 | + entry.link, |
| 128 | + entry.cycles, |
| 129 | + nextSuperBlockHeight, |
| 130 | + entry.paymentAddr, |
| 131 | + entry.amountPerCycle) |
| 132 | + entry.feeTxId = proposalFeeTxId |
| 133 | + return props |
| 134 | + |
| 135 | + |
| 136 | +def propagate_proposals(miner, props): |
| 137 | + nextSuperBlockHeight = miner.getnextsuperblock() |
| 138 | + for entry in props: |
| 139 | + proposalHash = miner.submitbudget( |
| 140 | + entry.name, |
| 141 | + entry.link, |
| 142 | + entry.cycles, |
| 143 | + nextSuperBlockHeight, |
| 144 | + entry.paymentAddr, |
| 145 | + entry.amountPerCycle, |
| 146 | + entry.feeTxId) |
| 147 | + entry.proposalHash = proposalHash |
| 148 | + return props |
0 commit comments