Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/rawtransaction.cpp
Show First 20 Lines • Show All 1,762 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | ||||
input.utxo = view.AccessCoin(psbtx.tx->vin[i].prevout).GetTxOut(); | input.utxo = view.AccessCoin(psbtx.tx->vin[i].prevout).GetTxOut(); | ||||
} | } | ||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | ||||
ssTx << psbtx; | ssTx << psbtx; | ||||
return EncodeBase64((uint8_t *)ssTx.data(), ssTx.size()); | return EncodeBase64((uint8_t *)ssTx.data(), ssTx.size()); | ||||
} | } | ||||
UniValue joinpsbts(const Config &config, const JSONRPCRequest &request) { | |||||
if (request.fHelp || request.params.size() != 1) { | |||||
throw std::runtime_error(RPCHelpMan{ | |||||
"joinpsbts", | |||||
"\nJoins multiple distinct PSBTs with different inputs and outputs " | |||||
"into one PSBT with inputs and outputs from all of the PSBTs\n" | |||||
"No input in any of the PSBTs can be in more than one of the " | |||||
"PSBTs.\n", | |||||
{{"txs", | |||||
RPCArg::Type::ARR, | |||||
/* opt */ false, | |||||
/* default_val */ "", | |||||
"A json array of base64 strings of partially signed transactions", | |||||
{{"psbt", RPCArg::Type::STR, /* opt */ false, | |||||
/* default_val */ "", "A base64 string of a PSBT"}}}}, | |||||
RPCResult{" \"psbt\" (string) The base64-encoded " | |||||
"partially signed transaction\n"}, | |||||
RPCExamples{HelpExampleCli("joinpsbts", "\"psbt\"")}} | |||||
.ToString()); | |||||
} | |||||
RPCTypeCheck(request.params, {UniValue::VARR}, true); | |||||
// Unserialize the transactions | |||||
std::vector<PartiallySignedTransaction> psbtxs; | |||||
UniValue txs = request.params[0].get_array(); | |||||
if (txs.size() <= 1) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"At least two PSBTs are required to join PSBTs."); | |||||
} | |||||
int32_t best_version = 1; | |||||
uint32_t best_locktime = 0xffffffff; | |||||
for (size_t i = 0; i < txs.size(); ++i) { | |||||
PartiallySignedTransaction psbtx; | |||||
std::string error; | |||||
if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) { | |||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | |||||
strprintf("TX decode failed %s", error)); | |||||
} | |||||
psbtxs.push_back(psbtx); | |||||
// Choose the highest version number | |||||
if (psbtx.tx->nVersion > best_version) { | |||||
best_version = psbtx.tx->nVersion; | |||||
} | |||||
// Choose the lowest lock time | |||||
if (psbtx.tx->nLockTime < best_locktime) { | |||||
best_locktime = psbtx.tx->nLockTime; | |||||
} | |||||
} | |||||
// Create a blank psbt where everything will be added | |||||
PartiallySignedTransaction merged_psbt; | |||||
merged_psbt.tx = CMutableTransaction(); | |||||
merged_psbt.tx->nVersion = best_version; | |||||
merged_psbt.tx->nLockTime = best_locktime; | |||||
// Merge | |||||
for (auto &psbt : psbtxs) { | |||||
for (size_t i = 0; i < psbt.tx->vin.size(); ++i) { | |||||
if (!merged_psbt.AddInput(psbt.tx->vin[i], psbt.inputs[i])) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
strprintf( | |||||
"Input %s:%d exists in multiple PSBTs", | |||||
psbt.tx->vin[i].prevout.GetTxId().ToString().c_str(), | |||||
psbt.tx->vin[i].prevout.GetN())); | |||||
} | |||||
} | |||||
for (size_t i = 0; i < psbt.tx->vout.size(); ++i) { | |||||
merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]); | |||||
} | |||||
merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); | |||||
} | |||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | |||||
ssTx << merged_psbt; | |||||
return EncodeBase64((uint8_t *)ssTx.data(), ssTx.size()); | |||||
} | |||||
// 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"} }, | { "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} }, | ||||
{ "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, | { "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} }, | ||||
{ "rawtransactions", "decodescript", decodescript, {"hexstring"} }, | { "rawtransactions", "decodescript", decodescript, {"hexstring"} }, | ||||
{ "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} }, | { "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} }, | ||||
{ "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} }, | { "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} }, | ||||
{ "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, | { "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, | ||||
{ "rawtransactions", "testmempoolaccept", testmempoolaccept, {"rawtxs","allowhighfees"} }, | { "rawtransactions", "testmempoolaccept", testmempoolaccept, {"rawtxs","allowhighfees"} }, | ||||
{ "rawtransactions", "decodepsbt", decodepsbt, {"psbt"} }, | { "rawtransactions", "decodepsbt", decodepsbt, {"psbt"} }, | ||||
{ "rawtransactions", "combinepsbt", combinepsbt, {"txs"} }, | { "rawtransactions", "combinepsbt", combinepsbt, {"txs"} }, | ||||
{ "rawtransactions", "finalizepsbt", finalizepsbt, {"psbt", "extract"} }, | { "rawtransactions", "finalizepsbt", finalizepsbt, {"psbt", "extract"} }, | ||||
{ "rawtransactions", "createpsbt", createpsbt, {"inputs","outputs","locktime"} }, | { "rawtransactions", "createpsbt", createpsbt, {"inputs","outputs","locktime"} }, | ||||
{ "rawtransactions", "converttopsbt", converttopsbt, {"hexstring","permitsigdata"} }, | { "rawtransactions", "converttopsbt", converttopsbt, {"hexstring","permitsigdata"} }, | ||||
{ "rawtransactions", "utxoupdatepsbt", utxoupdatepsbt, {"psbt"} }, | { "rawtransactions", "utxoupdatepsbt", utxoupdatepsbt, {"psbt"} }, | ||||
{ "rawtransactions", "joinpsbts", joinpsbts, {"txs"} }, | |||||
{ "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} }, | { "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} }, | ||||
{ "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} }, | { "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} }, | ||||
}; | }; | ||||
// clang-format on | // clang-format on | ||||
void RegisterRawTransactionRPCCommands(CRPCTable &t) { | void RegisterRawTransactionRPCCommands(CRPCTable &t) { | ||||
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { | ||||
t.appendCommand(commands[vcidx].name, &commands[vcidx]); | t.appendCommand(commands[vcidx].name, &commands[vcidx]); | ||||
} | } | ||||
} | } |