Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: unlock proregtx collateral on error #5838

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 50 additions & 39 deletions src/rpc/evo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,10 +648,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,

ptx.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex);
paramIdx += 2;

// TODO unlock on failure
LOCK(wallet->cs_wallet);
wallet->LockCoin(ptx.collateralOutpoint);
}

if (request.params[paramIdx].get_str() != "") {
Expand Down Expand Up @@ -721,15 +717,14 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str());
}

FundSpecialTx(wallet.get(), tx, ptx, fundDest);
UpdateSpecialTxInputsHash(tx, ptx);

bool fSubmit{true};
if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 7].isNull()) {
fSubmit = ParseBoolV(request.params[paramIdx + 7], "submit");
}

if (isFundRegister) {
FundSpecialTx(wallet.get(), tx, ptx, fundDest);
UpdateSpecialTxInputsHash(tx, ptx);
CAmount fundCollateral = GetMnType(mnType).collat_amount;
uint32_t collateralIndex = (uint32_t) -1;
for (uint32_t i = 0; i < tx.vout.size(); i++) {
Expand All @@ -746,41 +741,57 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
} else {
// referencing external collateral

Coin coin;
if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort()));
}
CTxDestination txDest;
ExtractDestination(coin.out.scriptPubKey, txDest);
const PKHash *pkhash = std::get_if<PKHash>(&txDest);
if (!pkhash) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort()));
}

if (isPrepareRegister) {
// external signing with collateral key
ptx.vchSig.clear();
SetTxPayload(tx, ptx);

UniValue ret(UniValue::VOBJ);
ret.pushKV("tx", EncodeHexTx(CTransaction(tx)));
ret.pushKV("collateralAddress", EncodeDestination(txDest));
ret.pushKV("signMessage", ptx.MakeSignString());
return ret;
} else {
// lets prove we own the collateral
LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan();
if (!spk_man) {
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
const bool unlockOnError = [&]() {
if (LOCK(wallet->cs_wallet); !wallet->IsLockedCoin(ptx.collateralOutpoint.hash, ptx.collateralOutpoint.n)) {
wallet->LockCoin(ptx.collateralOutpoint);
return true;
}
return false;
}();
try {
FundSpecialTx(wallet.get(), tx, ptx, fundDest);
UpdateSpecialTxInputsHash(tx, ptx);
Coin coin;
if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort()));
}
CTxDestination txDest;
ExtractDestination(coin.out.scriptPubKey, txDest);
const PKHash* pkhash = std::get_if<PKHash>(&txDest);
if (!pkhash) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort()));
}

CKey key;
if (!spk_man->GetKey(ToKeyID(*pkhash), key)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest)));
if (isPrepareRegister) {
// external signing with collateral key
ptx.vchSig.clear();
SetTxPayload(tx, ptx);

UniValue ret(UniValue::VOBJ);
ret.pushKV("tx", EncodeHexTx(CTransaction(tx)));
ret.pushKV("collateralAddress", EncodeDestination(txDest));
ret.pushKV("signMessage", ptx.MakeSignString());
return ret;
} else {
// lets prove we own the collateral
LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan();
if (!spk_man) {
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
}

CKey key;
if (!spk_man->GetKey(ToKeyID(*pkhash), key)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest)));
}
SignSpecialTxPayloadByString(tx, ptx, key);
panleone marked this conversation as resolved.
Show resolved Hide resolved
SetTxPayload(tx, ptx);
return SignAndSendSpecialTx(request, chainman, tx, fSubmit);
}
} catch (...) {
if (unlockOnError) {
WITH_LOCK(wallet->cs_wallet, wallet->UnlockCoin(ptx.collateralOutpoint));
}
SignSpecialTxPayloadByString(tx, ptx, key);
SetTxPayload(tx, ptx);
return SignAndSendSpecialTx(request, chainman, tx, fSubmit);
throw;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is there a naked throw?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bcs need to re-throw the exception which has been caught by except without changing logic of code. If throw is naked - it throws the last exception again.

But I'd suggest to write here a wrapper around Lock/Unlock same as 'lock_guard + mutex works'.

}
}
}
Expand Down
Loading