[BROKEN] Add raw tx RPCs for CA

This commit is contained in:
Steven Roose 2019-03-20 10:45:24 +00:00
parent 6038c37c7e
commit a03239dd31
No known key found for this signature in database
GPG key ID: 7FC91380BB4CE800
3 changed files with 234 additions and 33 deletions

View file

@ -167,6 +167,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "combineblocksigs", 1, "signatures" }, { "combineblocksigs", 1, "signatures" },
{ "sendtomainchain", 1, "amount" }, { "sendtomainchain", 1, "amount" },
{ "sendtomainchain", 2, "subtractfeefromamount" }, { "sendtomainchain", 2, "subtractfeefromamount" },
{ "rawblindrawtransaction", 1, "inputblinder" },
{ "rawblindrawtransaction", 2, "inputamount" },
{ "rawblindrawtransaction", 3, "inputasset" },
{ "rawblindrawtransaction", 4, "inputassetblinder" },
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
{ "createrawtransaction", 4, "output_assets" },
}; };
// clang-format on // clang-format on

View file

@ -28,7 +28,13 @@
#include <uint256.h> #include <uint256.h>
#include <utilstrencodings.h> #include <utilstrencodings.h>
#include <validation.h> #include <validation.h>
#include <confidential_validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <blind.h>
#include <rpc/util.h>
#ifdef ENABLE_WALLET
#include <wallet/rpcwallet.h>
#endif
#include <future> #include <future>
#include <stdint.h> #include <stdint.h>
@ -343,7 +349,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
return res; return res;
} }
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf) CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf, const UniValue& assets_in)
{ {
if (inputs_in.isNull() || outputs_in.isNull()) if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
@ -363,6 +369,11 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
bool rbfOptIn = rbf.isTrue(); bool rbfOptIn = rbf.isTrue();
UniValue assets;
if (!assets_in.isNull()) {
assets = assets_in.get_obj();
}
for (unsigned int idx = 0; idx < inputs.size(); idx++) { for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx]; const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj(); const UniValue& o = input.get_obj();
@ -417,11 +428,22 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
} }
outputs = std::move(outputs_dict); outputs = std::move(outputs_dict);
} }
// Keep track of the fee output so we can add it in the very end of the transaction.
CTxOut fee_out;
for (const std::string& name_ : outputs.getKeys()) { for (const std::string& name_ : outputs.getKeys()) {
// ELEMENTS:
// Asset defaults to policyAsset
CAsset asset(::policyAsset);
if (!assets.isNull()) {
if (!find_value(assets, name_).isNull()) {
asset = CAsset(ParseHashO(assets, name_));
}
}
if (name_ == "data") { if (name_ == "data") {
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
CTxOut out(0, CScript() << OP_RETURN << data); CTxOut out(asset, 0, CScript() << OP_RETURN << data);
rawTx.vout.push_back(out); rawTx.vout.push_back(out);
} else if (name_ == "vdata") { } else if (name_ == "vdata") {
// ELEMENTS: support multi-push OP_RETURN // ELEMENTS: support multi-push OP_RETURN
@ -432,9 +454,12 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
datascript << data; datascript << data;
} }
//TODO(rebase) CA asset CTxOut out(asset, 0, datascript);
CTxOut out(0, datascript);
rawTx.vout.push_back(out); rawTx.vout.push_back(out);
} else if (name_ == "fee") {
// ELEMENTS: explicit fee outputs
CAmount nAmount = AmountFromValue(outputs[name_]);
fee_out = CTxOut(asset, nAmount, CScript());
} else { } else {
CTxDestination destination = DecodeDestination(name_); CTxDestination destination = DecodeDestination(name_);
if (!IsValidDestination(destination)) { if (!IsValidDestination(destination)) {
@ -448,11 +473,20 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
CScript scriptPubKey = GetScriptForDestination(destination); CScript scriptPubKey = GetScriptForDestination(destination);
CAmount nAmount = AmountFromValue(outputs[name_]); CAmount nAmount = AmountFromValue(outputs[name_]);
CTxOut out(nAmount, scriptPubKey); CTxOut out(asset, nAmount, scriptPubKey);
if (IsBlindDestination(destination)) {
CPubKey blind_pub = GetDestinationBlindingKey(destination);
out.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub.begin(), blind_pub.end());
}
rawTx.vout.push_back(out); rawTx.vout.push_back(out);
} }
} }
// Add fee output in the end.
if (!fee_out.nValue.IsNull() && fee_out.nValue.GetAmount() > 0) {
rawTx.vout.push_back(fee_out);
}
if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(rawTx)) { if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(rawTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
} }
@ -462,10 +496,10 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
static UniValue createrawtransaction(const JSONRPCRequest& request) static UniValue createrawtransaction(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) {
throw std::runtime_error( throw std::runtime_error(
// clang-format off // clang-format off
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n" "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( {\"address\":\"asset\"} )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n" "Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n" "Returns hex-encoded raw transaction.\n"
@ -490,6 +524,9 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
" {\n" " {\n"
" \"data\": \"hex\" , (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" " \"data\": \"hex\" , (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
" \"vdata\": [\"hex\"] (string, optional) The key is \"vdata\", the value is an array of hex encoded data\n" " \"vdata\": [\"hex\"] (string, optional) The key is \"vdata\", the value is an array of hex encoded data\n"
" },\n"
" {\n"
" \"fee\": x.xxx (numeric or string, optional) The key is \"fee\", the value the fee output you want to add.\n"
" }\n" " }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n" " accepted as second parameter.\n"
@ -497,6 +534,12 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
"4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
"5. \"output_assets\" (strings, optional, default=bitcoin) A json object of assets to addresses\n"
" {\n"
" \"address\": \"hex\" \n"
" \"fee\": \"hex\" \n"
" ...\n"
" }\n"
"\nResult:\n" "\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n" "\"transaction\" (string) hex string of the transaction\n"
@ -513,11 +556,12 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
UniValue::VARR, UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later UniValueType(), // ARR or OBJ, checked later
UniValue::VNUM, UniValue::VNUM,
UniValue::VBOOL UniValue::VBOOL,
UniValue::VOBJ
}, true }, true
); );
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]); CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4]);
return EncodeHexTx(rawTx); return EncodeHexTx(rawTx);
} }
@ -834,9 +878,13 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
} }
Coin newcoin; Coin newcoin;
newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.scriptPubKey = scriptPubKey;
newcoin.out.nValue = MAX_MONEY; newcoin.out.nValue = CConfidentialValue(MAX_MONEY);
if (prevOut.exists("amount")) { if (prevOut.exists("amount")) {
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); newcoin.out.nValue = CConfidentialValue(AmountFromValue(find_value(prevOut, "amount")));
} else if (prevOut.exists("amountcommitment")) {
// Segwit sigs require the amount commitment to be sighashed
uint256 asset_commit = uint256S(prevOut["amountcommitment"].get_str());
newcoin.out.nValue.vchCommitment = std::vector<unsigned char>(asset_commit.begin(), asset_commit.end());
} }
newcoin.nHeight = 1; newcoin.nHeight = 1;
view.AddCoin(out, std::move(newcoin), true); view.AddCoin(out, std::move(newcoin), true);
@ -896,9 +944,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
} }
const CScript& prevPubKey = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).scriptPubKey : coin.out.scriptPubKey; const CScript& prevPubKey = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).scriptPubKey : coin.out.scriptPubKey;
//TODO(rebase) CT const CConfidentialValue& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
//const CConfidentialValue& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
const CAmount& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
SignatureData sigdata = DataFromTransaction(mtx, i, coin.out); SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output: // Only sign SIGHASH_SINGLE if there's a corresponding output:
@ -909,7 +955,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
UpdateTransaction(mtx, i, sigdata); UpdateTransaction(mtx, i, sigdata);
// amount must be specified for valid segwit signature // amount must be specified for valid segwit signature
if (amount == MAX_MONEY && !inWitness.scriptWitness.IsNull()) { if (amount.IsExplicit() && amount.GetAmount() == MAX_MONEY && !mtx.witness.vtxinwit[i].scriptWitness.IsNull()) {
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString())); throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
} }
@ -963,7 +1009,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
" \"vout\":n, (numeric, required) The output number\n" " \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n"
" \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
" \"amount\": value (numeric, required) The amount spent\n" " \"amount\": value (numeric, required if non-confidential segwit output) The amount spent\n"
" \"amountcommitment\": \"hex\", (string, required if confidential segiwt output) The amount commitment spent\n"
" }\n" " }\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
@ -1336,6 +1383,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
UniValue tx_univ(UniValue::VOBJ); UniValue tx_univ(UniValue::VOBJ);
TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false); TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
result.pushKV("tx", tx_univ); result.pushKV("tx", tx_univ);
result.pushKV("fees", AmountMapToUniv(GetFeeMap(CTransaction(*psbtx.tx)), ""));
// Unknown data // Unknown data
UniValue unknowns(UniValue::VOBJ); UniValue unknowns(UniValue::VOBJ);
@ -1345,8 +1393,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
result.pushKV("unknown", unknowns); result.pushKV("unknown", unknowns);
// inputs // inputs
CAmount total_in = 0;
bool have_all_utxos = true;
UniValue inputs(UniValue::VARR); UniValue inputs(UniValue::VARR);
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
const PSBTInput& input = psbtx.inputs[i]; const PSBTInput& input = psbtx.inputs[i];
@ -1357,8 +1403,11 @@ UniValue decodepsbt(const JSONRPCRequest& request)
UniValue out(UniValue::VOBJ); UniValue out(UniValue::VOBJ);
out.pushKV("amount", ValueFromAmount(txout.nValue)); if (txout.nValue.IsExplicit()) {
total_in += txout.nValue; out.pushKV("amount", ValueFromAmount(txout.nValue.GetAmount()));
} else {
out.pushKV("amountcommitment", txout.nValue.GetHex());
}
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
ScriptToUniv(txout.scriptPubKey, o, true); ScriptToUniv(txout.scriptPubKey, o, true);
@ -1368,9 +1417,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
UniValue non_wit(UniValue::VOBJ); UniValue non_wit(UniValue::VOBJ);
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false); TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
in.pushKV("non_witness_utxo", non_wit); in.pushKV("non_witness_utxo", non_wit);
total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
} else {
have_all_utxos = false;
} }
// Partial sigs // Partial sigs
@ -1442,7 +1488,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
result.pushKV("inputs", inputs); result.pushKV("inputs", inputs);
// outputs // outputs
CAmount output_value = 0;
UniValue outputs(UniValue::VARR); UniValue outputs(UniValue::VARR);
for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) { for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
const PSBTOutput& output = psbtx.outputs[i]; const PSBTOutput& output = psbtx.outputs[i];
@ -1482,14 +1527,8 @@ UniValue decodepsbt(const JSONRPCRequest& request)
} }
outputs.push_back(out); outputs.push_back(out);
// Fee calculation
output_value += psbtx.tx->vout[i].nValue;
} }
result.pushKV("outputs", outputs); result.pushKV("outputs", outputs);
if (have_all_utxos) {
result.pushKV("fee", ValueFromAmount(total_in - output_value));
}
return result; return result;
} }
@ -1642,6 +1681,12 @@ UniValue createpsbt(const JSONRPCRequest& request)
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
"4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
"5. \"output_assets\" (strings, optional, default=bitcoin) A json object of assets to addresses\n"
" {\n"
" \"address\": \"hex\" \n"
" \"fee\": \"hex\" \n"
" ...\n"
" }\n"
"\nResult:\n" "\nResult:\n"
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
"\nExamples:\n" "\nExamples:\n"
@ -1657,7 +1702,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
}, true }, true
); );
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]); CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4]);
// Make a blank psbt // Make a blank psbt
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
@ -1745,12 +1790,160 @@ UniValue converttopsbt(const JSONRPCRequest& request)
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()); return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
} }
UniValue rawblindrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() < 5 || request.params.size() > 7))
throw std::runtime_error(
"rawblindrawtransaction \"hexstring\" [\"inputblinder\",...] [\"inputamount\",...] [\"inputasset\",...] [\"inputassetblinder\",...] ( totalblinder, ignoreblindfail )\n"
"\nConvert one or more outputs of a raw transaction into confidential ones.\n"
"Returns the hex-encoded raw transaction.\n"
"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"
"2. [ (array, required) An array with one entry per transaction input.\n"
" \"inputamountblinder\" (string, required) A hex-encoded blinding factor, one for each input.\n"
" Blinding factors can be found in the \"blinder\" output of listunspent.\n"
" ],\n"
"3. [ (array, required) An array with one entry per transaction input.\n"
" \"inputamount\" (numeric, required) An amount for each input.\n"
" ],\n"
"4. [ (array, required) An array with one entry per transaction input.\n"
" \"inputasset\" (string, required) A hex-encoded asset id, one for each input.\n"
" ],\n"
"5. [ (array, required) An array with one entry per transaction input.\n"
" \"inputassetblinder\" (string, required) A hex-encoded asset blinding factor, one for each input.\n"
" ],\n"
"6. \"totalblinder\" (string, optional) Ignored for now.\n"
"7. \"ignoreblindfail\"\" (bool, optional, default=true) Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs.\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
);
std::vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CMutableTransaction tx;
try {
ssData >> tx;
} catch (const std::exception &) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
UniValue inputBlinds = request.params[1].get_array();
UniValue inputAmounts = request.params[2].get_array();
UniValue inputAssets = request.params[3].get_array();
UniValue inputAssetBlinds = request.params[4].get_array();
bool fIgnoreBlindFail = true;
if (!request.params[6].isNull()) {
fIgnoreBlindFail = request.params[6].get_bool();
}
int n_blinded_ins = 0;
if (inputBlinds.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter: one (potentially empty) input blind for each input must be provided");
}
if (inputAmounts.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter: one (potentially empty) input blind for each input must be provided");
}
if (inputAssets.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter: one (potentially empty) input asset id for each input must be provided");
}
if (inputAssetBlinds.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter: one (potentially empty) input asset blind for each input must be provided");
}
std::vector<CAmount> input_amounts;
std::vector<uint256> input_blinds;
std::vector<uint256> input_asset_blinds;
std::vector<CAsset> input_assets;
std::vector<uint256> output_value_blinds;
std::vector<uint256> output_asset_blinds;
std::vector<CAsset> output_assets;
std::vector<CPubKey> output_pubkeys;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
if (!inputBlinds[nIn].isStr())
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of hex strings");
if (!inputAssetBlinds[nIn].isStr())
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of hex strings");
if (!inputAssets[nIn].isStr())
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset ids must be an array of hex strings");
std::string blind(inputBlinds[nIn].get_str());
std::string assetblind(inputAssetBlinds[nIn].get_str());
std::string asset(inputAssets[nIn].get_str());
if (!IsHex(blind) || blind.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of 32-byte hex-encoded strings");
if (!IsHex(assetblind) || assetblind.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
if (!IsHex(asset) || asset.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
input_blinds.push_back(uint256S(blind));
input_asset_blinds.push_back(uint256S(assetblind));
input_assets.push_back(CAsset(uint256S(asset)));
input_amounts.push_back(AmountFromValue(inputAmounts[nIn]));
if (!input_blinds.back().IsNull()) {
n_blinded_ins++;
}
}
RawFillBlinds(tx, output_value_blinds, output_asset_blinds, output_pubkeys);
// How many are we trying to blind?
int num_pubkeys = 0;
unsigned int keyIndex = -1;
for (unsigned int i = 0; i < output_pubkeys.size(); i++) {
const CPubKey& key = output_pubkeys[i];
if (key.IsValid()) {
num_pubkeys++;
keyIndex = i;
}
}
if (num_pubkeys == 0 && n_blinded_ins == 0) {
// Vacuous, just return the transaction
return EncodeHexTx(tx);
} else if (n_blinded_ins > 0 && num_pubkeys == 0) {
// No notion of wallet, cannot complete this blinding without passed-in pubkey
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to blind transaction: Add another output to blind in order to complete the blinding.");
} else if (n_blinded_ins == 0 && num_pubkeys == 1) {
if (fIgnoreBlindFail) {
// Just get rid of the ECDH key in the nonce field and return
tx.vout[keyIndex].nNonce.SetNull();
return EncodeHexTx(tx);
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to blind transaction: Add another output to blind in order to complete the blinding.");
}
}
int ret = BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_value_blinds, output_asset_blinds, output_pubkeys, std::vector<CKey>(), std::vector<CKey>(), tx);
if (ret != num_pubkeys) {
// 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, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?");
}
return EncodeHexTx(tx);
}
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable", "output_assets"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
@ -1763,6 +1956,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} }, { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} }, { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
{ "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, {"hexstring", "inputblinder", "inputamount", "inputasset", "inputassetblinder", "totalblinder", "ignoreblindfail"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },

View file

@ -13,6 +13,6 @@ class UniValue;
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType); UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */ /** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf); CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf, const UniValue& assets_in);
#endif // BITCOIN_RPC_RAWTRANSACTION_H #endif // BITCOIN_RPC_RAWTRANSACTION_H