|
|
|
@ -2447,12 +2447,15 @@ UniValue lockunspent(const JSONRPCRequest& request)
@@ -2447,12 +2447,15 @@ UniValue lockunspent(const JSONRPCRequest& request)
|
|
|
|
|
|
|
|
|
|
RPCTypeCheckArgument(request.params[1], UniValue::VARR); |
|
|
|
|
|
|
|
|
|
UniValue outputs = request.params[1].get_array(); |
|
|
|
|
for (unsigned int idx = 0; idx < outputs.size(); idx++) { |
|
|
|
|
const UniValue& output = outputs[idx]; |
|
|
|
|
if (!output.isObject()) |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
|
|
|
|
const UniValue& o = output.get_obj(); |
|
|
|
|
const UniValue& output_params = request.params[1]; |
|
|
|
|
|
|
|
|
|
// Create and validate the COutPoints first.
|
|
|
|
|
|
|
|
|
|
std::vector<COutPoint> outputs; |
|
|
|
|
outputs.reserve(output_params.size()); |
|
|
|
|
|
|
|
|
|
for (unsigned int idx = 0; idx < output_params.size(); idx++) { |
|
|
|
|
const UniValue& o = output_params[idx].get_obj(); |
|
|
|
|
|
|
|
|
|
RPCTypeCheckObj(o, |
|
|
|
|
{ |
|
|
|
@ -2460,20 +2463,50 @@ UniValue lockunspent(const JSONRPCRequest& request)
@@ -2460,20 +2463,50 @@ UniValue lockunspent(const JSONRPCRequest& request)
|
|
|
|
|
{"vout", UniValueType(UniValue::VNUM)}, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
std::string txid = find_value(o, "txid").get_str(); |
|
|
|
|
if (!IsHex(txid)) |
|
|
|
|
const std::string& txid = find_value(o, "txid").get_str(); |
|
|
|
|
if (!IsHex(txid)) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int nOutput = find_value(o, "vout").get_int(); |
|
|
|
|
if (nOutput < 0) |
|
|
|
|
const int nOutput = find_value(o, "vout").get_int(); |
|
|
|
|
if (nOutput < 0) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
COutPoint outpt(uint256S(txid), nOutput); |
|
|
|
|
const COutPoint outpt(uint256S(txid), nOutput); |
|
|
|
|
|
|
|
|
|
if (fUnlock) |
|
|
|
|
pwallet->UnlockCoin(outpt); |
|
|
|
|
else |
|
|
|
|
pwallet->LockCoin(outpt); |
|
|
|
|
const auto it = pwallet->mapWallet.find(outpt.hash); |
|
|
|
|
if (it == pwallet->mapWallet.end()) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const CWalletTx& trans = it->second; |
|
|
|
|
|
|
|
|
|
if (outpt.n >= trans.tx->vout.size()) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pwallet->IsSpent(outpt.hash, outpt.n)) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n); |
|
|
|
|
|
|
|
|
|
if (fUnlock && !is_locked) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!fUnlock && is_locked) { |
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
outputs.push_back(outpt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Atomically set (un)locked status for the outputs.
|
|
|
|
|
for (const COutPoint& outpt : outputs) { |
|
|
|
|
if (fUnlock) pwallet->UnlockCoin(outpt); |
|
|
|
|
else pwallet->LockCoin(outpt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|