Skip to content

Commit

Permalink
feat: implement new rpc getislock
Browse files Browse the repository at this point in the history
It return InstantSend Lock as JSON or as hex-string depends on verbose=0|1

Hex format is compatible with zmq-subscription
  • Loading branch information
knst committed Jan 9, 2025
1 parent 1ecb6b5 commit 9cbd1c6
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettransaction", 1, "include_watchonly" },
{ "gettransaction", 2, "verbose" },
{ "getrawtransaction", 1, "verbose" },
{ "getislocks", 0, "txids" },
{ "getislocks", 1, "verbose" },
{ "getrawtransactionmulti", 0, "transactions" },
{ "getrawtransactionmulti", 1, "verbose" },
{ "gettxchainlocks", 0, "txids" },
Expand Down
96 changes: 96 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ static RPCHelpMan getrawtransactionmulti() {
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
"If false, return a string, otherwise return a json object"},
},
// TODO: replace RPCResults to proper annotation
RPCResults{},
RPCExamples{
HelpExampleCli("getrawtransactionmulti",
Expand Down Expand Up @@ -366,6 +367,100 @@ static RPCHelpMan getrawtransactionmulti() {
};
}

static RPCHelpMan getislocks()
{
return RPCHelpMan{"getislocks",
"\nReturns the raw InstantSend lock data for each txids. Returns Null if there is no known IS yet.",
{
{"txids", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction ids (no more than 100)",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
},
},
{"verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a json object"},
},
RPCResult{
RPCResult::Type::ARR, "", "Response is an array with the same size as the input txids",
{
RPCResult{"for verbose = 0 if InstantSend Lock is known for specified txid",
RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'"
},
RPCResult{"for verbose = 1 if InstantSend Lock is known for specified txid",
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
{RPCResult::Type::ARR, "inputs", "The inputs",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
{RPCResult::Type::NUM, "vout", "The output number"},
},
},
}},
{RPCResult::Type::STR_HEX, "cycleHash", "The Cycle Hash"},
{RPCResult::Type::STR_HEX, "signature", "The InstantSend's BLS signature"},
}},
RPCResult{"if no InstantSend Lock is known for specified txid",
RPCResult::Type::STR, "data", "Just 'None' string"
},
}},
RPCExamples{
HelpExampleCli("getislocks", "'[\"txid\",...]'")
+ HelpExampleRpc("getislocks", "'[\"txid\",...]'")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);

UniValue result_arr(UniValue::VARR);
UniValue txids = request.params[0].get_array();
if (txids.size() > 100) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only");
}

int verbose = 1;
if (!request.params[1].isNull()) {
if (request.params[1].isBool()) {
verbose = request.params[1].get_bool() ? 1 : 0;
} else {
verbose = request.params[1].get_int();
}
}

const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
for (const auto idx : irange::range(txids.size())) {
const uint256 txid(ParseHashV(txids[idx], "txid"));

if (const llmq::CInstantSendLockPtr islock = llmq_ctx.isman->GetInstantSendLockByTxid(txid); islock != nullptr) {
if (verbose == 1) {
UniValue objIS(UniValue::VOBJ);
objIS.pushKV("txid", islock->txid.ToString());
UniValue outputs(UniValue::VARR);
for (const auto out : islock->inputs) {
UniValue outpoint(UniValue::VOBJ);
outpoint.pushKV("txid", out.hash.ToString());
outpoint.pushKV("vout", static_cast<int64_t>(out.n));
outputs.push_back(outpoint);
}
objIS.pushKV("cycleHash", islock->cycleHash.ToString());
objIS.pushKV("sig", islock->sig.ToString());
result_arr.push_back(objIS);
} else {
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *islock;
result_arr.push_back(HexStr(ssTx));
}
} else {
result_arr.push_back("None");
}
}
return result_arr;

},
};
}

static RPCHelpMan gettxchainlocks()
{
return RPCHelpMan{
Expand Down Expand Up @@ -2088,6 +2183,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", &getassetunlockstatuses, },
{ "rawtransactions", &getrawtransaction, },
{ "rawtransactions", &getrawtransactionmulti, },
{ "rawtransactions", &getislocks, },
{ "rawtransactions", &gettxchainlocks, },
{ "rawtransactions", &createrawtransaction, },
{ "rawtransactions", &decoderawtransaction, },
Expand Down
3 changes: 3 additions & 0 deletions test/functional/interface_zmq_dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def test_instantsend_publishers(self):
# Create two raw TXs, they will conflict with each other
rpc_raw_tx_1 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)
rpc_raw_tx_2 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100)
assert_equal(['None'], self.nodes[0].getislocks([rpc_raw_tx_1['txid']], 0))
# Send the first transaction and wait for the InstantLock
rpc_raw_tx_1_hash = self.nodes[0].sendrawtransaction(rpc_raw_tx_1['hex'])
self.wait_for_instantlock(rpc_raw_tx_1_hash, self.nodes[0])
Expand All @@ -307,6 +308,8 @@ def test_instantsend_publishers(self):
assert_equal(zmq_tx_lock_tx.hash, rpc_raw_tx_1['txid'])
zmq_tx_lock = msg_isdlock()
zmq_tx_lock.deserialize(zmq_tx_lock_sig_stream)
assert_equal(rpc_raw_tx_1['txid'], self.nodes[0].getislocks([rpc_raw_tx_1['txid']], 1)[0]['txid'])
assert_equal([zmq_tx_lock.serialize().hex()], self.nodes[0].getislocks([rpc_raw_tx_1['txid']], 0))
assert_equal(uint256_to_string(zmq_tx_lock.txid), rpc_raw_tx_1['txid'])
# Try to send the second transaction. This must throw an RPC error because it conflicts with rpc_raw_tx_1
# which already got the InstantSend lock.
Expand Down

0 comments on commit 9cbd1c6

Please sign in to comment.