Skip to content

Commit 5cc40ff

Browse files
authored
Merge pull request #1239 from smolcars/rpc/sendraw-allowhighfees
rpc: let LND and btcwallet broadcast through sendrawtransaction
2 parents 6e6dc9a + 9841269 commit 5cc40ff

6 files changed

Lines changed: 42 additions & 8 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fixed
2+
3+
- **LND and other btcd `rpcclient` users can broadcast.** That client reads
4+
`/rbitcoin:` as Core before 0.19 and sends `sendrawtransaction` a boolean
5+
`allowhighfees`, which was `-3 maxfeerate is not a number or string`.
6+
`true` now lifts the fee cap and `false` keeps the default 10000 sat/vB.

‎crates/rbitcoin-rpc/src/methods/mempool.rs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,13 @@ pub const MAX_BURN_MSG: &str =
646646
pub fn sendrawtransaction(ctx: &RpcContext, params: &RpcParams) -> Result<Value, Value> {
647647
params.reject_unknown(&["hexstring", "maxfeerate", "maxburnamount"])?;
648648
let hex = params.req_str(0, "hexstring")?;
649-
let max_feerate = opt_maxfeerate_sat_vb(params, 1)?;
649+
// Pre-0.19 `allowhighfees`. btcd rpcclient (LND, btcwallet) sends it
650+
// because `/rbitcoin:` sorts below its `0.19.0` subversion check.
651+
let max_feerate = match params.get(1, "maxfeerate") {
652+
Some(Value::Bool(true)) => 0,
653+
Some(Value::Bool(false)) => DEFAULT_MAX_RAW_TX_FEE_SAT_VB,
654+
_ => opt_maxfeerate_sat_vb(params, 1)?,
655+
};
650656
let max_burn = opt_maxburn_sat(params, 2)?;
651657
let tx = decode_tx_hex(hex)?;
652658
let mp = ctx

‎crates/rbitcoin-rpc/src/methods/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ pub const NAMED_HELP: &[(&str, &str)] = &[
862862
"sendrawtransaction",
863863
"sendrawtransaction hexstring (maxfeerate) (maxburnamount)\n\
864864
RPC submit only. Default maxfeerate 10000 sat/vB (0 unlimited; >=100000 sat/vB is a parameter error). \
865+
A pre-0.19 boolean allowhighfees there: true is unlimited, false the default. \
865866
Default maxburnamount 0. P2P relay is not capped.",
866867
),
867868
(

‎crates/rbitcoin-rpc/tests/methods/mod.rs‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,11 +2404,25 @@ fn pin_sendraw_maxfeerate_at_default_and_one_sat_over(ctx: &RpcContext, at: u32,
24042404
cb - max_fee - 1,
24052405
ScriptBuf::from_bytes(vec![0x51]),
24062406
);
2407-
let e = dispatch(ctx, "sendrawtransaction", vec![json!(over_hex)]).unwrap_err();
2408-
assert_eq!(e["code"], ERR_VERIFY_ERROR, "{e}");
2409-
assert_eq!(
2410-
e["message"], "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
2411-
"{e}"
2407+
// Pre-0.19 `allowhighfees`, which btcd rpcclient sends for a `/rbitcoin:`
2408+
// subversion: `false` keeps the default cap, `true` lifts it.
2409+
for params in [vec![json!(over_hex)], vec![json!(over_hex), json!(false)]] {
2410+
let e = dispatch(ctx, "sendrawtransaction", params).unwrap_err();
2411+
assert_eq!(e["code"], ERR_VERIFY_ERROR, "{e}");
2412+
assert_eq!(
2413+
e["message"], "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
2414+
"{e}"
2415+
);
2416+
}
2417+
let ok = dispatch(
2418+
ctx,
2419+
"sendrawtransaction",
2420+
vec![json!(over_hex), json!(true)],
2421+
)
2422+
.unwrap();
2423+
assert!(
2424+
ok.as_str().is_some(),
2425+
"allowhighfees true must lift the cap: {ok}"
24122426
);
24132427
}
24142428

@@ -2427,7 +2441,7 @@ fn pin_maxfeerate_json_shapes(ctx: &RpcContext) {
24272441
"maxfeerate must be an integer sat/vB",
24282442
),
24292443
(
2430-
json!(true),
2444+
json!({}),
24312445
ERR_TYPE_ERROR,
24322446
"maxfeerate is not a number or string",
24332447
),

‎docs/lightning.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,13 @@ when that integer is at least `150000`, which it is. Raising the integer
243243
toward a current Core release would advertise wallet RPC that this node
244244
does not have. See [`rpc.md`](./rpc.md#getnetworkinfoversion).
245245

246+
LND reaches this node through btcd `rpcclient`, which string-compares
247+
`subversion` after trimming `/Satoshi:`. `/rbitcoin:…/` sorts below
248+
`0.19.0`, so the client speaks the pre-0.19 dialect:
249+
`sendrawtransaction` gets the boolean `allowhighfees`, which this node
250+
accepts. The client also refuses to call `testmempoolaccept`, so LND
251+
skips that pre-check and publishes directly.
252+
246253
`estimatesmartfee` accepts `estimate_mode` `economical` and `conservative`
247254
and returns the same 10-minute feerate either way. `initialblockdownload`
248255
means relay is inhibited (`--min-chain-work` / `--max-tip-age`), not

‎docs/rpc.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ still wait for durable SH when shindex is on.
144144
| `decoderawtransaction` | All networks. Decode hex. Optional `iswitness`: `false` refuses a BIP141 marker (`-22 TX decode failed`). Extra trailing bytes also `-22`. `scriptSig.asm` is rust-bitcoin, not Core `ScriptToAsmStr` sighash suffixes. Coinbase vin is Core-shaped `{coinbase, sequence}` (plus rbitcoin's `n`), with no `txid`/`vout`. |
145145
| `decodescript` | All networks. `asm`, Core-style `type`, `hex`, `address` when `Address::from_script` succeeds, and `desc`. `desc` is Core's inference with no keystore: `pk`, `multi`, `rawtr`, `addr`, else `raw`, with the BIP380 checksum; `pkh` / `wpkh` / `sh` / `wsh` / `tr` never appear. No `p2sh` wrap, `segwit` wrap, or miniscript. |
146146
| `validateaddress` | All networks. Valid: `isvalid`, `address`, `scriptPubKey`, `isscript`, `iswitness`, plus `witness_version` / `witness_program` when segwit except pay-to-anchor. P2TR and P2A set `isscript` true. Invalid (parse fail or wrong chain): `{isvalid: false}` only — no `error` / `error_locations`. |
147-
| `sendrawtransaction` / `testmempoolaccept` | `sendrawtransaction` is live accept (RPC still admits under `-blocksonly`; serving-only refuse is IBD / tip-not-ready `relay disabled`). `testmempoolaccept` / `submitpackage` `vsize` is sigop-adjusted. `testmempoolaccept` is dry-run (`MempoolHub::test_accept`: prepare + scripts + RBF/cluster checks, no commit / announce / RBF eviction / orphan park). Invalid hex / decode is `-22 TX decode failed` (same as `decoderawtransaction`). Confirmed on the active chain is `txn-already-known` (tip + confirmed-strong); live mempool duplicate is `txn-already-in-mempool`; archive-only after invalidate is not already-known. RPC-submit only: `maxfeerate` is **sat/vB** (default **10000**; `0` unlimited; `>= 100000` is `-8`). sendraw over-cap is `-25` configured-max text; `testmempoolaccept` reject-reason stays `max-fee-exceeded`. `maxburnamount` default **0** (valued unspendable / OP_RETURN outs). nVersion outside 1/2 is `"version"`. P2P `accept_tx` does not apply these caps. Multi-tx `testmempoolaccept` is sequential: earlier `allowed: true` rows stay (no abort-class blanking). Core functional tests still speak BTC/kvB via `scripts/core-functional/rpc_proxy.py`. |
147+
| `sendrawtransaction` / `testmempoolaccept` | `sendrawtransaction` is live accept (RPC still admits under `-blocksonly`; serving-only refuse is IBD / tip-not-ready `relay disabled`). `testmempoolaccept` / `submitpackage` `vsize` is sigop-adjusted. `testmempoolaccept` is dry-run (`MempoolHub::test_accept`: prepare + scripts + RBF/cluster checks, no commit / announce / RBF eviction / orphan park). Invalid hex / decode is `-22 TX decode failed` (same as `decoderawtransaction`). Confirmed on the active chain is `txn-already-known` (tip + confirmed-strong); live mempool duplicate is `txn-already-in-mempool`; archive-only after invalidate is not already-known. RPC-submit only: `maxfeerate` is **sat/vB** (default **10000**; `0` unlimited; `>= 100000` is `-8`). `sendrawtransaction` also takes the pre-0.19 boolean `allowhighfees` in that slot (`true` unlimited, `false` the default); btcd `rpcclient` sends it because `/rbitcoin:` sorts below its `0.19.0` subversion check. sendraw over-cap is `-25` configured-max text; `testmempoolaccept` reject-reason stays `max-fee-exceeded`. `maxburnamount` default **0** (valued unspendable / OP_RETURN outs). nVersion outside 1/2 is `"version"`. P2P `accept_tx` does not apply these caps. Multi-tx `testmempoolaccept` is sequential: earlier `allowed: true` rows stay (no abort-class blanking). Core functional tests still speak BTC/kvB via `scripts/core-functional/rpc_proxy.py`. |
148148
| `estimatesmartfee` | **10-minute inclusion frontier** — not Core historical multi-horizon. Core's result shape: `{feerate, blocks}`, or `{errors, blocks}` with no `feerate` when there is no estimate. Like Core, `feerate` is at least `mempoolminfee`. See [`mempool-fee-estimation.md`](./mempool-fee-estimation.md). |
149149
| `estimaterawfee` | Same 10-minute frontier product as `estimatesmartfee` (Core RPC name for harness scripts). Not Core historical `estimaterawfee` buckets. |
150150
| `getnetworkhashps` | Core `GetNetworkHashPS`: `chainwork(end) − chainwork(start)` over `(maxTime − minTime)` in the lookup window. Default `nblocks` 120; `nblocks<=0` uses `height % difficulty_adjustment_interval + 1` (capped to height). `height<0` or past tip → tip. Genesis / zero dt → `0.0`. |

0 commit comments

Comments
 (0)