Skip to content

Commit 4fd4e0e

Browse files
committed
rpc: allow ipAndPort to accept multiple entries with arrays
1 parent 8c28f30 commit 4fd4e0e

File tree

4 files changed

+62
-24
lines changed

4 files changed

+62
-24
lines changed

src/rpc/evo.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,19 @@ static RPCArg GetRpcArg(const std::string& strParamName)
9393
"The private key belonging to this address must be known in your wallet."}
9494
},
9595
{"ipAndPort",
96-
{"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO,
97-
"IP and port in the form \"IP:PORT\". Must be unique on the network.\n"
98-
"Can be set to an empty string, which will require a ProUpServTx afterwards."}
96+
{"ipAndPort", RPCArg::Type::ARR, RPCArg::Optional::NO,
97+
"Array of addresses in the form \"ADDR:PORT\". Must be unique on the network.\n"
98+
"Can be set to an empty string, which will require a ProUpServTx afterwards.",
99+
{
100+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, ""},
101+
}}
99102
},
100103
{"ipAndPort_update",
101-
{"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO,
102-
"IP and port in the form \"IP:PORT\". Must be unique on the network."}
104+
{"ipAndPort", RPCArg::Type::ARR, RPCArg::Optional::NO,
105+
"Array of addresses in the form \"ADDR:PORT\". Must be unique on the network.",
106+
{
107+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, ""},
108+
}}
103109
},
104110
{"operatorKey",
105111
{"operatorKey", RPCArg::Type::STR, RPCArg::Optional::NO,

src/rpc/evo_util.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,50 @@ void ProcessNetInfoCore(T1& ptx, const UniValue& input, const bool optional)
1818
{
1919
CHECK_NONFATAL(ptx.netInfo);
2020

21-
if (!input.isStr()) {
22-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for ipAndPort, must be string");
23-
}
24-
if (!optional && input.get_str().empty()) {
25-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty param for ipAndPort not allowed");
26-
}
27-
if (!input.get_str().empty()) {
28-
if (auto entryRet = ptx.netInfo->AddEntry(input.get_str()); entryRet != NetInfoStatus::Success) {
21+
if (input.isStr()) {
22+
const std::string& entry = input.get_str();
23+
if (entry.empty()) {
24+
if (!optional) {
25+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty param for ipAndPort not allowed");
26+
}
27+
return; // Nothing to do
28+
}
29+
if (auto entryRet = ptx.netInfo->AddEntry(entry); entryRet != NetInfoStatus::Success) {
2930
throw JSONRPCError(RPC_INVALID_PARAMETER,
30-
strprintf("Error setting ipAndPort to '%s' (%s)", input.get_str(), NISToString(entryRet)));
31+
strprintf("Error setting ipAndPort[0] to '%s' (%s)", entry, NISToString(entryRet)));
32+
}
33+
return; // Parsing complete
34+
}
35+
36+
if (input.isArray()) {
37+
const UniValue& entries = input.get_array();
38+
if (entries.empty()) {
39+
if (!optional) {
40+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Empty params for ipAndPort not allowed");
41+
}
42+
return; // Nothing to do
3143
}
44+
for (size_t idx{0}; idx < entries.size(); idx++) {
45+
const UniValue& entry_uv{entries[idx]};
46+
if (!entry_uv.isStr()) {
47+
throw JSONRPCError(RPC_INVALID_PARAMETER,
48+
strprintf("Invalid param for ipAndPort[%d], must be string", idx));
49+
}
50+
const std::string& entry = entry_uv.get_str();
51+
if (entry.empty()) {
52+
throw JSONRPCError(RPC_INVALID_PARAMETER,
53+
strprintf("Invalid param for ipAndPort[%d], cannot be empty string", idx));
54+
}
55+
if (auto entryRet = ptx.netInfo->AddEntry(entry); entryRet != NetInfoStatus::Success) {
56+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Error setting ipAndPort[%d] to '%s' (%s)", idx,
57+
entry, NISToString(entryRet)));
58+
}
59+
}
60+
return; // Parsing complete
3261
}
62+
63+
// Invalid input
64+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for ipAndPort, must be string or array");
3365
}
3466
template void ProcessNetInfoCore(CProRegTx& ptx, const UniValue& input, const bool optional);
3567
template void ProcessNetInfoCore(CProUpServTx& ptx, const UniValue& input, const bool optional);

test/functional/feature_dip3_deterministicmns.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def update_mn_payee(self, mn: MasternodeInfo, payee):
272272

273273
def test_protx_update_service(self, mn: MasternodeInfo):
274274
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
275-
mn.update_service(self.nodes[0], submit=True, ipAndPort=f'127.0.0.2:{mn.nodePort}')
275+
mn.update_service(self.nodes[0], submit=True, ipAndPort=[f'127.0.0.2:{mn.nodePort}'])
276276
self.generate(self.nodes[0], 1)
277277
for node in self.nodes:
278278
protx_info = node.protx('info', mn.proTxHash)

test/functional/test_framework/test_framework.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import time
2424
from concurrent.futures import ThreadPoolExecutor
2525

26-
from typing import List, Optional
26+
from typing import List, Optional, Union
2727
from .address import ADDRESS_BCRT1_P2SH_OP_TRUE
2828
from .authproxy import JSONRPCException
2929
from test_framework.masternodes import check_banned, check_punished
@@ -1216,7 +1216,7 @@ def get_node(self, test: BitcoinTestFramework) -> TestNode:
12161216
return test.nodes[self.nodeIdx]
12171217

12181218
def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str] = None, collateral_vout: Optional[int] = None,
1219-
ipAndPort: Optional[str] = None, ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, votingAddr: Optional[str] = None,
1219+
ipAndPort: Union[str, List[str], None] = None, ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, votingAddr: Optional[str] = None,
12201220
operator_reward: Optional[int] = None, rewards_address: Optional[str] = None, fundsAddr: Optional[str] = None,
12211221
platform_node_id: Optional[str] = None, platform_p2p_port: Optional[int] = None, platform_http_port: Optional[int] = None,
12221222
expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None) -> Optional[str]:
@@ -1236,7 +1236,7 @@ def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str]
12361236
args = [
12371237
collateral_txid or self.collateral_txid,
12381238
collateral_vout or self.collateral_vout,
1239-
ipAndPort or f'127.0.0.1:{self.nodePort}',
1239+
ipAndPort or [f'127.0.0.1:{self.nodePort}'],
12401240
ownerAddr or self.ownerAddr,
12411241
pubKeyOperator or self.pubKeyOperator,
12421242
votingAddr or self.votingAddr,
@@ -1271,7 +1271,7 @@ def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str]
12711271

