feat(rpc): protx listdiff rpc (#5338)

## Issue being fixed or feature implemented

## What was done?
Feature requested by @QuantumExplorer and @iammadab.
This PR introduces `protx listdiff`: a more rich alternative of `protx
diff` RPC.

Currently, `protx diff` is returning data only required from SPV for SML
Coinbase MerkleMNListRoot calculation.

Platform team needed a similar RPC returning all the MNs data in order
to calculate the identities.


## How Has This Been Tested?

## Breaking Changes

## Checklist:
_Go over all the following points, and put an `x` in all the boxes that
apply._
- [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
- [x] 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:
Odysseas Gabrielides 2023-04-19 17:47:49 +03:00 committed by GitHub
parent c5dee4107f
commit 6499917a83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 2 deletions

View File

@ -0,0 +1,4 @@
Added RPCs
--------
- `protx listdiff` RPC returns a full deterministic masternode list diff between two heigts.

View File

@ -61,3 +61,65 @@ void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const
obj.pushKV("operatorPayoutAddress", EncodeDestination(dest));
}
}
void CDeterministicMNStateDiff::ToJson(UniValue& obj, MnType nType) const
{
obj.clear();
obj.setObject();
if (fields & Field_addr) {
obj.pushKV("service", state.addr.ToStringIPPort(false));
}
if (fields & Field_nRegisteredHeight) {
obj.pushKV("registeredHeight", state.nRegisteredHeight);
}
if (fields & Field_nLastPaidHeight) {
obj.pushKV("lastPaidHeight", state.nLastPaidHeight);
}
if (fields & Field_nConsecutivePayments) {
obj.pushKV("consecutivePayments", state.nConsecutivePayments);
}
if (fields & Field_nPoSePenalty) {
obj.pushKV("PoSePenalty", state.nPoSePenalty);
}
if (fields & Field_nPoSeRevivedHeight) {
obj.pushKV("PoSeRevivedHeight", state.nPoSeRevivedHeight);
}
if (fields & Field_nPoSeBanHeight) {
obj.pushKV("PoSeBanHeight", state.nPoSeBanHeight);
}
if (fields & Field_nRevocationReason) {
obj.pushKV("revocationReason", state.nRevocationReason);
}
if (fields & Field_keyIDOwner) {
obj.pushKV("ownerAddress", EncodeDestination(PKHash(state.keyIDOwner)));
}
if (fields & Field_keyIDVoting) {
obj.pushKV("votingAddress", EncodeDestination(PKHash(state.keyIDVoting)));
}
if (fields & Field_scriptPayout) {
CTxDestination dest;
if (ExtractDestination(state.scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
}
if (fields & Field_scriptOperatorPayout) {
CTxDestination dest;
if (ExtractDestination(state.scriptOperatorPayout, dest)) {
obj.pushKV("operatorPayoutAddress", EncodeDestination(dest));
}
}
if (fields & Field_pubKeyOperator) {
obj.pushKV("pubKeyOperator", state.pubKeyOperator.Get().ToString());
}
if (nType == MnType::HighPerformance) {
if (fields & Field_platformNodeID) {
obj.pushKV("platformNodeID", state.platformNodeID.ToString());
}
if (fields & Field_platformP2PPort) {
obj.pushKV("platformP2PPort", state.platformP2PPort);
}
if (fields & Field_platformHTTPPort) {
obj.pushKV("platformHTTPPort", state.platformHTTPPort);
}
}
}

View File

@ -261,6 +261,8 @@ public:
#undef DMN_STATE_DIFF_LINE
}
void ToJson(UniValue& obj, MnType nType) const;
SERIALIZE_METHODS(CDeterministicMNStateDiff, obj)
{
READWRITE(VARINT(obj.fields));

View File

@ -1467,6 +1467,92 @@ static UniValue protx_diff(const JSONRPCRequest& request)
return ret;
}
static void protx_listdiff_help(const JSONRPCRequest& request)
{
RPCHelpMan{"protx listdiff",
"\nCalculate a full MN list diff between two masternode lists.\n",
{
{"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."},
{"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static const CBlockIndex* ParseBlockIndex(const UniValue& v, std::string strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
try {
uint256 hash = ParseHashV(v, strName);
const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex)
throw std::runtime_error(strprintf("Block %s with hash %s not found", strName, v.getValStr()));
return pblockindex;
} catch (...) {
int h = ParseInt32V(v, strName);
if (h < 1 || h > ::ChainActive().Height())
throw std::runtime_error(strprintf("%s must be a chain height and not %s", strName, v.getValStr()));
return ::ChainActive()[h];
}
}
static UniValue protx_listdiff(const JSONRPCRequest& request)
{
protx_listdiff_help(request);
LOCK(cs_main);
UniValue ret(UniValue::VOBJ);
const CBlockIndex* pBaseBlockIndex = ParseBlockIndex(request.params[0], "baseBlock");
const CBlockIndex* pTargetBlockIndex = ParseBlockIndex(request.params[1], "block");
if (pBaseBlockIndex == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Base block not found");
}
if (pTargetBlockIndex == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
ret.pushKV("baseHeight", pBaseBlockIndex->nHeight);
ret.pushKV("blockHeight", pTargetBlockIndex->nHeight);
auto baseBlockMNList = deterministicMNManager->GetListForBlock(pBaseBlockIndex);
auto blockMNList = deterministicMNManager->GetListForBlock(pTargetBlockIndex);
auto mnDiff = baseBlockMNList.BuildDiff(blockMNList);
UniValue jaddedMNs(UniValue::VARR);
for(const auto& mn : mnDiff.addedMNs) {
UniValue obj;
mn->ToJson(obj);
jaddedMNs.push_back(obj);
}
ret.pushKV("addedMNs", jaddedMNs);
UniValue jremovedMNs(UniValue::VARR);
for(const auto& internal_id : mnDiff.removedMns) {
auto dmn = baseBlockMNList.GetMNByInternalId(internal_id);
jremovedMNs.push_back(dmn->proTxHash.ToString());
}
ret.pushKV("removedMNs", jremovedMNs);
UniValue jupdatedMNs(UniValue::VARR);
for(const auto& [internal_id, stateDiff] : mnDiff.updatedMNs) {
auto dmn = baseBlockMNList.GetMNByInternalId(internal_id);
UniValue s(UniValue::VOBJ);
stateDiff.ToJson(s, dmn->nType);
UniValue obj(UniValue::VOBJ);
obj.pushKV(dmn->proTxHash.ToString(), s);
jupdatedMNs.push_back(obj);
}
ret.pushKV("updatedMNs", jupdatedMNs);
return ret;
}
[[ noreturn ]] static void protx_help()
{
RPCHelpMan{
@ -1495,7 +1581,8 @@ static UniValue protx_diff(const JSONRPCRequest& request)
" update_registrar_legacy - Create ProUpRegTx by parsing BLS using the legacy scheme, then send it to network\n"
" revoke - Create and send ProUpRevTx to network\n"
#endif
" diff - Calculate a diff and a proof between two masternode lists\n",
" diff - Calculate a diff and a proof between two masternode lists\n"
" listdiff - Calculate a full MN list diff between two masternode lists\n",
{
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
},
@ -1536,7 +1623,10 @@ static UniValue protx(const JSONRPCRequest& request)
return protx_info(new_request);
} else if (command == "protxdiff") {
return protx_diff(new_request);
} else {
} else if (command == "protxlistdiff") {
return protx_listdiff(new_request);
}
else {
protx_help();
}
}