Skip to content

Commit

Permalink
Add safe flag to listunspent result
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier authored and random-zebra committed Jun 2, 2021
1 parent 0201065 commit f219be9
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static const CRPCConvertParam vRPCConvertParams[] = {
{ "listunspent", 2, "addresses" },
{ "listunspent", 3, "watchonly_config" },
{ "listunspent", 4, "query_options" },
{ "listunspent", 5, "include_unsafe" },
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
{ "logging", 0, "include" },
Expand Down
19 changes: 14 additions & 5 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3591,9 +3591,9 @@ UniValue listunspent(const JSONRPCRequest& request)
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;

if (request.fHelp || request.params.size() > 5)
if (request.fHelp || request.params.size() > 6)
throw std::runtime_error(
"listunspent ( minconf maxconf [\"address\",...] watchonly_config [query_options])\n"
"listunspent ( minconf maxconf [\"address\",...] watchonly_config [query_options] include_unsafe)\n"
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n"
Expand All @@ -3616,6 +3616,9 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
" \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
" }\n"
"6. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
" See description of \"safe\" attribute below.\n"

"\nResult\n"
"[ (array of json object)\n"
" {\n"
Expand All @@ -3629,7 +3632,10 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"amount\" : x.xxx, (numeric) the transaction amount in PIV\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"spendable\" : true|false (boolean) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
" \"solvable\" : xxx (boolean) Whether we know how to spend this output, ignoring the lack of keys\n"
" \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
" and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
" }\n"
" ,...\n"
"]\n"
Expand All @@ -3638,6 +3644,7 @@ UniValue listunspent(const JSONRPCRequest& request)
HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
+ HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }' false")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , 1, { \"minimumAmount\": 0.005 } ")
);

Expand Down Expand Up @@ -3713,7 +3720,8 @@ UniValue listunspent(const JSONRPCRequest& request)
CCoinControl coinControl;
coinControl.fAllowWatchOnly = nWatchonlyConfig == 2;

coinFilter.fOnlySafe = false;
bool include_unsafe = request.params.size() < 6 || request.params[5].get_bool();
coinFilter.fOnlySafe = !include_unsafe;

UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
Expand Down Expand Up @@ -3760,6 +3768,7 @@ UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}

Expand Down Expand Up @@ -4492,7 +4501,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly","filter"} },
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
{ "wallet", "listtransactions", &listtransactions, false, {"dummy","count","from","include_watchonly","include_delegated","include_cold"} },
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config" } },
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config","query_options","include_unsafe" } },
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
{ "wallet", "rawdelegatestake", &rawdelegatestake, false, {"staking_addr","amount","owner_addr","ext_owner","include_delegated","from_shield","force"} },
{ "wallet", "sendmany", &sendmany, false, {"dummy","amounts","minconf","comment","include_delegated"} },
Expand Down
22 changes: 18 additions & 4 deletions test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def run_test(self):

# Exercise locking of unspent outputs
unspent_0 = self.nodes[1].listunspent()[0]
assert unspent_0["solvable"]
assert unspent_0["spendable"]
assert unspent_0["safe"]
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
self.nodes[1].lockunspent(False, [unspent_0])
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[1].sendtoaddress, self.nodes[1].getnewaddress(), 20)
Expand All @@ -85,15 +88,26 @@ def run_test(self):

# Send 21 PIV from 1 to 0 using sendtoaddress call.
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 21)
self.sync_mempools(self.nodes[0:3])

# Node0 should have two unspent outputs.
# One safe, the other one not yet
node0utxos = self.nodes[0].listunspent(0)
assert_equal(len(node0utxos), 2)
newutxos = [x for x in node0utxos if x["txid"] != utxos[0]["txid"]]
assert_equal(len(newutxos), 1)
assert not newutxos[0]["safe"]

# Mine the other tx
self.nodes[1].generate(1)
self.sync_all(self.nodes[0:3])
node0utxos = self.nodes[0].listunspent()
assert_equal(len(node0utxos), 2)
for u in node0utxos:
assert u["safe"]

# Node0 should have two unspent outputs.
# Create a couple of transactions to send them to node2, submit them through
# node1, and make sure both node0 and node2 pick them up properly:
node0utxos = self.nodes[0].listunspent(1)
assert_equal(len(node0utxos), 2)

# create both transactions
fee_per_kbyte = Decimal('0.001')
txns_to_send = []
Expand Down

0 comments on commit f219be9

Please sign in to comment.