diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b26bbd8521..4f3a401347 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -712,14 +712,16 @@ void FillBlinds(CMutableTransaction& tx, bool fUseWallet, std::vector& CScript blindingScript = CScript() << OP_RETURN << std::vector(tx.vin[nIn].prevout.hash.begin(), tx.vin[nIn].prevout.hash.end()) << tx.vin[nIn].prevout.n; for (size_t nPseudo = 0; nPseudo < 2; nPseudo++) { - CConfidentialValue& confValue = (nPseudo == 0) ? issuance.nAmount : issuance.nInflationKeys; + bool issuance_asset = (nPseudo == 0); + std::vector& issuance_blinding_keys = issuance_asset ? asset_keys : token_keys; + CConfidentialValue& confValue = issuance_asset ? issuance.nAmount : issuance.nInflationKeys; if (confValue.IsCommitment()) { // Rangeproof must exist if (tx.wit.vtxinwit.size() <= nIn) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Transaction issuance is already blinded but has no attached rangeproof.")); } CTxInWitness& txinwit = tx.wit.vtxinwit[nIn]; - std::vector& vchRangeproof = (nPseudo == 0) ? txinwit.vchIssuanceAmountRangeproof : txinwit.vchInflationKeysRangeproof; + std::vector& vchRangeproof = issuance_asset ? txinwit.vchIssuanceAmountRangeproof : txinwit.vchInflationKeysRangeproof; uint256 blinding_factor; uint256 asset_blinding_factor; CAmount amount; @@ -728,29 +730,29 @@ void FillBlinds(CMutableTransaction& tx, bool fUseWallet, std::vector& // Wipe out confidential info from issuance vchRangeproof.clear(); confValue = CConfidentialValue(amount); - // One key both blinded values, single key needed for issuance reveal - asset_keys.push_back(pwalletMain->GetBlindingKey(&blindingScript)); - token_keys.push_back(pwalletMain->GetBlindingKey(&blindingScript)); + // One key blinds both values, single key needed for issuance reveal + issuance_blinding_keys.push_back(pwalletMain->GetBlindingKey(&blindingScript)); continue; } #endif // If no wallet, or unable to unblind, leave it alone in next blinding step - asset_keys.push_back(CKey()); - token_keys.push_back(CKey()); - } else { + issuance_blinding_keys.push_back(CKey()); + + } else if (confValue.IsExplicit()) { // Without wallet, nothing to be done. - asset_keys.push_back(CKey()); - token_keys.push_back(CKey()); + issuance_blinding_keys.push_back(CKey()); #ifdef ENABLE_WALLET // Use wallet to generate blindingkey used directly as nonce // as user is not "sending" to anyone. // Always assumed we want to blind here. // TODO Signal intent for all blinding via API including replacing nonce commitment if (fUseWallet) { - asset_keys[asset_keys.size()-1] = pwalletMain->GetBlindingKey(&blindingScript); - token_keys[token_keys.size()-1] = pwalletMain->GetBlindingKey(&blindingScript); + issuance_blinding_keys[issuance_blinding_keys.size()-1] = pwalletMain->GetBlindingKey(&blindingScript); } #endif + } else { + // Null or invalid, don't try anything but append an empty key + issuance_blinding_keys.push_back(CKey()); } } } @@ -766,6 +768,7 @@ UniValue rawblindrawtransaction(const JSONRPCRequest& request) "The input raw transaction cannot have already-blinded outputs.\n" "The output keys used can be specified by using a confidential address in createrawtransaction.\n" "If an additional blinded output is required to make a balanced blinding, a 0-value unspendable output will be added. Since there is no access to the wallet the blinding pubkey from the last output with blinding key will be repeated.\n" + "You can not blind issuances with this call.\n" "\nArguments:\n" "1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n" @@ -903,9 +906,9 @@ UniValue rawblindrawtransaction(const JSONRPCRequest& request) #ifdef ENABLE_WALLET UniValue blindrawtransaction(const JSONRPCRequest& request) { - if (request.fHelp || (request.params.size() < 1 || request.params.size() > 4)) + if (request.fHelp || (request.params.size() < 1 || request.params.size() > 5)) throw runtime_error( - "blindrawtransaction \"hexstring\" ( ignoreblindfail [\"assetcommitment,...\"] totalblinder )\n" + "blindrawtransaction \"hexstring\" ( ignoreblindfail [\"assetcommitment,...\"] blind_issuances \"totalblinder\" )\n" "\nConvert one or more outputs of a raw transaction into confidential ones using only wallet inputs.\n" "Returns the hex-encoded raw transaction.\n" "The output keys used can be specified by using a confidential address in createrawtransaction.\n" @@ -914,11 +917,13 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) "\nArguments:\n" "1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n" "2. \"ignoreblindfail\"\" (bool, optional, default=true) Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs.\n" - "3. [ (array, optional) An array of input asset generators. If provided, this list must be empty, or match the final input commitment list, including ordering, to make a valid surjection proof. This list does not include generators for issuances, as these assets are inherently unblinded.\n" + "3. \"asset_commitments\" \n" + " [ (array, optional) An array of input asset generators. If provided, this list must be empty, or match the final input commitment list, including ordering, to make a valid surjection proof. This list does not include generators for issuances, as these assets are inherently unblinded.\n" " \"assetcommitment\" (string, optional) A hex-encoded asset commitment, one for each input.\n" " Null commitments must be \"\".\n" " ],\n" - "4. \"totalblinder\" (string, optional) Ignored for now.\n" + "4. \"blind_issuances\" (bool, optional, default=true) Blind the issuances found in the raw transaction or not. All issuances will be blinded if true. \n" + "5. \"totalblinder\" (string, optional) Ignored for now.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -930,8 +935,10 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); } else if (request.params.size() == 3){ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)(UniValue::VARR)); + } else if (request.params.size() == 4){ + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)(UniValue::VARR)(UniValue::VBOOL)); } else { - RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)(UniValue::VARR)(UniValue::VSTR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)(UniValue::VARR)(UniValue::VBOOL)(UniValue::VSTR)); } vector txData(ParseHexV(request.params[0], "argument 1")); @@ -966,8 +973,11 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) } } + bool blind_issuances = request.params[3].isNull() || request.params[3].get_bool(); + LOCK(pwalletMain->cs_wallet); + std::vector input_blinds; std::vector input_asset_blinds; std::vector input_assets; @@ -976,6 +986,7 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) std::vector output_asset_blinds; std::vector output_assets; std::vector output_pubkeys; + std::vector blind_issuance_asset; int n_blinded_ins = 0; for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) { @@ -1014,8 +1025,14 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) std::vector asset_keys; std::vector token_keys; + // This fills out issuance blinding data for you from the wallet itself FillBlinds(tx, true, output_blinds, output_asset_blinds, output_pubkeys, asset_keys, token_keys); + if (!blind_issuances) { + asset_keys.clear(); + token_keys.clear(); + } + // How many are we trying to blind? int numPubKeys = 0; unsigned int keyIndex = 0; @@ -1026,6 +1043,12 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) keyIndex = i; } } + for (const auto& key : asset_keys) { + if (key.IsValid()) numPubKeys++; + } + for (const auto& key : token_keys) { + if (key.IsValid()) numPubKeys++; + } if (numPubKeys == 0 && n_blinded_ins == 0) { // Vacuous, just return the transaction @@ -1046,7 +1069,7 @@ UniValue blindrawtransaction(const JSONRPCRequest& request) } } - if (BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, std::vector(), std::vector(), tx, (auxiliary_generators.size() ? &auxiliary_generators : NULL)) != numPubKeys) { + if (BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, asset_keys, token_keys, tx, (auxiliary_generators.size() ? &auxiliary_generators : NULL)) != numPubKeys) { // TODO Have more rich return values, communicating to user what has been blinded // User may be ok not blinding something that for instance has no corresponding type on input throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?")); @@ -1567,7 +1590,7 @@ static const CRPCCommand commands[] = { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ { "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, false, {}}, #ifdef ENABLE_WALLET - { "rawtransactions", "blindrawtransaction", &blindrawtransaction, true, {}}, + { "rawtransactions", "blindrawtransaction", &blindrawtransaction, true, {"hexstring", "ignoreblindfail", "asset_commitments", "blind_issuances", "totalblinder"}}, #endif { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} },