Allow lockunspent to store the lock in the wallet DB

This commit is contained in:
Samuel Dobson 2021-09-23 00:18:39 +12:00
parent c52789365e
commit f13fc16295
2 changed files with 23 additions and 7 deletions

View file

@ -131,6 +131,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxoutsetinfo", 2, "use_index"}, { "gettxoutsetinfo", 2, "use_index"},
{ "lockunspent", 0, "unlock" }, { "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" }, { "lockunspent", 1, "transactions" },
{ "lockunspent", 2, "persistent" },
{ "send", 0, "outputs" }, { "send", 0, "outputs" },
{ "send", 1, "conf_target" }, { "send", 1, "conf_target" },
{ "send", 3, "fee_rate"}, { "send", 3, "fee_rate"},

View file

@ -2137,8 +2137,9 @@ static RPCHelpMan lockunspent()
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
"Manually selected coins are automatically unlocked.\n" "Manually selected coins are automatically unlocked.\n"
"Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
"is always cleared (by virtue of process exit) when a node stops or fails.\n" "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
"(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
"Also see the listunspent call\n", "Also see the listunspent call\n",
{ {
{"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"}, {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
@ -2152,6 +2153,7 @@ static RPCHelpMan lockunspent()
}, },
}, },
}, },
{"persistent", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to write/erase this lock in the wallet database, or keep the change in memory only. Ignored for unlocking."},
}, },
RPCResult{ RPCResult{
RPCResult::Type::BOOL, "", "Whether the command was successful or not" RPCResult::Type::BOOL, "", "Whether the command was successful or not"
@ -2165,6 +2167,8 @@ static RPCHelpMan lockunspent()
+ HelpExampleCli("listlockunspent", "") + + HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n" "\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
"\nLock the transaction persistently in the wallet database\n"
+ HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
"\nAs a JSON-RPC call\n" "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
}, },
@ -2183,9 +2187,13 @@ static RPCHelpMan lockunspent()
bool fUnlock = request.params[0].get_bool(); bool fUnlock = request.params[0].get_bool();
const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
if (request.params[1].isNull()) { if (request.params[1].isNull()) {
if (fUnlock) if (fUnlock) {
pwallet->UnlockAllCoins(); if (!pwallet->UnlockAllCoins())
throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
}
return true; return true;
} }
@ -2236,17 +2244,24 @@ static RPCHelpMan lockunspent()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
} }
if (!fUnlock && is_locked) { if (!fUnlock && is_locked && !persistent) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
} }
outputs.push_back(outpt); outputs.push_back(outpt);
} }
std::unique_ptr<WalletBatch> batch = nullptr;
// Unlock is always persistent
if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
// Atomically set (un)locked status for the outputs. // Atomically set (un)locked status for the outputs.
for (const COutPoint& outpt : outputs) { for (const COutPoint& outpt : outputs) {
if (fUnlock) pwallet->UnlockCoin(outpt); if (fUnlock) {
else pwallet->LockCoin(outpt); if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
} else {
if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
}
} }
return true; return true;