Skip to content

Commit

Permalink
Added a new 'walletdeniabilizecoin' RPC.
Browse files Browse the repository at this point in the history
  • Loading branch information
denavila committed May 31, 2023
1 parent 92b64d8 commit f231492
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "walletcreatefundedpsbt", 2, "locktime" },
{ "walletcreatefundedpsbt", 3, "options" },
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
{ "walletdeniabilizecoin", 0, "inputs" },
{ "walletdeniabilizecoin", 1, "conf_target" },
{ "walletdeniabilizecoin", 2, "add_to_wallet" },
{ "walletprocesspsbt", 1, "sign" },
{ "walletprocesspsbt", 3, "bip32derivs" },
{ "walletprocesspsbt", 4, "finalize" },
Expand Down
122 changes: 122 additions & 0 deletions src/wallet/rpc/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1723,4 +1723,126 @@ RPCHelpMan walletcreatefundedpsbt()
},
};
}

// clang-format off
RPCHelpMan walletdeniabilizecoin()
{
return RPCHelpMan{"walletdeniabilizecoin",
"\nDeniabilize one or more UTXOs that share the same address.\n",
{
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "Specify inputs (must share the same address). A JSON array of JSON objects",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
}
}
},
},
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns the serialized transaction without broadcasting or adding it to the wallet"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The deniabilization transaction id."},
{RPCResult::Type::STR_AMOUNT, "fee", "The fee used in the deniabilization transaction."},
{RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
}
},
RPCExamples{
"\nDeniabilize a single UTXO\n"
+ HelpExampleCli("walletdeniabilizecoin", "\"[{\"txid\":\"4c14d20709daef476854fe7ef75bdfcfd5a7636a431b4622ec9481f297e12e8c\", \"vout\": 0}]\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return UniValue::VNULL;

std::optional<CTxDestination> shared_address;
std::set<COutPoint> inputs;
unsigned int deniabilization_cycles = UINT_MAX;
for (const UniValue& input : request.params[0].get_array().getValues()) {
uint256 txid = ParseHashO(input, "txid");

const UniValue& vout_v = input.find_value("vout");
if (!vout_v.isNum()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
}
int nOutput = vout_v.getInt<int>();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
}

COutPoint outpoint(txid, nOutput);
LOCK(pwallet->cs_wallet);
auto walletTx = pwallet->GetWalletTx(outpoint.hash);
if (!walletTx) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, txid not found in wallet.");
}
if (outpoint.n >= walletTx->tx->vout.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout is out of range");
}
const auto& output = walletTx->tx->vout[outpoint.n];

isminetype mine = pwallet->IsMine(output);
if (mine == ISMINE_NO) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, transaction's output doesn't belong to this wallet.");
}

bool spendable = (mine & ISMINE_SPENDABLE) != ISMINE_NO;

CTxDestination address;
if (spendable && ExtractDestination(FindNonChangeParentOutput(*pwallet, outpoint).scriptPubKey, address)) {
if (!shared_address) {
shared_address = address;
}
else if (!(*shared_address == address)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, inputs must share the same address");
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, inputs must be spendable and have a valid address");
}

inputs.emplace(outpoint);
auto cycles_res = CalculateDeniabilizationCycles(*pwallet, outpoint);
deniabilization_cycles = std::min(deniabilization_cycles, cycles_res.first);
}

if (inputs.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, inputs must not be empty");
}

unsigned int confirm_target = !request.params[1].isNull() ? request.params[1].getInt<unsigned int>() : 6;
const bool add_to_wallet = !request.params[2].isNull() ? request.params[2].get_bool() : true;

CTransactionRef tx;
CAmount tx_fee = 0;
{
bool sign = !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
bool insufficient_amount = false;
auto res = CreateDeniabilizationTransaction(*pwallet, inputs, confirm_target, deniabilization_cycles, sign, insufficient_amount);
if (!res) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, ErrorString(res).original);
}
tx = res->tx;
tx_fee = res->fee;
}

UniValue result(UniValue::VOBJ);
result.pushKV("txid", tx->GetHash().GetHex());
if (add_to_wallet) {
pwallet->CommitTransaction(tx, {}, /*orderForm=*/{});
} else {
std::string hex{EncodeHexTx(*tx)};
result.pushKV("hex", hex);
}
result.pushKV("fee", ValueFromAmount(tx_fee));
return result;
}
};
}
// clang-format on

} // namespace wallet
2 changes: 2 additions & 0 deletions src/wallet/rpc/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ RPCHelpMan send();
RPCHelpMan sendall();
RPCHelpMan walletprocesspsbt();
RPCHelpMan walletcreatefundedpsbt();
RPCHelpMan walletdeniabilizecoin();
RPCHelpMan signrawtransactionwithwallet();

// signmessage
Expand Down Expand Up @@ -956,6 +957,7 @@ Span<const CRPCCommand> GetWalletRPCCommands()
{"wallet", &walletpassphrase},
{"wallet", &walletpassphrasechange},
{"wallet", &walletprocesspsbt},
{"wallet", &walletdeniabilizecoin},
};
return commands;
}
Expand Down

0 comments on commit f231492

Please sign in to comment.