12721272
return ret
12731273

1274-
def register_fund(self, node: TestNode, submit: bool, collateral_address: Optional[str] = None, ipAndPort: Optional[str] = None,
1274+
def register_fund(self, node: TestNode, submit: bool, collateral_address: Optional[str] = None, ipAndPort: Union[str, List[str], None] = None,
12751275
ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, votingAddr: Optional[str] = None,
12761276
operator_reward: Optional[int] = None, rewards_address: Optional[str] = None, fundsAddr: Optional[str] = None,
12771277
platform_node_id: Optional[str] = None, platform_p2p_port: Optional[int] = None, platform_http_port: Optional[int] = None,
@@ -1299,7 +1299,7 @@ def register_fund(self, node: TestNode, submit: bool, collateral_address: Option
12991299
# Common arguments shared between regular masternodes and EvoNodes
13001300
args = [
13011301
collateral_address or self.collateral_address,
1302-
ipAndPort or f'127.0.0.1:{self.nodePort}',
1302+
ipAndPort or [f'127.0.0.1:{self.nodePort}'],
13031303
ownerAddr or self.ownerAddr,
13041304
pubKeyOperator or self.pubKeyOperator,
13051305
votingAddr or self.votingAddr,
@@ -1410,7 +1410,7 @@ def update_registrar(self, node: TestNode, submit: bool, pubKeyOperator: Optiona
14101410

14111411
return ret
14121412

1413-
def update_service(self, node: TestNode, submit: bool, ipAndPort: Optional[str] = None, platform_node_id: Optional[str] = None, platform_p2p_port: Optional[int] = None,
1413+
def update_service(self, node: TestNode, submit: bool, ipAndPort: Union[str, List[str], None] = None, platform_node_id: Optional[str] = None, platform_p2p_port: Optional[int] = None,
14141414
platform_http_port: Optional[int] = None, address_operator: Optional[str] = None, fundsAddr: Optional[str] = None,
14151415
expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None) -> Optional[str]:
14161416
if (expected_assert_code and not expected_assert_msg) or (not expected_assert_code and expected_assert_msg):
@@ -1442,7 +1442,7 @@ def update_service(self, node: TestNode, submit: bool, ipAndPort: Optional[str]
14421442
# Common arguments shared between regular masternodes and EvoNodes
14431443
args = [
14441444
self.proTxHash,
1445-
ipAndPort or f'127.0.0.1:{self.nodePort}',
1445+
ipAndPort or [f'127.0.0.1:{self.nodePort}'],
14461446
self.keyOperator,
14471447
]
14481448
address_funds = fundsAddr or self.fundsAddr
@@ -1643,7 +1643,7 @@ def dynamically_prepare_masternode(self, idx, node_p2p_port, evo=False, rnd=None
16431643
mn.bury_tx(self, genIdx=0, txid=collateral_txid, depth=1)
16441644
collateral_vout = mn.get_collateral_vout(self.nodes[0], collateral_txid)
16451645

1646-
ipAndPort = '127.0.0.1:%d' % node_p2p_port
1646+
ipAndPort = ['127.0.0.1:%d' % node_p2p_port]
16471647
operatorReward = idx
16481648

16491649
# platform_node_id, platform_p2p_port and platform_http_port are ignored for regular masternodes
@@ -1709,7 +1709,7 @@ def prepare_masternode(self, idx):
17091709
self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001)
17101710

17111711
port = p2p_port(len(self.nodes) + idx)
1712-
ipAndPort = '127.0.0.1:%d' % port
1712+
ipAndPort = ['127.0.0.1:%d' % port]
17131713
operatorReward = idx
17141714

17151715
submit = (idx % 4) < 2

0 commit comments

Comments
 (0)