Support 'blockchain.transaction.broadcast_package' with verbose=false
What changed, and why it matters
This commit adds support for a new optional parameter on an existing Electrum RPC method that broadcasts groups of Bitcoin transactions. When the new verbose=false option is used, the server now returns a smaller, simplified response instead of the full raw reply from Bitcoin Core. There is no obvious security bug in the change itself; it is a feature completion patch.
Review the untagged BroadcastArgs deserialization for type confusion and ensure submitpackage response schema matches all supported Bitcoin Core versions. Consider adding tests for malformed responses and large package sizes.
Security signals we found
Untagged enum deserialization can produce ambiguous parsing if a future caller passes a tuple whose second element is not a bool
Response parsing assumes specific JSON shape from daemon.submitpackage; malformed responses now raise an error instead of being passed through
No new authentication, rate-limiting, or size checks on the transaction package are introduced
Evidence from the diff
The diff extends transaction_broadcast_package to accept either (Vec
Changed components
src/electrum.rstransaction_broadcast_package RPC handlerBroadcastArgs parameter parsingInspect captured patch +50 / −5
diff --git a/src/electrum.rs b/src/electrum.rs
index 31547a5..a4df772 100644
--- a/src/electrum.rs
+++ b/src/electrum.rs
@@ -78,6 +78,29 @@ impl From<&TxGetArgs> for (Txid, bool) {
}
}
+#[derive(Deserialize)]
+#[serde(untagged)]
+enum BroadcastArgs {
+ Package((Vec<String>,)),
+ PackageVerbose((Vec<String>, bool)),
+}
+
+impl BroadcastArgs {
+ fn txs(&self) -> &[String] {
+ match self {
+ BroadcastArgs::Package((txs,)) => txs,
+ BroadcastArgs::PackageVerbose((txs, _)) => txs,
+ }
+ }
+
+ fn verbose(&self) -> bool {
+ match self {
+ BroadcastArgs::Package(_) => false,
+ BroadcastArgs::PackageVerbose((_, verbose)) => *verbose,
+ }
+ }
+}
+
enum StandardError {
ParseError,
InvalidRequest,
@@ -363,12 +386,34 @@ impl Rpc {
Ok(json!(txid))
}
- fn transaction_broadcast_package(&self, (txs_hex,): &(Vec<String>,)) -> Result<Value> {
- let txs = txs_hex
+ fn transaction_broadcast_package(&self, args: &BroadcastArgs) -> Result<Value> {
+ let txs: Vec<Transaction> = args
+ .txs()
.iter()
.map(|s| tx_from_hex(s))
- .collect::<Result<Vec<_>>>()?;
- self.daemon.submitpackage(&txs)
+ .collect::<Result<_>>()?;
+ let response = self.daemon.submitpackage(&txs)?;
+ if args.verbose() {
+ return Ok(response);
+ }
+ let build_result = || -> Option<Value> {
+ let success = response.get("package_msg")? == &json!("success");
+
+ let mut errors = vec![];
+ for tx in response.get("tx-results")?.as_object()?.values() {
+ let tx_obj = tx.as_object()?;
+ if let Some(error) = tx_obj.get("error") {
+ let txid = tx_obj.get("txid");
+ errors.push(json!({"error": error, "txid": txid}));
+ }
+ }
+ Some(if errors.is_empty() {
+ json!({"success": success})
+ } else {
+ json!({"success": success, "errors": errors})
+ })
+ };
+ build_result().ok_or(anyhow!("Unexpected `submitpackage` response"))
}
fn transaction_get(&self, args: &TxGetArgs) -> Result<Value> {
@@ -587,7 +632,7 @@ enum Params {
BlockHeader((usize,)),
BlockHeaders((usize, usize)),
TransactionBroadcast((String,)),
- TransactionBroadcastPackage((Vec<String>,)),
+ TransactionBroadcastPackage(BroadcastArgs),
Donation,
EstimateFee((u16,)),
Features,
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.