mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
rpc: Add masternode payments
(#3863)
* rpc: Implement `masternode payments` Returns an array of deterministic masternodes and their payments for a specific block * tests: Add rpc_masternode.py * Apply review suggestions * Add amounts calculated per masternode and per block * Tweak help string * Update src/rpc/masternode.cpp Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com> * rpc: Check against vector size instead of decrementing a counter * rpc: Use `std::vector::begin()` instead of `std::begin(std::vector)` * Drop set_dash_dip8_activation in rpc_masternode.py * Apply suggestions from code review Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: dustinface <35775977+xdustinface@users.noreply.github.com> Co-authored-by: xdustinface <xdustinfacex@gmail.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
This commit is contained in:
parent
0c28bf4ab8
commit
0a8664fd33
@ -368,6 +368,146 @@ UniValue masternode_winners(const JSONRPCRequest& request)
|
||||
|
||||
return obj;
|
||||
}
|
||||
void masternode_payments_help()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"masternode payments ( \"blockhash\" count )\n"
|
||||
"\nReturns an array of deterministic masternodes and their payments for the specified block\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhash\" (string, optional, default=tip) The hash of the starting block\n"
|
||||
"2. count (numeric, optional, default=1) The number of blocks to return.\n"
|
||||
" Will return <count> previous blocks if <count> is negative.\n"
|
||||
" Both 1 and -1 correspond to the chain tip.\n"
|
||||
"\nResult:\n"
|
||||
" [ (array) Blocks\n"
|
||||
" {\n"
|
||||
" \"height\" : n, (numeric) The height of the block\n"
|
||||
" \"blockhash\" : \"hash\", (string) The hash of the block\n"
|
||||
" \"amount\": n (numeric) Amount received in this block by all masternodes\n"
|
||||
" \"masternodes\": [ (array) Masternodes that received payments in this block\n"
|
||||
" {\n"
|
||||
" \"proTxHash\": \"xxxx\", (string) The hash of the corresponding ProRegTx\n"
|
||||
" \"amount\": n (numeric) Amount received by this masternode\n"
|
||||
" \"payees\": [ (array) Payees who received a share of this payment\n"
|
||||
" {\n"
|
||||
" \"address\" : \"xxx\", (string) Payee address\n"
|
||||
" \"script\" : \"xxx\", (string) Payee scriptPubKey\n"
|
||||
" \"amount\": n (numeric) Amount received by this payee\n"
|
||||
" },...\n"
|
||||
" ]\n"
|
||||
" },...\n"
|
||||
" ]\n"
|
||||
" },...\n"
|
||||
" ]\n"
|
||||
);
|
||||
}
|
||||
|
||||
UniValue masternode_payments(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() > 3) {
|
||||
masternode_payments_help();
|
||||
}
|
||||
|
||||
CBlockIndex* pindex{nullptr};
|
||||
|
||||
if (request.params[1].isNull()) {
|
||||
LOCK(cs_main);
|
||||
pindex = chainActive.Tip();
|
||||
} else {
|
||||
LOCK(cs_main);
|
||||
uint256 blockHash = ParseHashV(request.params[1], "blockhash");
|
||||
auto it = mapBlockIndex.find(blockHash);
|
||||
if (it == mapBlockIndex.end()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
}
|
||||
pindex = it->second;
|
||||
}
|
||||
|
||||
int64_t nCount = request.params.size() > 2 ? ParseInt64V(request.params[2], "count") : 1;
|
||||
|
||||
// A temporary vector which is used to sort results properly (there is no "reverse" in/for UniValue)
|
||||
std::vector<UniValue> vecPayments;
|
||||
|
||||
while (vecPayments.size() < std::abs(nCount) != 0 && pindex != nullptr) {
|
||||
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
||||
}
|
||||
|
||||
// Note: we have to actually calculate block reward from scratch instead of simply querying coinbase vout
|
||||
// because miners might collect less coins than they potentially could and this would break our calculations.
|
||||
CAmount nBlockFees{0};
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (tx->IsCoinBase()) {
|
||||
continue;
|
||||
}
|
||||
CAmount nValueIn{0};
|
||||
for (const auto txin : tx->vin) {
|
||||
CTransactionRef txPrev;
|
||||
uint256 blockHashTmp;
|
||||
GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), blockHashTmp);
|
||||
nValueIn += txPrev->vout[txin.prevout.n].nValue;
|
||||
}
|
||||
nBlockFees += nValueIn - tx->GetValueOut();
|
||||
}
|
||||
|
||||
std::vector<CTxOut> voutMasternodePayments, voutDummy;
|
||||
CMutableTransaction dummyTx;
|
||||
CAmount blockReward = nBlockFees + GetBlockSubsidy(pindex->pprev->nBits, pindex->pprev->nHeight, Params().GetConsensus());
|
||||
FillBlockPayments(dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);
|
||||
|
||||
UniValue blockObj(UniValue::VOBJ);
|
||||
CAmount payedPerBlock{0};
|
||||
|
||||
UniValue masternodeArr(UniValue::VARR);
|
||||
UniValue protxObj(UniValue::VOBJ);
|
||||
UniValue payeesArr(UniValue::VARR);
|
||||
CAmount payedPerMasternode{0};
|
||||
|
||||
for (const auto& txout : voutMasternodePayments) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CTxDestination dest;
|
||||
ExtractDestination(txout.scriptPubKey, dest);
|
||||
obj.pushKV("address", EncodeDestination(dest));
|
||||
obj.pushKV("script", HexStr(txout.scriptPubKey));
|
||||
obj.pushKV("amount", txout.nValue);
|
||||
payedPerMasternode += txout.nValue;
|
||||
payeesArr.push_back(obj);
|
||||
}
|
||||
|
||||
const auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee();
|
||||
protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString());
|
||||
protxObj.pushKV("amount", payedPerMasternode);
|
||||
protxObj.pushKV("payees", payeesArr);
|
||||
payedPerBlock += payedPerMasternode;
|
||||
masternodeArr.push_back(protxObj);
|
||||
|
||||
blockObj.pushKV("height", pindex->nHeight);
|
||||
blockObj.pushKV("blockhash", pindex->GetBlockHash().ToString());
|
||||
blockObj.pushKV("amount", payedPerBlock);
|
||||
blockObj.pushKV("masternodes", masternodeArr);
|
||||
vecPayments.push_back(blockObj);
|
||||
|
||||
if (nCount > 0) {
|
||||
LOCK(cs_main);
|
||||
pindex = chainActive.Next(pindex);
|
||||
} else {
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
}
|
||||
|
||||
if (nCount < 0) {
|
||||
std::reverse(vecPayments.begin(), vecPayments.end());
|
||||
}
|
||||
|
||||
UniValue paymentsArr(UniValue::VARR);
|
||||
for (const auto& payment : vecPayments) {
|
||||
paymentsArr.push_back(payment);
|
||||
}
|
||||
|
||||
return paymentsArr;
|
||||
}
|
||||
|
||||
[[ noreturn ]] void masternode_help()
|
||||
{
|
||||
@ -384,6 +524,7 @@ UniValue masternode_winners(const JSONRPCRequest& request)
|
||||
#endif // ENABLE_WALLET
|
||||
" status - Print masternode status information\n"
|
||||
" list - Print list of all known masternodes (see masternodelist for more info)\n"
|
||||
" payments - Return information about masternode payments in a mined block\n"
|
||||
" winner - Print info on next masternode winner to vote for\n"
|
||||
" winners - Print list of masternode winners\n"
|
||||
);
|
||||
@ -416,6 +557,8 @@ UniValue masternode(const JSONRPCRequest& request)
|
||||
#endif // ENABLE_WALLET
|
||||
} else if (strCommand == "status") {
|
||||
return masternode_status(request);
|
||||
} else if (strCommand == "payments") {
|
||||
return masternode_payments(request);
|
||||
} else if (strCommand == "winners") {
|
||||
return masternode_winners(request);
|
||||
} else {
|
||||
|
54
test/functional/rpc_masternode.py
Normal file
54
test/functional/rpc_masternode.py
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020 The Dash Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
from test_framework.test_framework import DashTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
'''
|
||||
rpc_masternode.py
|
||||
|
||||
Test "masternode" rpc subcommands
|
||||
'''
|
||||
|
||||
class RPCMasternodeTest(DashTestFramework):
|
||||
def set_test_params(self):
|
||||
self.set_dash_test_params(4, 3, fast_dip3_enforcement=True)
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("test that results from `winners` and `payments` RPCs match")
|
||||
bi = self.nodes[0].getblockchaininfo()
|
||||
height = bi["blocks"]
|
||||
blockhash = bi["bestblockhash"]
|
||||
winners_payee = self.nodes[0].masternode("winners")[str(height)]
|
||||
payments = self.nodes[0].masternode("payments", blockhash)
|
||||
assert_equal(len(payments), 1)
|
||||
payments_block = payments[0]
|
||||
payments_payee = payments_block["masternodes"][0]["payees"][0]["address"]
|
||||
assert_equal(payments_block["height"], height)
|
||||
assert_equal(payments_block["blockhash"], blockhash)
|
||||
assert_equal(winners_payee, payments_payee)
|
||||
|
||||
self.log.info("test various `payments` RPC options")
|
||||
payments1 = self.nodes[0].masternode("payments", blockhash, -1)
|
||||
assert_equal(payments, payments1)
|
||||
payments2_1 = self.nodes[0].masternode("payments", blockhash, 2)
|
||||
# using chaintip as a start block should return 1 block only
|
||||
assert_equal(len(payments2_1), 1)
|
||||
assert_equal(payments[0], payments2_1[0])
|
||||
payments2_2 = self.nodes[0].masternode("payments", blockhash, -2)
|
||||
# using chaintip as a start block should return 2 blocks now, with the tip being the last one
|
||||
assert_equal(len(payments2_2), 2)
|
||||
assert_equal(payments[0], payments2_2[-1])
|
||||
|
||||
self.log.info("test that `masternode payments` results at chaintip match `getblocktemplate` results for that block")
|
||||
gbt_masternode = self.nodes[0].getblocktemplate()["masternode"]
|
||||
self.nodes[0].generate(1)
|
||||
payments_masternode = self.nodes[0].masternode("payments")[0]["masternodes"][0]
|
||||
assert_equal(gbt_masternode[0]["payee"], payments_masternode["payees"][0]["address"])
|
||||
assert_equal(gbt_masternode[0]["script"], payments_masternode["payees"][0]["script"])
|
||||
assert_equal(gbt_masternode[0]["amount"], payments_masternode["payees"][0]["amount"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RPCMasternodeTest().main()
|
@ -155,6 +155,7 @@ BASE_SCRIPTS= [
|
||||
'p2p_unrequested_blocks.py', # NOTE: needs dash_hash to pass
|
||||
'feature_shutdown.py',
|
||||
'rpc_privatesend.py',
|
||||
'rpc_masternode.py',
|
||||
'p2p_fingerprint.py',
|
||||
'rpc_platform_filter.py',
|
||||
'feature_dip0020_activation.py',
|
||||
|
Loading…
Reference in New Issue
Block a user