mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
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:
parent
c2354fb55f
commit
704c594237
@ -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;
|
||||
}
|
||||
|
||||
void FillBlockPayments(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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%
|
||||
|
@ -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()
|
||||
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]
|
||||
|
Loading…
Reference in New Issue
Block a user