From 3f6c8bfda13164a05e4b3dd6bd63ee76e6a4115a Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 8 Oct 2020 16:04:14 -0400 Subject: [PATCH] Add walletblindpsbt rpc --- src/wallet/rpcwallet.cpp | 51 ++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 50 +++++++++++++++++++++++++++++++++++---- src/wallet/wallet.h | 3 +++ 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 22ebac7d0a..973adbebe1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -4865,6 +4866,55 @@ static RPCHelpMan walletprocesspsbt() }; } +static RPCHelpMan walletblindpsbt() +{ + return RPCHelpMan{"walletblindpsbt", + "\nUpdate a PSBT with input information from our wallet and then sign inputs\n" + "that we can sign for.\n\n" + + HELP_REQUIRING_PASSPHRASE, + { + {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"}, + } + }, + RPCExamples{ + HelpExampleCli("walletblindpsbt", "\"psbt\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (!g_con_elementsmode) + throw std::runtime_error("PSBT operations are disabled when not in elementsmode.\n"); + + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR}); + + // Unserialize the transaction + PartiallySignedTransaction psbtx; + std::string error; + if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); + } + + if (pwallet->WalletBlindPSBT(psbtx) != BlindingStatus::OK) { + throw JSONRPCError(RPC_WALLET_ERROR, "Something went wrong"); + } + + UniValue result(UniValue::VOBJ); + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << psbtx; + result.pushKV("psbt", EncodeBase64(ssTx.str())); + + return result; +}, + }; +} + static RPCHelpMan walletcreatefundedpsbt() { return RPCHelpMan{"walletcreatefundedpsbt", @@ -7106,6 +7156,7 @@ static const CRPCCommand commands[] = { "wallet", "issueasset", &issueasset, {"assetamount", "tokenamount", "blind"}}, { "wallet", "reissueasset", &reissueasset, {"asset", "assetamount"}}, { "wallet", "destroyamount", &destroyamount, {"asset", "amount", "comment", "verbose"} }, + { "wallet", "walletblindpsbt", &walletblindpsbt, {"psbt"} }, { "hidden", "generatepegoutproof", &generatepegoutproof, {"sumkey", "btcpubkey", "onlinepubkey"} }, { "hidden", "getpegoutkeys", &getpegoutkeys, {"btcprivkey", "offlinepubkey"} }, }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cd60c25822..01806e5413 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1258,16 +1258,21 @@ void CWallet::BlockUntilSyncedToCurrentChain() const { chain().waitForNotificationsIfTipChanged(last_block_hash); } - -isminetype CWallet::IsMine(const CTxIn &txin) const +isminetype CWallet::IsMine(const CTxIn& txin) const { AssertLockHeld(cs_wallet); - std::map::const_iterator mi = mapWallet.find(txin.prevout.hash); + return IsMine(txin.prevout); +} + +isminetype CWallet::IsMine(const COutPoint &outpoint) const +{ + AssertLockHeld(cs_wallet); + std::map::const_iterator mi = mapWallet.find(outpoint.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.tx->vout.size()) - return IsMine(prev.tx->vout[txin.prevout.n]); + if (outpoint.n < prev.tx->vout.size()) + return IsMine(prev.tx->vout[outpoint.n]); } return ISMINE_NO; } @@ -2792,6 +2797,41 @@ TransactionError CWallet::FillPSBTData(PartiallySignedTransaction& psbtx, bool b return TransactionError::OK; } +BlindingStatus CWallet::WalletBlindPSBT(PartiallySignedTransaction& psbtx) const +{ + // Gather our input data + LOCK(cs_wallet); + std::map> our_input_data; + std::map> our_issuances_to_blind; + for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { + PSBTInput& input = psbtx.inputs[i]; + + if (input.m_peg_in_value && !input.m_peg_in_claim_script.empty()) { + if (!IsMine(CTxOut(Params().GetConsensus().pegged_asset, *input.m_peg_in_value, input.m_peg_in_claim_script))) continue; + our_input_data[i] = std::make_tuple(*input.m_peg_in_value, Params().GetConsensus().pegged_asset, uint256(), uint256()); + } else { + if (!IsMine(COutPoint(input.prev_txid, *input.prev_out))) continue; + const CWalletTx* wtx = GetWalletTx(input.prev_txid); + if (!wtx) continue; + CPubKey blinding_pubkey; + CAmount amount; + uint256 value_blinder; + CAsset asset; + uint256 asset_blinder; + wtx->GetNonIssuanceBlindingData(*input.prev_out, &blinding_pubkey, &amount, &value_blinder, &asset, &asset_blinder); + our_input_data[i] = std::make_tuple(amount, asset, asset_blinder, value_blinder); + } + + // Blind issuances on our inputs + if (input.m_issuance_value || input.m_issuance_inflation_keys_amount) { + CScript blinding_script(CScript() << OP_RETURN << std::vector(input.prev_txid.begin(), input.prev_txid.end()) << *input.prev_out); + our_issuances_to_blind[i] = std::make_pair(GetBlindingKey(&blinding_script), GetBlindingKey(&blinding_script)); + } + } + + // Blind the PSBT + return BlindPSBT(psbtx, our_input_data, our_issuances_to_blind); +} TransactionError CWallet::SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool imbalance_ok, bool bip32derivs, size_t* n_signed) const { /* diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f2f737557a..99e65225c0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -1090,6 +1091,7 @@ public: // ELEMENTS TransactionError FillPSBTData(PartiallySignedTransaction& psbtx, bool bip32derivs = false) const; TransactionError SignPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type = 1, bool sign = true, bool imbalance_ok = false, bool bip32derivs = false, size_t* n_signed = nullptr) const; + BlindingStatus WalletBlindPSBT(PartiallySignedTransaction& psbtx) const; // end ELEMENTS /** @@ -1172,6 +1174,7 @@ public: isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + isminetype IsMine(const COutPoint& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Returns amount of debit if the input matches the * filter, otherwise returns 0