Merge 9d31e94bf584e12269683b74b1c96c7ac883a760 into db2c57ae9eebdb75c58cd165ac929919969c19a9

This commit is contained in:
Adam Andrews 2025-03-17 10:37:25 +01:00 committed by GitHub
commit 320a553f4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 4 deletions

View File

@ -639,16 +639,37 @@ static RPCHelpMan combinerawtransaction()
{
UniValue txs = request.params[0].get_array();
// Can't merge < 2 items
if (txs.size() < 2) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions");
}
std::vector<CMutableTransaction> txVariants(txs.size());
for (unsigned int idx = 0; idx < txs.size(); idx++) {
for (unsigned int idx{0}; idx < txs.size(); ++idx) {
if (!DecodeHexTx(txVariants[idx], txs[idx].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d. Make sure the tx has at least one input.", idx));
}
}
if (txVariants.empty()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions");
{ // Test Tx relation for mergeability. This involves converting each tx to PSBT format (stripping sigscripts/witnesses) and ensuring txid is consistent.
std::vector<CMutableTransaction> txVariantsCopy(txVariants);
Txid txid{};
for (unsigned int k{0}; k < txVariantsCopy.size(); ++k) {
// Remove all scriptSigs and scriptWitnesses from inputs
for (CTxIn& input : txVariantsCopy[k].vin) {
input.scriptSig.clear();
input.scriptWitness.SetNull();
}
if (k == 0) {
txid = txVariantsCopy[k].GetHash();
} else {
if (txid != txVariantsCopy[k].GetHash()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Transaction %d not compatible (different transactions)", k));
}
}
}
}
// mergedTx will end up with all the signatures; it
@ -678,7 +699,7 @@ static RPCHelpMan combinerawtransaction()
// transaction to avoid rehashing.
const CTransaction txConst(mergedTx);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
for (unsigned int i{0}; i < mergedTx.vin.size(); ++i) {
CTxIn& txin = mergedTx.vin[i];
const Coin& coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {

View File

@ -199,8 +199,18 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
assert_equal(rawtx3["complete"], False)
assert_raises_rpc_error(-22, "TX decode failed", node2.combinerawtransaction, [rawtx2['hex'], rawtx3['hex'] + "00"])
assert_raises_rpc_error(-22, "Missing transactions", node2.combinerawtransaction, [])
assert_raises_rpc_error(-22, "Missing transactions", node2.combinerawtransaction, [rawtx2['hex']])
combined_rawtx = node2.combinerawtransaction([rawtx2["hex"], rawtx3["hex"]])
# transactions are not related
UNRELATED_TXID = "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000"
unrelatedTx = self.nodes[2].createrawtransaction(inputs=[{'txid': UNRELATED_TXID, 'vout': 9}], outputs=[{out_addr: 99}])
assert_raises_rpc_error(-8, "Transaction 1 not compatible (different transactions)", self.nodes[0].combinerawtransaction, [rawtx2['hex'], unrelatedTx])
# Accept duplicate transactions in combinerawtransaction
dupeMergedTx = self.nodes[0].combinerawtransaction([rawtx2['hex'], rawtx2['hex']])
assert_equal(rawtx2['hex'], dupeMergedTx)
tx = node0.sendrawtransaction(combined_rawtx, 0)
blk = self.generate(node0, 1)[0]
assert tx in node0.getblock(blk)["tx"]