mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
[BROKEN] Add CA wallet RPCs
This commit is contained in:
parent
b6397d6959
commit
a361dda7a3
3 changed files with 435 additions and 1 deletions
|
|
@ -174,9 +174,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||
{ "rawblindrawtransaction", 4, "inputassetblinder" },
|
||||
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
|
||||
{ "blindrawtransaction", 1, "assetcommitments" },
|
||||
{ "blindrawtransaction", 2, "ignoreblindfail" },
|
||||
{ "blindrawtransaction", 3, "blind_issuances" },
|
||||
{ "sendmany", 7 , "output_assets" },
|
||||
{ "sendmany", 8 , "ignoreblindfail" },
|
||||
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||
{ "importblindingkey", 2, "key_is_master"},
|
||||
{ "createrawtransaction", 4, "output_assets" },
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1333,3 +1333,113 @@ UniValue getwalletpakinfo(const JSONRPCRequest& request)
|
|||
ret.pushKV("address_lookahead", address_list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue importblindingkey(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"importblindingkey \"address\" \"blindinghex\"\n"
|
||||
"\nImports a private blinding key in hex for a CT address."
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The CT address\n"
|
||||
"2. \"hexkey\" (string, required) The blinding key in hex\n"
|
||||
"3. \"key_is_master\" (bool, optional, default=false) If the `hexkey` is a master blinding key. Note: wallets can only have one master blinding key at a time. Funds could be permanently lost if user doesn't know what they are doing. Recommended use is only for wallet recovery using this in conjunction with `sethdseed`.\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("importblindingkey", "\"my blinded CT address\" <blindinghex>")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
||||
if (!IsValidDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address.");
|
||||
}
|
||||
if (!IsBlindDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address is not confidential.");
|
||||
}
|
||||
|
||||
if (!IsHex(request.params[1].get_str())) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
|
||||
}
|
||||
std::vector<unsigned char> keydata = ParseHex(request.params[1].get_str());
|
||||
if (keydata.size() != 32) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
|
||||
}
|
||||
|
||||
bool key_is_master = false;
|
||||
if (!request.params[2].isNull()) {
|
||||
key_is_master = request.params[2].get_bool();
|
||||
}
|
||||
|
||||
CKey key;
|
||||
key.Set(keydata.begin(), keydata.end(), true);
|
||||
if (!key.IsValid() || key.GetPubKey() != GetDestinationBlindingKey(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address and key do not match");
|
||||
}
|
||||
|
||||
uint256 keyval;
|
||||
memcpy(keyval.begin(), &keydata[0], 32);
|
||||
if (key_is_master) {
|
||||
if (pwallet->SetMasterBlindingKey(keyval)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import master blinding key");
|
||||
}
|
||||
} else {
|
||||
if (!pwallet->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(dest)), keyval)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
|
||||
}
|
||||
}
|
||||
pwallet->MarkDirty();
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue dumpblindingkey(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
"dumpblindingkey \"address\"\n"
|
||||
"\nDumps the private blinding key for a CT address in hex."
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The CT address\n"
|
||||
"\nResult:\n"
|
||||
"\"blindingkey\" (string) The blinding key\n"
|
||||
"\nExample:\n"
|
||||
+ HelpExampleCli("dumpblindingkey", "\"my address\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
||||
if (!IsValidDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||
}
|
||||
if (!IsBlindDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
|
||||
}
|
||||
CScript script = GetScriptForDestination(dest);
|
||||
CKey key;
|
||||
key = pwallet->GetBlindingKey(&script);
|
||||
if (key.IsValid()) {
|
||||
CPubKey pubkey(key.GetPubKey());
|
||||
if (pubkey == GetDestinationBlindingKey(dest)) {
|
||||
return HexStr(key.begin(), key.end());
|
||||
}
|
||||
}
|
||||
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5334,11 +5334,328 @@ UniValue claimpegin(const JSONRPCRequest& request)
|
|||
return mtx.GetHash().GetHex();
|
||||
}
|
||||
|
||||
// Rewind the outputs to unblinded, and push placeholders for blinding info. Not exported.
|
||||
void FillBlinds(CWallet* pwallet, CMutableTransaction& tx, std::vector<uint256>& output_value_blinds, std::vector<uint256>& output_asset_blinds, std::vector<CPubKey>& output_pubkeys, std::vector<CKey>& asset_keys, std::vector<CKey>& token_keys) {
|
||||
|
||||
// Resize witness before doing anything
|
||||
tx.witness.vtxinwit.resize(tx.vin.size());
|
||||
tx.witness.vtxoutwit.resize(tx.vout.size());
|
||||
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); ++nOut) {
|
||||
CTxOut& out = tx.vout[nOut];
|
||||
if (out.nValue.IsExplicit()) {
|
||||
CPubKey pubkey(out.nNonce.vchCommitment);
|
||||
if (!pubkey.IsFullyValid()) {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
} else {
|
||||
output_pubkeys.push_back(pubkey);
|
||||
}
|
||||
output_value_blinds.push_back(uint256());
|
||||
output_asset_blinds.push_back(uint256());
|
||||
} else if (out.nValue.IsCommitment()) {
|
||||
CTxOutWitness* ptxoutwit = &tx.witness.vtxoutwit[nOut];
|
||||
uint256 blinding_factor;
|
||||
uint256 asset_blinding_factor;
|
||||
CAsset asset;
|
||||
CAmount amount;
|
||||
// This can only be used to recover things like change addresses and self-sends.
|
||||
if (UnblindConfidentialPair(pwallet->GetBlindingKey(&out.scriptPubKey), out.nValue, out.nAsset, out.nNonce, out.scriptPubKey, ptxoutwit->vchRangeproof, amount, blinding_factor, asset, asset_blinding_factor) != 0) {
|
||||
// Wipe out confidential info from output and output witness
|
||||
CScript scriptPubKey = tx.vout[nOut].scriptPubKey;
|
||||
CTxOut newOut(asset, amount, scriptPubKey);
|
||||
tx.vout[nOut] = newOut;
|
||||
ptxoutwit->SetNull();
|
||||
|
||||
// Mark for re-blinding with same key that deblinded it
|
||||
CPubKey pubkey(pwallet->GetBlindingKey(&out.scriptPubKey).GetPubKey());
|
||||
output_pubkeys.push_back(pubkey);
|
||||
output_value_blinds.push_back(uint256());
|
||||
output_asset_blinds.push_back(uint256());
|
||||
} else {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_value_blinds.push_back(uint256());
|
||||
output_asset_blinds.push_back(uint256());
|
||||
}
|
||||
} else {
|
||||
// Null or invalid, do nothing for that output
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_value_blinds.push_back(uint256());
|
||||
output_asset_blinds.push_back(uint256());
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out issuance blinding keys to be used directly as nonce for rangeproof
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); ++nIn) {
|
||||
CAssetIssuance& issuance = tx.vin[nIn].assetIssuance;
|
||||
if (issuance.IsNull()) {
|
||||
asset_keys.push_back(CKey());
|
||||
token_keys.push_back(CKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate underlying asset for use as blinding key script
|
||||
CAsset asset;
|
||||
// New issuance, compute the asset ids
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, tx.vin[nIn].prevout, issuance.assetEntropy);
|
||||
CalculateAsset(asset, entropy);
|
||||
}
|
||||
// Re-issuance
|
||||
else {
|
||||
// TODO Give option to skip blinding when the reissuance token was derived without blinding ability. For now we assume blinded
|
||||
// hashAssetIdentifier doubles as the entropy on reissuance
|
||||
CalculateAsset(asset, issuance.assetEntropy);
|
||||
}
|
||||
|
||||
// Special format for issuance blinding keys, unique for each transaction
|
||||
CScript blindingScript = CScript() << OP_RETURN << std::vector<unsigned char>(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++) {
|
||||
bool issuance_asset = (nPseudo == 0);
|
||||
std::vector<CKey>& issuance_blinding_keys = issuance_asset ? asset_keys : token_keys;
|
||||
CConfidentialValue& conf_value = issuance_asset ? issuance.nAmount : issuance.nInflationKeys;
|
||||
if (conf_value.IsCommitment()) {
|
||||
// Rangeproof must exist
|
||||
if (tx.witness.vtxinwit.size() <= nIn) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction issuance is already blinded but has no attached rangeproof.");
|
||||
}
|
||||
CTxInWitness& txinwit = tx.witness.vtxinwit[nIn];
|
||||
std::vector<unsigned char>& vchRangeproof = issuance_asset ? txinwit.vchIssuanceAmountRangeproof : txinwit.vchInflationKeysRangeproof;
|
||||
uint256 blinding_factor;
|
||||
uint256 asset_blinding_factor;
|
||||
CAmount amount;
|
||||
if (UnblindConfidentialPair(pwallet->GetBlindingKey(&blindingScript), conf_value, CConfidentialAsset(asset), CConfidentialNonce(), CScript(), vchRangeproof, amount, blinding_factor, asset, asset_blinding_factor) != 0) {
|
||||
// Wipe out confidential info from issuance
|
||||
vchRangeproof.clear();
|
||||
conf_value = CConfidentialValue(amount);
|
||||
// One key blinds both values, single key needed for issuance reveal
|
||||
issuance_blinding_keys.push_back(pwallet->GetBlindingKey(&blindingScript));
|
||||
} else {
|
||||
// If unable to unblind, leave it alone in next blinding step
|
||||
issuance_blinding_keys.push_back(CKey());
|
||||
}
|
||||
} else if (conf_value.IsExplicit()) {
|
||||
// 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
|
||||
issuance_blinding_keys.push_back(pwallet->GetBlindingKey(&blindingScript));
|
||||
} else {
|
||||
// Null or invalid, don't try anything but append an empty key
|
||||
issuance_blinding_keys.push_back(CKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UniValue blindrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (request.fHelp || (request.params.size() < 1 || request.params.size() > 5))
|
||||
throw std::runtime_error(
|
||||
"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"
|
||||
"This call may add an additional 0-value unspendable output in order to balance the blinders.\n"
|
||||
|
||||
"\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. \"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. \"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"
|
||||
);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
bool ignore_blind_fail = true;
|
||||
if (request.params.size() > 1) {
|
||||
ignore_blind_fail = request.params[1].get_bool();
|
||||
}
|
||||
|
||||
std::vector<std::vector<unsigned char> > auxiliary_generators;
|
||||
if (request.params.size() > 2) {
|
||||
UniValue assetCommitments = request.params[2].get_array();
|
||||
if (assetCommitments.size() != 0 && assetCommitments.size() != tx.vin.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Asset commitment array must have exactly as many entries as transaction inputs.");
|
||||
}
|
||||
for (size_t nIn = 0; nIn < assetCommitments.size(); nIn++) {
|
||||
if (assetCommitments[nIn].isStr()) {
|
||||
std::string assetcommitment = assetCommitments[nIn].get_str();
|
||||
if (IsHex(assetcommitment) && assetcommitment.size() == 66) {
|
||||
auxiliary_generators.push_back(ParseHex(assetcommitment));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Asset commitments must be a hex encoded string of length 66.");
|
||||
}
|
||||
}
|
||||
|
||||
bool blind_issuances = request.params[3].isNull() || request.params[3].get_bool();
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<CAmount> input_amounts;
|
||||
int n_blinded_ins = 0;
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); ++nIn) {
|
||||
COutPoint prevout = tx.vin[nIn].prevout;
|
||||
|
||||
std::map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.find(prevout.hash);
|
||||
if (it == pwallet->mapWallet.end()) {
|
||||
// For inputs we don't own input assetcommitments for the surjection must be supplied
|
||||
if (auxiliary_generators.size() > 0) {
|
||||
input_blinds.push_back(uint256());
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_assets.push_back(CAsset());
|
||||
input_amounts.push_back(-1);
|
||||
continue;
|
||||
}
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter: transaction spends from non-wallet output and no assetcommitment list was given.");
|
||||
}
|
||||
|
||||
if (prevout.n >= it->second.tx->vout.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter: transaction spends non-existing output");
|
||||
}
|
||||
input_blinds.push_back(it->second.GetOutputAmountBlindingFactor(prevout.n));
|
||||
input_asset_blinds.push_back(it->second.GetOutputAssetBlindingFactor(prevout.n));
|
||||
input_assets.push_back(it->second.GetOutputAsset(prevout.n));
|
||||
input_amounts.push_back(it->second.GetOutputValueOut(prevout.n));
|
||||
if (it->second.tx->vout[prevout.n].nValue.IsCommitment()) {
|
||||
n_blinded_ins += 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint256> output_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
std::vector<CKey> asset_keys;
|
||||
std::vector<CKey> token_keys;
|
||||
// This fills out issuance blinding data for you from the wallet itself
|
||||
FillBlinds(pwallet, tx, 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 num_pubkeys = 0;
|
||||
unsigned int key_index = 0;
|
||||
for (unsigned int i = 0; i < output_pubkeys.size(); i++) {
|
||||
const CPubKey& key = output_pubkeys[i];
|
||||
if (key.IsValid()) {
|
||||
num_pubkeys++;
|
||||
key_index = i;
|
||||
}
|
||||
}
|
||||
for (const CKey& key : asset_keys) {
|
||||
if (key.IsValid()) num_pubkeys++;
|
||||
}
|
||||
for (const CKey& key : token_keys) {
|
||||
if (key.IsValid()) num_pubkeys++;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Blinded inputs need to balanced with something to be valid, make a dummy.
|
||||
CTxOut newTxOut(tx.vout.back().nAsset.GetAsset(), 0, CScript() << OP_RETURN);
|
||||
tx.vout.push_back(newTxOut);
|
||||
num_pubkeys++;
|
||||
output_pubkeys.push_back(pwallet->GetBlindingPubKey(newTxOut.scriptPubKey));
|
||||
} else if (n_blinded_ins == 0 && num_pubkeys == 1) {
|
||||
if (ignore_blind_fail) {
|
||||
// Just get rid of the ECDH key in the nonce field and return
|
||||
tx.vout[key_index].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.");
|
||||
}
|
||||
}
|
||||
|
||||
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)) != 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);
|
||||
}
|
||||
|
||||
static UniValue unblindrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
"unblindrawtransaction \"hex\"\n"
|
||||
"\nRecovers unblinded transaction outputs from blinded outputs and issuance inputs when possible using wallet's known blinding keys, and strips related witness data.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"hex\" (string, required) The hex string of the raw transaction\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"hex\": \"value\", (string) The resulting unblinded raw transaction (hex-encoded string)\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("unblindrawtransaction", "\"blindedtransactionhex\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
CMutableTransaction tx;
|
||||
if (!DecodeHexTx(tx, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
std::vector<uint256> output_value_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
std::vector<CKey> asset_keys;
|
||||
std::vector<CKey> token_keys;
|
||||
FillBlinds(pwallet, tx, output_value_blinds, output_asset_blinds, output_pubkeys, asset_keys, token_keys);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("hex", EncodeHexTx(tx));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// END ELEMENTS commands
|
||||
//
|
||||
|
||||
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importprivkey(const JSONRPCRequest& request);
|
||||
UniValue importaddress(const JSONRPCRequest& request);
|
||||
UniValue importpubkey(const JSONRPCRequest& request);
|
||||
|
|
@ -5413,10 +5730,13 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "getpeginaddress", &getpeginaddress, {} },
|
||||
{ "wallet", "claimpegin", &claimpegin, {"bitcoin_tx", "txoutproof", "claim_script"} },
|
||||
{ "wallet", "createrawpegin", &createrawpegin, {"bitcoin_tx", "txoutproof", "claim_script"} },
|
||||
{ "wallet", "blindrawtransaction", &blindrawtransaction, {"hexstring", "ignoreblindfail", "assetcommitment", "blind_issuances", "totalblinder"} },
|
||||
{ "wallet", "unblindrawtransaction", &unblindrawtransaction, {"hex"} },
|
||||
{ "wallet", "sendtomainchain", &sendtomainchain, {"address", "amount", "subtractfeefromamount"} },
|
||||
{ "wallet", "initpegoutwallet", &initpegoutwallet, {"bitcoin_descriptor", "bip32_counter", "liquid_pak"} },
|
||||
{ "wallet", "getwalletpakinfo", &getwalletpakinfo, {} },
|
||||
|
||||
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey", "key_is_master"}},
|
||||
{ "wallet", "dumpblindingkey", &dumpblindingkey, {"address"}},
|
||||
{ "wallet", "signblock", &signblock, {"blockhex"}},
|
||||
};
|
||||
// clang-format on
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue