mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Merge 6b3927f79a into merged_master (Bitcoin PR bitcoin/bitcoin#28848)
This commit is contained in:
commit
87ebe0f079
4 changed files with 80 additions and 33 deletions
|
|
@ -829,7 +829,7 @@ static RPCHelpMan submitpackage()
|
|||
return RPCHelpMan{"submitpackage",
|
||||
"Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
|
||||
"The package must consist of a child with its parents, and none of the parents may depend on one another.\n"
|
||||
"The package will be validated according to consensus and mempool policy rules. If all transactions pass, they will be accepted to mempool.\n"
|
||||
"The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
|
||||
"This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
|
||||
"Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
|
||||
,
|
||||
|
|
@ -843,19 +843,21 @@ static RPCHelpMan submitpackage()
|
|||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
|
||||
{RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by wtxid",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
|
||||
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
|
||||
{RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."},
|
||||
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141."},
|
||||
{RPCResult::Type::OBJ, "fees", "Transaction fees", {
|
||||
{RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
|
||||
{RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
|
||||
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/true, "if the transaction was not already in the mempool, the effective feerate in " + CURRENCY_UNIT + " per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction."},
|
||||
{RPCResult::Type::ARR, "effective-includes", /*optional=*/true, "if effective-feerate is provided, the wtxids of the transactions whose fees and vsizes are included in effective-feerate.",
|
||||
{{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::STR, "error", /*optional=*/true, "The transaction error string, if it was rejected by the mempool"},
|
||||
}}
|
||||
}},
|
||||
{RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
|
||||
|
|
@ -895,57 +897,77 @@ static RPCHelpMan submitpackage()
|
|||
Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
|
||||
const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false));
|
||||
|
||||
// First catch any errors.
|
||||
std::string package_msg = "success";
|
||||
|
||||
// First catch package-wide errors, continue if we can
|
||||
switch(package_result.m_state.GetResult()) {
|
||||
case PackageValidationResult::PCKG_RESULT_UNSET: break;
|
||||
case PackageValidationResult::PCKG_POLICY:
|
||||
case PackageValidationResult::PCKG_RESULT_UNSET:
|
||||
{
|
||||
throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE,
|
||||
package_result.m_state.GetRejectReason());
|
||||
// Belt-and-suspenders check; everything should be successful here
|
||||
CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size());
|
||||
for (const auto& tx : txns) {
|
||||
CHECK_NONFATAL(mempool.exists(GenTxid::Txid(tx->GetHash())));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PackageValidationResult::PCKG_MEMPOOL_ERROR:
|
||||
{
|
||||
// This only happens with internal bug; user should stop and report
|
||||
throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
|
||||
package_result.m_state.GetRejectReason());
|
||||
}
|
||||
case PackageValidationResult::PCKG_POLICY:
|
||||
case PackageValidationResult::PCKG_TX:
|
||||
{
|
||||
for (const auto& tx : txns) {
|
||||
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
|
||||
if (it != package_result.m_tx_results.end() && it->second.m_state.IsInvalid()) {
|
||||
throw JSONRPCTransactionError(TransactionError::MEMPOOL_REJECTED,
|
||||
strprintf("%s failed: %s", tx->GetHash().ToString(), it->second.m_state.GetRejectReason()));
|
||||
}
|
||||
}
|
||||
// If a PCKG_TX error was returned, there must have been an invalid transaction.
|
||||
NONFATAL_UNREACHABLE();
|
||||
// Package-wide error we want to return, but we also want to return individual responses
|
||||
package_msg = package_result.m_state.GetRejectReason();
|
||||
CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size() ||
|
||||
package_result.m_tx_results.empty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_broadcast{0};
|
||||
for (const auto& tx : txns) {
|
||||
// We don't want to re-submit the txn for validation in BroadcastTransaction
|
||||
if (!mempool.exists(GenTxid::Txid(tx->GetHash()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We do not expect an error here; we are only broadcasting things already/still in mempool
|
||||
std::string err_string;
|
||||
const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true);
|
||||
if (err != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(err,
|
||||
strprintf("transaction broadcast failed: %s (all transactions were submitted, %d transactions were broadcast successfully)",
|
||||
strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
|
||||
err_string, num_broadcast));
|
||||
}
|
||||
num_broadcast++;
|
||||
}
|
||||
|
||||
UniValue rpc_result{UniValue::VOBJ};
|
||||
rpc_result.pushKV("package_msg", package_msg);
|
||||
UniValue tx_result_map{UniValue::VOBJ};
|
||||
std::set<uint256> replaced_txids;
|
||||
for (const auto& tx : txns) {
|
||||
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
|
||||
CHECK_NONFATAL(it != package_result.m_tx_results.end());
|
||||
UniValue result_inner{UniValue::VOBJ};
|
||||
result_inner.pushKV("txid", tx->GetHash().GetHex());
|
||||
const auto& tx_result = it->second;
|
||||
if (it->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS) {
|
||||
result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
|
||||
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
|
||||
if (it == package_result.m_tx_results.end()) {
|
||||
// No results, report error and continue
|
||||
result_inner.pushKV("error", "unevaluated");
|
||||
continue;
|
||||
}
|
||||
if (it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
|
||||
it->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY) {
|
||||
const auto& tx_result = it->second;
|
||||
switch(it->second.m_result_type) {
|
||||
case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
|
||||
result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
|
||||
break;
|
||||
case MempoolAcceptResult::ResultType::INVALID:
|
||||
result_inner.pushKV("error", it->second.m_state.ToString());
|
||||
break;
|
||||
case MempoolAcceptResult::ResultType::VALID:
|
||||
case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
|
||||
result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
|
||||
UniValue fees(UniValue::VOBJ);
|
||||
fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
|
||||
|
|
@ -966,6 +988,7 @@ static RPCHelpMan submitpackage()
|
|||
replaced_txids.insert(ptx->GetHash());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), result_inner);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue