Skip to content

Commit c1a861e

Browse files
committedJan 14, 2025··
feat: implement new rpc getislock
The field hex contains hex-encoded binary representation which is compatible with zmq-subscription
1 parent 53a5707 commit c1a861e

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed
 

‎src/rpc/client.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
115115
{ "gettransaction", 1, "include_watchonly" },
116116
{ "gettransaction", 2, "verbose" },
117117
{ "getrawtransaction", 1, "verbose" },
118+
{ "getislocks", 0, "txids" },
118119
{ "getrawtransactionmulti", 0, "transactions" },
119120
{ "getrawtransactionmulti", 1, "verbose" },
120121
{ "gettxchainlocks", 0, "txids" },

‎src/rpc/rawtransaction.cpp

+82
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ static RPCHelpMan getrawtransactionmulti() {
300300
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
301301
"If false, return a string, otherwise return a json object"},
302302
},
303+
// TODO: replace RPCResults to proper annotation
303304
RPCResults{},
304305
RPCExamples{
305306
HelpExampleCli("getrawtransactionmulti",
@@ -366,6 +367,86 @@ static RPCHelpMan getrawtransactionmulti() {
366367
};
367368
}
368369

370+
static RPCHelpMan getislocks()
371+
{
372+
return RPCHelpMan{"getislocks",
373+
"\nReturns the raw InstantSend lock data for each txids. Returns Null if there is no known IS yet.",
374+
{
375+
{"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction ids (no more than 100)",
376+
{
377+
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
378+
},
379+
},
380+
},
381+
RPCResult{
382+
RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids",
383+
{{RPCResult::Type::OBJ, "", "",
384+
{
385+
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
386+
{RPCResult::Type::ARR, "inputs", "The inputs",
387+
{
388+
{RPCResult::Type::OBJ, "", "",
389+
{
390+
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
391+
{RPCResult::Type::NUM, "vout", "The output number"},
392+
},
393+
},
394+
}},
395+
{RPCResult::Type::STR_HEX, "cycleHash", "The Cycle Hash"},
396+
{RPCResult::Type::STR_HEX, "signature", "The InstantSend's BLS signature"},
397+
{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"},
398+
}},
399+
RPCResult{"if no InstantSend Lock is known for specified txid",
400+
RPCResult::Type::STR, "data", "Just 'None' string"
401+
},
402+
}},
403+
RPCExamples{
404+
HelpExampleCli("getislocks", "'[\"txid\",...]'")
405+
+ HelpExampleRpc("getislocks", "'[\"txid\",...]'")
406+
},
407+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
408+
{
409+
const NodeContext& node = EnsureAnyNodeContext(request.context);
410+
411+
UniValue result_arr(UniValue::VARR);
412+
UniValue txids = request.params[0].get_array();
413+
if (txids.size() > 100) {
414+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only");
415+
}
416+
417+
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
418+
for (const auto idx : irange::range(txids.size())) {
419+
const uint256 txid(ParseHashV(txids[idx], "txid"));
420+
421+
if (const llmq::CInstantSendLockPtr islock = llmq_ctx.isman->GetInstantSendLockByTxid(txid); islock != nullptr) {
422+
UniValue objIS(UniValue::VOBJ);
423+
objIS.pushKV("txid", islock->txid.ToString());
424+
UniValue inputs(UniValue::VARR);
425+
for (const auto out : islock->inputs) {
426+
UniValue outpoint(UniValue::VOBJ);
427+
outpoint.pushKV("txid", out.hash.ToString());
428+
outpoint.pushKV("vout", static_cast<int64_t>(out.n));
429+
inputs.push_back(outpoint);
430+
}
431+
objIS.pushKV("inputs", inputs);
432+
objIS.pushKV("cycleHash", islock->cycleHash.ToString());
433+
objIS.pushKV("signature", islock->sig.ToString());
434+
{
435+
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
436+
ssTx << *islock;
437+
objIS.pushKV("hex", HexStr(ssTx));
438+
}
439+
result_arr.push_back(objIS);
440+
} else {
441+
result_arr.push_back("None");
442+
}
443+
}
444+
return result_arr;
445+
446+
},
447+
};
448+
}
449+
369450
static RPCHelpMan gettxchainlocks()
370451
{
371452
return RPCHelpMan{
@@ -2088,6 +2169,7 @@ static const CRPCCommand commands[] =
20882169
{ "rawtransactions", &getassetunlockstatuses, },
20892170
{ "rawtransactions", &getrawtransaction, },
20902171
{ "rawtransactions", &getrawtransactionmulti, },
2172+
{ "rawtransactions", &getislocks, },
20912173
{ "rawtransactions", &gettxchainlocks, },
20922174
{ "rawtransactions", &createrawtransaction, },
20932175
{ "rawtransactions", &decoderawtransaction, },

‎test/functional/interface_zmq_dash.py

+3
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ def test_instantsend_publishers(self):
288288
# Create two raw TXs, they will conflict with each other
289289
rpc_raw_tx_1 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)
290290
rpc_raw_tx_2 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)
291+
assert_equal(['None'], self.nodes[0].getislocks([rpc_raw_tx_1['txid']]))
291292
# Send the first transaction and wait for the InstantLock
292293
rpc_raw_tx_1_hash = self.nodes[0].sendrawtransaction(rpc_raw_tx_1['hex'])
293294
self.wait_for_instantlock(rpc_raw_tx_1_hash, self.nodes[0])
@@ -307,6 +308,8 @@ def test_instantsend_publishers(self):
307308
assert_equal(zmq_tx_lock_tx.hash, rpc_raw_tx_1['txid'])
308309
zmq_tx_lock = msg_isdlock()
309310
zmq_tx_lock.deserialize(zmq_tx_lock_sig_stream)
311+
assert_equal(rpc_raw_tx_1['txid'], self.nodes[0].getislocks([rpc_raw_tx_1['txid']])[0]['txid'])
312+
assert_equal(zmq_tx_lock.serialize().hex(), self.nodes[0].getislocks([rpc_raw_tx_1['txid']])[0]['hex'])
310313
assert_equal(uint256_to_string(zmq_tx_lock.txid), rpc_raw_tx_1['txid'])
311314
# Try to send the second transaction. This must throw an RPC error because it conflicts with rpc_raw_tx_1
312315
# which already got the InstantSend lock.

0 commit comments

Comments
 (0)
Please sign in to comment.