Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog.d/sendraw-allowhighfees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fixed

- **LND and other btcd `rpcclient` users can broadcast.** That client reads
`/rbitcoin:` as Core before 0.19 and sends `sendrawtransaction` a boolean
`allowhighfees`, which was `-3 maxfeerate is not a number or string`.
`true` now lifts the fee cap and `false` keeps the default 10000 sat/vB.
8 changes: 7 additions & 1 deletion crates/rbitcoin-rpc/src/methods/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,13 @@ pub const MAX_BURN_MSG: &str =
pub fn sendrawtransaction(ctx: &RpcContext, params: &RpcParams) -> Result<Value, Value> {
params.reject_unknown(&["hexstring", "maxfeerate", "maxburnamount"])?;
let hex = params.req_str(0, "hexstring")?;
let max_feerate = opt_maxfeerate_sat_vb(params, 1)?;
// Pre-0.19 `allowhighfees`. btcd rpcclient (LND, btcwallet) sends it
// because `/rbitcoin:` sorts below its `0.19.0` subversion check.
let max_feerate = match params.get(1, "maxfeerate") {
Some(Value::Bool(true)) => 0,
Some(Value::Bool(false)) => DEFAULT_MAX_RAW_TX_FEE_SAT_VB,
_ => opt_maxfeerate_sat_vb(params, 1)?,
};
let max_burn = opt_maxburn_sat(params, 2)?;
let tx = decode_tx_hex(hex)?;
let mp = ctx
Expand Down
1 change: 1 addition & 0 deletions crates/rbitcoin-rpc/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ pub const NAMED_HELP: &[(&str, &str)] = &[
"sendrawtransaction",
"sendrawtransaction hexstring (maxfeerate) (maxburnamount)\n\
RPC submit only. Default maxfeerate 10000 sat/vB (0 unlimited; >=100000 sat/vB is a parameter error). \
A pre-0.19 boolean allowhighfees there: true is unlimited, false the default. \
Default maxburnamount 0. P2P relay is not capped.",
),
(
Expand Down
26 changes: 20 additions & 6 deletions crates/rbitcoin-rpc/tests/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2404,11 +2404,25 @@ fn pin_sendraw_maxfeerate_at_default_and_one_sat_over(ctx: &RpcContext, at: u32,
cb - max_fee - 1,
ScriptBuf::from_bytes(vec![0x51]),
);
let e = dispatch(ctx, "sendrawtransaction", vec![json!(over_hex)]).unwrap_err();
assert_eq!(e["code"], ERR_VERIFY_ERROR, "{e}");
assert_eq!(
e["message"], "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
"{e}"
// Pre-0.19 `allowhighfees`, which btcd rpcclient sends for a `/rbitcoin:`
// subversion: `false` keeps the default cap, `true` lifts it.
for params in [vec![json!(over_hex)], vec![json!(over_hex), json!(false)]] {
let e = dispatch(ctx, "sendrawtransaction", params).unwrap_err();
assert_eq!(e["code"], ERR_VERIFY_ERROR, "{e}");
assert_eq!(
e["message"], "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
"{e}"
);
}
let ok = dispatch(
ctx,
"sendrawtransaction",
vec![json!(over_hex), json!(true)],
)
.unwrap();
assert!(
ok.as_str().is_some(),
"allowhighfees true must lift the cap: {ok}"
);
}

Expand All @@ -2427,7 +2441,7 @@ fn pin_maxfeerate_json_shapes(ctx: &RpcContext) {
"maxfeerate must be an integer sat/vB",
),
(
json!(true),
json!({}),
ERR_TYPE_ERROR,
"maxfeerate is not a number or string",
),
Expand Down
7 changes: 7 additions & 0 deletions docs/lightning.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ when that integer is at least `150000`, which it is. Raising the integer
toward a current Core release would advertise wallet RPC that this node
does not have. See [`rpc.md`](./rpc.md#getnetworkinfoversion).

LND reaches this node through btcd `rpcclient`, which string-compares
`subversion` after trimming `/Satoshi:`. `/rbitcoin:…/` sorts below
`0.19.0`, so the client speaks the pre-0.19 dialect:
`sendrawtransaction` gets the boolean `allowhighfees`, which this node
accepts. The client also refuses to call `testmempoolaccept`, so LND
skips that pre-check and publishes directly.

`estimatesmartfee` accepts `estimate_mode` `economical` and `conservative`
and returns the same 10-minute feerate either way. `initialblockdownload`
means relay is inhibited (`--min-chain-work` / `--max-tip-age`), not
Expand Down
2 changes: 1 addition & 1 deletion docs/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ still wait for durable SH when shindex is on.
| `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`. |
| `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. |
| `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`. |
| `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`. |
| `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`. |
| `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). |
| `estimaterawfee` | Same 10-minute frontier product as `estimatesmartfee` (Core RPC name for harness scripts). Not Core historical `estimaterawfee` buckets. |
| `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`. |
Expand Down
Loading