fix: some fixes for block payee validation and corresponding tests (#5684)

## Issue being fixed or feature implemented
1. we _should not_ skip masternode payments checks below
nSuperblockStartBlock or when governance is disabled
2. we _should_ skip superblock payee checks while we aren't synced yet
(should help recovering from missed triggers)

## What was done?
pls see individual commits. 

## How Has This Been Tested?
run tests, sync w/ and w/out `--disablegovernance`, reindexed on testnet

## Breaking Changes
n/a

## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] 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)_
This commit is contained in:
UdjinM6 2023-11-13 19:02:52 +03:00 committed by GitHub
parent c2354fb55f
commit 704c594237
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 207 deletions

View File

@ -261,35 +261,40 @@ bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& go
return true;
}
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CTransaction& txNew, const CBlockIndex* const pindexPrev, const CAmount blockSubsidy, const CAmount feeReward)
{
if(fDisableGovernance) {
//there is no budget data to use to check anything, let's just accept the longest chain
LogPrint(BCLog::MNPAYMENTS, "%s -- WARNING: Not enough data, skipping block payee checks\n", __func__);
return true;
const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Check for correct masternode payment
if (IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward)) {
LogPrint(BCLog::MNPAYMENTS, "%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
} else {
LogPrintf("%s -- ERROR: Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
return false;
}
const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// we are still using budgets, but we have no data about them anymore,
// we can only check masternode payments
if (!mn_sync.IsSynced() || fDisableGovernance) {
// governance data is either incomplete or non-existent
LogPrint(BCLog::MNPAYMENTS, "%s -- WARNING: Not enough data, skipping superblock payee checks\n", __func__);
return true; // not an error
}
const Consensus::Params& consensusParams = Params().GetConsensus();
if(nBlockHeight < consensusParams.nSuperblockStartBlock) {
if (nBlockHeight < Params().GetConsensus().nSuperblockStartBlock) {
// We are still using budgets, but we have no data about them anymore,
// we can only check masternode payments.
// NOTE: old budget system is disabled since 12.1 and we should never enter this branch
// anymore when sync is finished (on mainnet). We have no old budget data but these blocks
// have tons of confirmations and can be safely accepted without payee verification
LogPrint(BCLog::GOBJECT, "%s -- WARNING: Client synced but old budget system is disabled, accepting any payee\n", __func__);
return true;
return true; // not an error
}
// superblocks started
// SEE IF THIS IS A VALID SUPERBLOCK
if(AreSuperblocksEnabled(sporkManager)) {
if(CSuperblockManager::IsSuperblockTriggered(governanceManager, nBlockHeight)) {
if(CSuperblockManager::IsValid(governanceManager, txNew, nBlockHeight, blockSubsidy + feeReward)) {
if (AreSuperblocksEnabled(sporkManager)) {
if (CSuperblockManager::IsSuperblockTriggered(governanceManager, nBlockHeight)) {
if (CSuperblockManager::IsValid(governanceManager, txNew, nBlockHeight, blockSubsidy + feeReward)) {
LogPrint(BCLog::GOBJECT, "%s -- Valid superblock at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
// continue validation, should also pay MN
} else {
@ -305,14 +310,7 @@ bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& go
LogPrint(BCLog::GOBJECT, "%s -- Superblocks are disabled, no superblocks allowed\n", __func__);
}
// Check for correct masternode payment
if(IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward)) {
LogPrint(BCLog::MNPAYMENTS, "%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
return true;
}
LogPrintf("%s -- ERROR: Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
return false;
return true;
}
void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,

View File

@ -28,7 +28,7 @@ namespace MasternodePayments
{
bool IsBlockValueValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet);
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
bool IsBlockPayeeValid(const CSporkManager& sporkManager, CGovernanceManager& governanceManager, const CMasternodeSync& mn_sync,
const CTransaction& txNew, const CBlockIndex* const pindexPrev, const CAmount blockSubsidy, const CAmount feeReward);
void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
CMutableTransaction& txNew, const CBlockIndex* const pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,

View File

@ -2416,7 +2416,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5_4 = GetTimeMicros(); nTimeValueValid += nTime5_4 - nTime5_3;
LogPrint(BCLog::BENCHMARK, " - IsBlockValueValid: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5_4 - nTime5_3), nTimeValueValid * MICRO, nTimeValueValid * MILLI / nBlocksTotal);
if (!MasternodePayments::IsBlockPayeeValid(*sporkManager, *governance, *block.vtx[0], pindex->pprev, blockSubsidy, feeReward)) {
if (!MasternodePayments::IsBlockPayeeValid(*sporkManager, *governance, *::masternodeSync, *block.vtx[0], pindex->pprev, blockSubsidy, feeReward)) {
// NOTE: Do not punish, the node might be missing governance data
LogPrintf("ERROR: ConnectBlock(DASH): couldn't find masternode or superblock payments\n");
return state.Invalid(BlockValidationResult::BLOCK_RESULT_UNSET, "bad-cb-payee");

View File

@ -9,8 +9,8 @@
from decimal import Decimal
from test_framework.blocktools import create_block, create_coinbase, get_masternode_payment
from test_framework.messages import CCbTx, COIN, CTransaction, FromHex, ToHex, uint256_to_string
from test_framework.blocktools import create_block_with_mnpayments
from test_framework.messages import CTransaction, FromHex, ToHex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, force_finish_mnsync, p2p_port
@ -131,7 +131,7 @@ class DIP3Test(BitcoinTestFramework):
self.assert_mnlist(self.nodes[0], mns_tmp)
self.log.info("cause a reorg with a double spend and check that mnlists are still correct on all nodes")
self.mine_double_spend(self.nodes[0], dummy_txins, self.nodes[0].getnewaddress(), use_mnmerkleroot_from_tip=True)
self.mine_double_spend(mns, self.nodes[0], dummy_txins, self.nodes[0].getnewaddress())
self.nodes[0].generate(spend_mns_count)
self.sync_all()
self.assert_mnlists(mns_tmp)
@ -139,7 +139,7 @@ class DIP3Test(BitcoinTestFramework):
self.log.info("test mn payment enforcement with deterministic MNs")
for i in range(20):
node = self.nodes[i % len(self.nodes)]
self.test_invalid_mn_payment(node)
self.test_invalid_mn_payment(mns, node)
self.nodes[0].generate(1)
self.sync_all()
@ -218,6 +218,7 @@ class DIP3Test(BitcoinTestFramework):
mn.idx = idx
mn.alias = alias
mn.p2p_port = p2p_port(mn.idx)
mn.operator_reward = (mn.idx % self.num_initial_mn)
blsKey = node.bls('generate')
mn.fundsAddr = node.getnewaddress()
@ -247,7 +248,7 @@ class DIP3Test(BitcoinTestFramework):
mn.collateral_address = node.getnewaddress()
mn.rewards_address = node.getnewaddress()
mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr)
mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
mn.collateral_txid = mn.protx_hash
mn.collateral_vout = None
@ -263,7 +264,7 @@ class DIP3Test(BitcoinTestFramework):
node.sendtoaddress(mn.fundsAddr, 0.001)
mn.rewards_address = node.getnewaddress()
mn.protx_hash = node.protx('register', mn.collateral_txid, mn.collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr)
mn.protx_hash = node.protx('register', mn.collateral_txid, mn.collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, mn.operator_reward, mn.rewards_address, mn.fundsAddr)
node.generate(1)
def start_mn(self, mn):
@ -353,93 +354,15 @@ class DIP3Test(BitcoinTestFramework):
return dummy_txin
def mine_block(self, node, vtx=None, miner_address=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None):
if vtx is None:
vtx = []
bt = node.getblocktemplate()
height = bt['height']
tip_hash = bt['previousblockhash']
tip_block = node.getblock(tip_hash)
coinbasevalue = bt['coinbasevalue']
if miner_address is None:
miner_address = self.nodes[0].getnewaddress()
if mn_payee is None:
if isinstance(bt['masternode'], list):
mn_payee = bt['masternode'][0]['payee']
else:
mn_payee = bt['masternode']['payee']
# we can't take the masternode payee amount from the template here as we might have additional fees in vtx
# calculate fees that the block template included (we'll have to remove it from the coinbase as we won't
# include the template's transactions
bt_fees = 0
for tx in bt['transactions']:
bt_fees += tx['fee']
new_fees = 0
for tx in vtx:
in_value = 0
out_value = 0
for txin in tx.vin:
txout = node.gettxout(uint256_to_string(txin.prevout.hash), txin.prevout.n, False)
in_value += int(txout['value'] * COIN)
for txout in tx.vout:
out_value += txout.nValue
new_fees += in_value - out_value
# fix fees
coinbasevalue -= bt_fees
coinbasevalue += new_fees
if mn_amount is None:
realloc_info = node.getblockchaininfo()['softforks']['realloc']
realloc_height = 99999999
if realloc_info['active']:
realloc_height = realloc_info['height']
mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height)
miner_amount = coinbasevalue - mn_amount
outputs = {miner_address: str(Decimal(miner_amount) / COIN)}
if mn_amount > 0:
outputs[mn_payee] = str(Decimal(mn_amount) / COIN)
coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs))
coinbase.vin = create_coinbase(height).vin
# We can't really use this one as it would result in invalid merkle roots for masternode lists
if len(bt['coinbase_payload']) != 0:
cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload'])
if use_mnmerkleroot_from_tip:
if 'cbTx' in tip_block:
cbtx.merkleRootMNList = int(tip_block['cbTx']['merkleRootMNList'], 16)
else:
cbtx.merkleRootMNList = 0
coinbase.nVersion = 3
coinbase.nType = 5 # CbTx
coinbase.vExtraPayload = cbtx.serialize()
coinbase.calc_sha256()
block = create_block(int(tip_hash, 16), coinbase)
block.vtx += vtx
# Add quorum commitments from template
for tx in bt['transactions']:
tx2 = FromHex(CTransaction(), tx['data'])
if tx2.nType == 6:
block.vtx.append(tx2)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
def mine_block(self, mns, node, vtx=None, mn_payee=None, mn_amount=None, expected_error=None):
block = create_block_with_mnpayments(mns, node, vtx, mn_payee, mn_amount)
result = node.submitblock(ToHex(block))
if expected_error is not None and result != expected_error:
raise AssertionError('mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result))
elif expected_error is None and result is not None:
raise AssertionError('submitblock returned %s' % (result))
def mine_double_spend(self, node, txins, target_address, use_mnmerkleroot_from_tip=False):
def mine_double_spend(self, mns, node, txins, target_address):
amount = Decimal(0)
for txin in txins:
txout = node.gettxout(txin['txid'], txin['vout'], False)
@ -450,12 +373,12 @@ class DIP3Test(BitcoinTestFramework):
rawtx = node.signrawtransactionwithwallet(rawtx)['hex']
tx = FromHex(CTransaction(), rawtx)
self.mine_block(node, [tx], use_mnmerkleroot_from_tip=use_mnmerkleroot_from_tip)
self.mine_block(mns, node, [tx])
def test_invalid_mn_payment(self, node):
def test_invalid_mn_payment(self, mns, node):
mn_payee = self.nodes[0].getnewaddress()
self.mine_block(node, mn_payee=mn_payee, expected_error='bad-cb-payee')
self.mine_block(node, mn_amount=1, expected_error='bad-cb-payee')
self.mine_block(mns, node, mn_payee=mn_payee, expected_error='bad-cb-payee')
self.mine_block(mns, node, mn_amount=1, expected_error='bad-cb-payee')
if __name__ == '__main__':
DIP3Test().main()

View File

@ -10,11 +10,10 @@ Checks conflict handling between ChainLocks and InstantSend
'''
from decimal import Decimal
import struct
from test_framework.blocktools import get_masternode_payment, create_coinbase, create_block
from test_framework.messages import CCbTx, CInv, COIN, CTransaction, FromHex, hash256, msg_clsig, msg_inv, ser_string, ToHex, uint256_from_str, uint256_to_string
from test_framework.blocktools import create_block_with_mnpayments
from test_framework.messages import CInv, CTransaction, FromHex, hash256, msg_clsig, msg_inv, ser_string, ToHex, uint256_from_str
from test_framework.mininode import P2PInterface
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes, wait_until
@ -112,7 +111,7 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework):
self.wait_for_instantlock(rawtx1_txid, node)
self.wait_for_instantlock(rawtx4_txid, node)
block = self.create_block(self.nodes[0], [rawtx2_obj])
block = create_block_with_mnpayments(self.mninfo, self.nodes[0], [rawtx2_obj])
if test_block_conflict:
# The block shouldn't be accepted/connected but it should be known to node 0 now
submit_result = self.nodes[0].submitblock(ToHex(block))
@ -234,7 +233,7 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework):
assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[0].sendrawtransaction, rawtx2)
# Create the block and the corresponding clsig but do not relay clsig yet
cl_block = self.create_block(self.nodes[0])
cl_block = create_block_with_mnpayments(self.mninfo, self.nodes[0])
cl = self.create_chainlock(self.nodes[0].getblockcount() + 1, cl_block)
self.nodes[0].submitblock(ToHex(cl_block))
self.sync_all()
@ -275,74 +274,6 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework):
# Previous tip should be marked as conflicting now
assert_equal(node.getchaintips(2)[1]["status"], "conflicting")
def create_block(self, node, vtx=None):
if vtx is None:
vtx = []
bt = node.getblocktemplate()
height = bt['height']
tip_hash = bt['previousblockhash']
coinbasevalue = bt['coinbasevalue']
miner_address = node.getnewaddress()
mn_payee = bt['masternode'][0]['payee']
# calculate fees that the block template included (we'll have to remove it from the coinbase as we won't
# include the template's transactions
bt_fees = 0
for tx in bt['transactions']:
bt_fees += tx['fee']
new_fees = 0
for tx in vtx:
in_value = 0
out_value = 0
for txin in tx.vin:
txout = node.gettxout(uint256_to_string(txin.prevout.hash), txin.prevout.n, False)
in_value += int(txout['value'] * COIN)
for txout in tx.vout:
out_value += txout.nValue
new_fees += in_value - out_value
# fix fees
coinbasevalue -= bt_fees
coinbasevalue += new_fees
realloc_info = self.nodes[0].getblockchaininfo()['softforks']['realloc']
realloc_height = 99999999
if realloc_info['active']:
realloc_height = realloc_info['height']
mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height)
miner_amount = coinbasevalue - mn_amount
outputs = {miner_address: str(Decimal(miner_amount) / COIN)}
if mn_amount > 0:
outputs[mn_payee] = str(Decimal(mn_amount) / COIN)
coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs))
coinbase.vin = create_coinbase(height).vin
# We can't really use this one as it would result in invalid merkle roots for masternode lists
if len(bt['coinbase_payload']) != 0:
cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload'])
coinbase.nVersion = 3
coinbase.nType = 5 # CbTx
coinbase.vExtraPayload = cbtx.serialize()
coinbase.calc_sha256()
block = create_block(int(tip_hash, 16), coinbase, ntime=bt['curtime'], version=bt['version'])
block.vtx += vtx
# Add quorum commitments from template
for tx in bt['transactions']:
tx2 = FromHex(CTransaction(), tx['data'])
if tx2.nType == 6:
block.vtx.append(tx2)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
return block
def create_chainlock(self, height, block):
request_id_buf = ser_string(b"clsig") + struct.pack("<I", height)
request_id = hash256(request_id_buf)[::-1].hex()

View File

@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
from decimal import Decimal
import unittest
from .messages import (
@ -14,6 +15,8 @@ from .messages import (
CTransaction,
CTxIn,
CTxOut,
FromHex,
uint256_to_string,
)
from .script import CScript, CScriptNum, CScriptOp, OP_TRUE, OP_CHECKSIG
from .util import assert_equal, hex_str_to_bytes
@ -43,6 +46,97 @@ def create_block(hashprev, coinbase, ntime=None, *, version=1):
block.calc_sha256()
return block
def create_block_with_mnpayments(mninfo, node, vtx=None, mn_payee=None, mn_amount=None):
if vtx is None:
vtx = []
bt = node.getblocktemplate()
height = bt['height']
tip_hash = bt['previousblockhash']
coinbasevalue = bt['coinbasevalue']
assert len(bt['masternode']) <= 2
if mn_payee is None:
mn_payee = bt['masternode'][0]['payee']
mn_operator_payee = None
if len(bt['masternode']) == 2:
mn_operator_payee = bt['masternode'][1]['payee']
# we can't take the masternode payee amount from the template here as we might have additional fees in vtx
# calculate fees that the block template included (we'll have to remove it from the coinbase as we won't
# include the template's transactions
bt_fees = 0
for tx in bt['transactions']:
bt_fees += tx['fee']
new_fees = 0
for tx in vtx:
in_value = 0
out_value = 0
for txin in tx.vin:
txout = node.gettxout(uint256_to_string(txin.prevout.hash), txin.prevout.n, False)
in_value += int(txout['value'] * COIN)
for txout in tx.vout:
out_value += txout.nValue
new_fees += in_value - out_value
# fix fees
coinbasevalue -= bt_fees
coinbasevalue += new_fees
operator_reward = 0
if mn_operator_payee is not None:
for mn in mninfo:
if mn.rewards_address == mn_payee:
operator_reward = mn.operator_reward
break
assert operator_reward > 0
mn_operator_amount = 0
if mn_amount is None:
v20_info = node.getblockchaininfo()['softforks']['v20']
mn_amount_total = get_masternode_payment(height, coinbasevalue, v20_info['active'])
mn_operator_amount = mn_amount_total * operator_reward // 100
mn_amount = mn_amount_total - mn_operator_amount
miner_amount = coinbasevalue - mn_amount - mn_operator_amount
miner_address = node.get_deterministic_priv_key().address
outputs = {miner_address: str(Decimal(miner_amount) / COIN)}
if mn_amount > 0:
outputs[mn_payee] = str(Decimal(mn_amount) / COIN)
if mn_operator_amount > 0:
outputs[mn_operator_payee] = str(Decimal(mn_operator_amount) / COIN)
coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs))
coinbase.vin = create_coinbase(height).vin
# We can't really use this one as it would result in invalid merkle roots for masternode lists
if len(bt['coinbase_payload']) != 0:
tip_block = node.getblock(tip_hash)
cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload'])
if 'cbTx' in tip_block:
cbtx.merkleRootMNList = int(tip_block['cbTx']['merkleRootMNList'], 16)
else:
cbtx.merkleRootMNList = 0
coinbase.nVersion = 3
coinbase.nType = 5 # CbTx
coinbase.vExtraPayload = cbtx.serialize()
coinbase.calc_sha256()
block = create_block(int(tip_hash, 16), coinbase, ntime=bt['curtime'], version=bt['version'])
block.vtx += vtx
# Add quorum commitments from template
for tx in bt['transactions']:
tx2 = FromHex(CTransaction(), tx['data'])
if tx2.nType == 6:
block.vtx.append(tx2)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
return block
def script_BIP34_coinbase_height(height):
if height <= 16:
res = CScriptOp.encode_op_n(height)
@ -128,11 +222,12 @@ def get_legacy_sigopcount_tx(tx, accurate=True):
return count
# Identical to GetMasternodePayment in C++ code
def get_masternode_payment(nHeight, blockValue, nReallocActivationHeight):
def get_masternode_payment(nHeight, blockValue, fV20Active):
ret = int(blockValue / 5)
nMNPIBlock = 350
nMNPIPeriod = 10
nReallocActivationHeight = 2500
if nHeight > nMNPIBlock:
ret += int(blockValue / 20)
@ -165,6 +260,12 @@ def get_masternode_payment(nHeight, blockValue, nReallocActivationHeight):
# Activated but we have to wait for the next cycle to start realocation, nothing to do
return ret
if fV20Active:
# Once MNRewardReallocated activates, block reward is 80% of block subsidy (+ tx fees) since treasury is 20%
# Since the MN reward needs to be equal to 60% of the block subsidy (according to the proposal), MN reward is set to 75% of the block reward.
# Previous reallocation periods are dropped.
return blockValue * 3 // 4
# Periods used to reallocate the masternode reward from 50% to 60%
vecPeriods = [
513, # Period 1: 51.3%

View File

@ -999,10 +999,12 @@ MASTERNODE_COLLATERAL = 1000
EVONODE_COLLATERAL = 4000
class MasternodeInfo:
def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, addr, evo=False):
def __init__(self, proTxHash, ownerAddr, votingAddr, rewards_address, operator_reward, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, addr, evo=False):
self.proTxHash = proTxHash
self.ownerAddr = ownerAddr
self.votingAddr = votingAddr
self.rewards_address = rewards_address
self.operator_reward = operator_reward
self.pubKeyOperator = pubKeyOperator
self.keyOperator = keyOperator
self.collateral_address = collateral_address
@ -1234,7 +1236,7 @@ class DashTestFramework(BitcoinTestFramework):
self.sync_all(self.nodes)
assert_equal(self.nodes[0].getrawtransaction(protx_result, 1, tip)['confirmations'], 1)
mn_info = MasternodeInfo(protx_result, owner_address, voting_address, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, ipAndPort, evo)
mn_info = MasternodeInfo(protx_result, owner_address, voting_address, reward_address, operatorReward, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, ipAndPort, evo)
self.mninfo.append(mn_info)
mn_type_str = "EvoNode" if evo else "MN"
@ -1273,20 +1275,18 @@ class DashTestFramework(BitcoinTestFramework):
def prepare_masternodes(self):
self.log.info("Preparing %d masternodes" % self.mn_count)
rewardsAddr = self.nodes[0].getnewaddress()
for idx in range(0, self.mn_count):
self.prepare_masternode(idx, rewardsAddr, False)
self.prepare_masternode(idx)
self.sync_all()
def prepare_masternode(self, idx, rewardsAddr=None, evo=False):
def prepare_masternode(self, idx):
register_fund = (idx % 2) == 0
bls = self.nodes[0].bls('generate')
address = self.nodes[0].getnewaddress()
collateral_amount = EVONODE_COLLATERAL if evo else MASTERNODE_COLLATERAL
collateral_amount = MASTERNODE_COLLATERAL
txid = None
txid = self.nodes[0].sendtoaddress(address, collateral_amount)
collateral_vout = 0
@ -1302,11 +1302,8 @@ class DashTestFramework(BitcoinTestFramework):
self.nodes[0].sendtoaddress(address, 0.001)
ownerAddr = self.nodes[0].getnewaddress()
# votingAddr = self.nodes[0].getnewaddress()
if rewardsAddr is None:
rewardsAddr = self.nodes[0].getnewaddress()
rewardsAddr = self.nodes[0].getnewaddress()
votingAddr = ownerAddr
# rewardsAddr = ownerAddr
port = p2p_port(len(self.nodes) + idx)
ipAndPort = '127.0.0.1:%d' % port
@ -1315,7 +1312,6 @@ class DashTestFramework(BitcoinTestFramework):
submit = (idx % 4) < 2
if register_fund:
# self.nodes[0].lockunspent(True, [{'txid': txid, 'vout': collateral_vout}])
protx_result = self.nodes[0].protx('register_fund', address, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit)
else:
self.nodes[0].generate(1)
@ -1331,11 +1327,9 @@ class DashTestFramework(BitcoinTestFramework):
operatorPayoutAddress = self.nodes[0].getnewaddress()
self.nodes[0].protx('update_service', proTxHash, ipAndPort, bls['secret'], operatorPayoutAddress, address)
self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, ipAndPort, evo))
# self.sync_all()
self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, rewardsAddr, operatorReward, bls['public'], bls['secret'], address, txid, collateral_vout, ipAndPort, False))
mn_type_str = "EvoNode" if evo else "MN"
self.log.info("Prepared %s %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (mn_type_str, idx, txid, collateral_vout, proTxHash))
self.log.info("Prepared MN %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (idx, txid, collateral_vout, proTxHash))
def remove_masternode(self, idx):
mn = self.mninfo[idx]