feerates: add floor field for the current minimum feerate bitcoind will accept

Changelog-Added: JSON-RPC: `feerates`: added `floor` field for current minimum feerate bitcoind will accept
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2023-04-07 14:23:49 +09:30
parent 812a5a14c0
commit 3a3370f4c1
9 changed files with 53 additions and 5 deletions

View file

@ -358,6 +358,7 @@
"FeeratesPerkb": { "FeeratesPerkb": {
"Feerates.perkb.delayed_to_us": 6, "Feerates.perkb.delayed_to_us": 6,
"Feerates.perkb.estimates[]": 9, "Feerates.perkb.estimates[]": 9,
"Feerates.perkb.floor": 10,
"Feerates.perkb.htlc_resolution": 7, "Feerates.perkb.htlc_resolution": 7,
"Feerates.perkb.max_acceptable": 2, "Feerates.perkb.max_acceptable": 2,
"Feerates.perkb.min_acceptable": 1, "Feerates.perkb.min_acceptable": 1,
@ -374,6 +375,7 @@
"FeeratesPerkw": { "FeeratesPerkw": {
"Feerates.perkw.delayed_to_us": 6, "Feerates.perkw.delayed_to_us": 6,
"Feerates.perkw.estimates[]": 9, "Feerates.perkw.estimates[]": 9,
"Feerates.perkw.floor": 10,
"Feerates.perkw.htlc_resolution": 7, "Feerates.perkw.htlc_resolution": 7,
"Feerates.perkw.max_acceptable": 2, "Feerates.perkw.max_acceptable": 2,
"Feerates.perkw.min_acceptable": 1, "Feerates.perkw.min_acceptable": 1,
@ -1572,6 +1574,10 @@
"added": "v23.05", "added": "v23.05",
"deprecated": false "deprecated": false
}, },
"Feerates.perkb.floor": {
"added": "v23.05",
"deprecated": false
},
"Feerates.perkb.htlc_resolution": { "Feerates.perkb.htlc_resolution": {
"added": "pre-v0.10.1", "added": "pre-v0.10.1",
"deprecated": "v23.05" "deprecated": "v23.05"
@ -1624,6 +1630,10 @@
"added": "v23.05", "added": "v23.05",
"deprecated": false "deprecated": false
}, },
"Feerates.perkw.floor": {
"added": "v23.05",
"deprecated": false
},
"Feerates.perkw.htlc_resolution": { "Feerates.perkw.htlc_resolution": {
"added": "pre-v0.10.1", "added": "pre-v0.10.1",
"deprecated": "v23.05" "deprecated": "v23.05"

View file

@ -1130,6 +1130,7 @@ message FeeratesResponse {
message FeeratesPerkb { message FeeratesPerkb {
uint32 min_acceptable = 1; uint32 min_acceptable = 1;
uint32 max_acceptable = 2; uint32 max_acceptable = 2;
optional uint32 floor = 10;
repeated FeeratesPerkbEstimates estimates = 9; repeated FeeratesPerkbEstimates estimates = 9;
optional uint32 opening = 3; optional uint32 opening = 3;
optional uint32 mutual_close = 4; optional uint32 mutual_close = 4;
@ -1148,6 +1149,7 @@ message FeeratesPerkbEstimates {
message FeeratesPerkw { message FeeratesPerkw {
uint32 min_acceptable = 1; uint32 min_acceptable = 1;
uint32 max_acceptable = 2; uint32 max_acceptable = 2;
optional uint32 floor = 10;
repeated FeeratesPerkwEstimates estimates = 9; repeated FeeratesPerkwEstimates estimates = 9;
optional uint32 opening = 3; optional uint32 opening = 3;
optional uint32 mutual_close = 4; optional uint32 mutual_close = 4;

View file

@ -932,6 +932,7 @@ impl From<responses::FeeratesPerkb> for pb::FeeratesPerkb {
Self { Self {
min_acceptable: c.min_acceptable, // Rule #2 for type u32 min_acceptable: c.min_acceptable, // Rule #2 for type u32
max_acceptable: c.max_acceptable, // Rule #2 for type u32 max_acceptable: c.max_acceptable, // Rule #2 for type u32
floor: c.floor, // Rule #2 for type u32?
estimates: c.estimates.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 estimates: c.estimates.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3
opening: c.opening, // Rule #2 for type u32? opening: c.opening, // Rule #2 for type u32?
mutual_close: c.mutual_close, // Rule #2 for type u32? mutual_close: c.mutual_close, // Rule #2 for type u32?
@ -962,6 +963,7 @@ impl From<responses::FeeratesPerkw> for pb::FeeratesPerkw {
Self { Self {
min_acceptable: c.min_acceptable, // Rule #2 for type u32 min_acceptable: c.min_acceptable, // Rule #2 for type u32
max_acceptable: c.max_acceptable, // Rule #2 for type u32 max_acceptable: c.max_acceptable, // Rule #2 for type u32
floor: c.floor, // Rule #2 for type u32?
estimates: c.estimates.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 estimates: c.estimates.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3
opening: c.opening, // Rule #2 for type u32? opening: c.opening, // Rule #2 for type u32?
mutual_close: c.mutual_close, // Rule #2 for type u32? mutual_close: c.mutual_close, // Rule #2 for type u32?
@ -3297,6 +3299,7 @@ impl From<pb::FeeratesPerkb> for responses::FeeratesPerkb {
Self { Self {
min_acceptable: c.min_acceptable, // Rule #1 for type u32 min_acceptable: c.min_acceptable, // Rule #1 for type u32
max_acceptable: c.max_acceptable, // Rule #1 for type u32 max_acceptable: c.max_acceptable, // Rule #1 for type u32
floor: c.floor, // Rule #1 for type u32?
estimates: Some(c.estimates.into_iter().map(|s| s.into()).collect()), // Rule #4 estimates: Some(c.estimates.into_iter().map(|s| s.into()).collect()), // Rule #4
opening: c.opening, // Rule #1 for type u32? opening: c.opening, // Rule #1 for type u32?
mutual_close: c.mutual_close, // Rule #1 for type u32? mutual_close: c.mutual_close, // Rule #1 for type u32?
@ -3325,6 +3328,7 @@ impl From<pb::FeeratesPerkw> for responses::FeeratesPerkw {
Self { Self {
min_acceptable: c.min_acceptable, // Rule #1 for type u32 min_acceptable: c.min_acceptable, // Rule #1 for type u32
max_acceptable: c.max_acceptable, // Rule #1 for type u32 max_acceptable: c.max_acceptable, // Rule #1 for type u32
floor: c.floor, // Rule #1 for type u32?
estimates: Some(c.estimates.into_iter().map(|s| s.into()).collect()), // Rule #4 estimates: Some(c.estimates.into_iter().map(|s| s.into()).collect()), // Rule #4
opening: c.opening, // Rule #1 for type u32? opening: c.opening, // Rule #1 for type u32?
mutual_close: c.mutual_close, // Rule #1 for type u32? mutual_close: c.mutual_close, // Rule #1 for type u32?

4
cln-rpc/src/model.rs generated
View file

@ -3231,6 +3231,8 @@ pub mod responses {
pub struct FeeratesPerkb { pub struct FeeratesPerkb {
pub min_acceptable: u32, pub min_acceptable: u32,
pub max_acceptable: u32, pub max_acceptable: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub floor: Option<u32>,
#[serde(skip_serializing_if = "crate::is_none_or_empty")] #[serde(skip_serializing_if = "crate::is_none_or_empty")]
pub estimates: Option<Vec<FeeratesPerkbEstimates>>, pub estimates: Option<Vec<FeeratesPerkbEstimates>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -3263,6 +3265,8 @@ pub mod responses {
pub struct FeeratesPerkw { pub struct FeeratesPerkw {
pub min_acceptable: u32, pub min_acceptable: u32,
pub max_acceptable: u32, pub max_acceptable: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub floor: Option<u32>,
#[serde(skip_serializing_if = "crate::is_none_or_empty")] #[serde(skip_serializing_if = "crate::is_none_or_empty")]
pub estimates: Option<Vec<FeeratesPerkwEstimates>>, pub estimates: Option<Vec<FeeratesPerkwEstimates>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]

View file

@ -741,6 +741,7 @@ def feerates_perkb2py(m):
return remove_default({ return remove_default({
"min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite "min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite
"max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite "max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite
"floor": m.floor, # PrimitiveField in generate_composite
"estimates": [feerates_perkb_estimates2py(i) for i in m.estimates], # ArrayField[composite] in generate_composite "estimates": [feerates_perkb_estimates2py(i) for i in m.estimates], # ArrayField[composite] in generate_composite
"opening": m.opening, # PrimitiveField in generate_composite "opening": m.opening, # PrimitiveField in generate_composite
"mutual_close": m.mutual_close, # PrimitiveField in generate_composite "mutual_close": m.mutual_close, # PrimitiveField in generate_composite
@ -763,6 +764,7 @@ def feerates_perkw2py(m):
return remove_default({ return remove_default({
"min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite "min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite
"max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite "max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite
"floor": m.floor, # PrimitiveField in generate_composite
"estimates": [feerates_perkw_estimates2py(i) for i in m.estimates], # ArrayField[composite] in generate_composite "estimates": [feerates_perkw_estimates2py(i) for i in m.estimates], # ArrayField[composite] in generate_composite
"opening": m.opening, # PrimitiveField in generate_composite "opening": m.opening, # PrimitiveField in generate_composite
"mutual_close": m.mutual_close, # PrimitiveField in generate_composite "mutual_close": m.mutual_close, # PrimitiveField in generate_composite

View file

@ -50,6 +50,7 @@ On success, an object is returned, containing:
- **perkb** (object, optional): If *style* parameter was perkb: - **perkb** (object, optional): If *style* parameter was perkb:
- **min\_acceptable** (u32): The smallest feerate that we allow peers to specify: half the 100-block estimate - **min\_acceptable** (u32): The smallest feerate that we allow peers to specify: half the 100-block estimate
- **max\_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet). - **max\_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet).
- **floor** (u32): The smallest feerate that our backend tells us it will accept (i.e. minrelayfee or mempoolminfee) *(added v23.05)*
- **estimates** (array of objects): Feerate estimates from plugin which we are using (usuallly bcli) *(added v23.05)*: - **estimates** (array of objects): Feerate estimates from plugin which we are using (usuallly bcli) *(added v23.05)*:
- **blockcount** (u32): The number of blocks the feerate is expected to get a transaction in *(added v23.05)* - **blockcount** (u32): The number of blocks the feerate is expected to get a transaction in *(added v23.05)*
- **feerate** (u32): The feerate for this estimate, in given *style* *(added v23.05)* - **feerate** (u32): The feerate for this estimate, in given *style* *(added v23.05)*
@ -63,6 +64,7 @@ On success, an object is returned, containing:
- **perkw** (object, optional): If *style* parameter was perkw: - **perkw** (object, optional): If *style* parameter was perkw:
- **min\_acceptable** (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend - **min\_acceptable** (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend
- **max\_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet). - **max\_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet).
- **floor** (u32): The smallest feerate that our backend tells us it will accept (i.e. minrelayfee or mempoolminfee) *(added v23.05)*
- **estimates** (array of objects): Feerate estimates from plugin which we are using (usuallly bcli) *(added v23.05)*: - **estimates** (array of objects): Feerate estimates from plugin which we are using (usuallly bcli) *(added v23.05)*:
- **blockcount** (u32): The number of blocks the feerate is expected to get a transaction in *(added v23.05)* - **blockcount** (u32): The number of blocks the feerate is expected to get a transaction in *(added v23.05)*
- **feerate** (u32): The feerate for this estimate, in given *style* *(added v23.05)* - **feerate** (u32): The feerate for this estimate, in given *style* *(added v23.05)*
@ -136,4 +138,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:c21d903c29fd6195d5890962eaa3265a26a57885b95714696916bd32168b66bc) [comment]: # ( SHA256STAMP:4921275aec48da8b9ddcba5d4237efa72f06b6e005008f2c3aa7029d3bd187fd)

View file

@ -15,6 +15,7 @@
"required": [ "required": [
"min_acceptable", "min_acceptable",
"max_acceptable", "max_acceptable",
"floor",
"estimates" "estimates"
], ],
"properties": { "properties": {
@ -26,6 +27,11 @@
"type": "u32", "type": "u32",
"description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)." "description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)."
}, },
"floor": {
"type": "u32",
"added": "v23.05",
"description": "The smallest feerate that our backend tells us it will accept (i.e. minrelayfee or mempoolminfee)"
},
"estimates": { "estimates": {
"type": "array", "type": "array",
"added": "v23.05", "added": "v23.05",
@ -92,6 +98,7 @@
"required": [ "required": [
"min_acceptable", "min_acceptable",
"max_acceptable", "max_acceptable",
"floor",
"estimates" "estimates"
], ],
"properties": { "properties": {
@ -103,6 +110,11 @@
"type": "u32", "type": "u32",
"description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)." "description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)."
}, },
"floor": {
"type": "u32",
"added": "v23.05",
"description": "The smallest feerate that our backend tells us it will accept (i.e. minrelayfee or mempoolminfee)"
},
"estimates": { "estimates": {
"type": "array", "type": "array",
"added": "v23.05", "added": "v23.05",

View file

@ -667,6 +667,9 @@ static struct command_result *json_feerates(struct command *cmd,
feerate_to_style(feerate_min(cmd->ld, NULL), *style)); feerate_to_style(feerate_min(cmd->ld, NULL), *style));
json_add_u64(response, "max_acceptable", json_add_u64(response, "max_acceptable",
feerate_to_style(feerate_max(cmd->ld, NULL), *style)); feerate_to_style(feerate_max(cmd->ld, NULL), *style));
json_add_u64(response, "floor",
feerate_to_style(get_feerate_floor(cmd->ld->topology),
*style));
json_array_start(response, "estimates"); json_array_start(response, "estimates");
assert(tal_count(topo->smoothed_feerates) == tal_count(topo->feerates[0])); assert(tal_count(topo->smoothed_feerates) == tal_count(topo->feerates[0]));

View file

@ -1532,13 +1532,14 @@ def test_feerates(node_factory):
feerate = l1.rpc.parsefeerate(t) feerate = l1.rpc.parsefeerate(t)
# Query feerates (shouldn't give any!) # Query feerates (shouldn't give any!)
wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 3) wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 4)
feerates = l1.rpc.feerates('perkw') feerates = l1.rpc.feerates('perkw')
assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?' assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?'
assert 'perkb' not in feerates assert 'perkb' not in feerates
assert feerates['perkw']['max_acceptable'] == 2**32 - 1 assert feerates['perkw']['max_acceptable'] == 2**32 - 1
assert feerates['perkw']['min_acceptable'] == 253 assert feerates['perkw']['min_acceptable'] == 253
assert feerates['perkw']['min_acceptable'] == 253 assert feerates['perkw']['min_acceptable'] == 253
assert feerates['perkw']['floor'] == 253
assert feerates['perkw']['estimates'] == [] assert feerates['perkw']['estimates'] == []
for t in types: for t in types:
assert t not in feerates['perkw'] assert t not in feerates['perkw']
@ -1548,6 +1549,8 @@ def test_feerates(node_factory):
assert 'perkw' not in feerates assert 'perkw' not in feerates
assert feerates['perkb']['max_acceptable'] == (2**32 - 1) assert feerates['perkb']['max_acceptable'] == (2**32 - 1)
assert feerates['perkb']['min_acceptable'] == 253 * 4 assert feerates['perkb']['min_acceptable'] == 253 * 4
# Note: This is floored at the FEERATE_FLOOR constant (253)
assert feerates['perkb']['floor'] == 1012
assert feerates['perkb']['estimates'] == [] assert feerates['perkb']['estimates'] == []
for t in types: for t in types:
assert t not in feerates['perkb'] assert t not in feerates['perkb']
@ -1955,6 +1958,7 @@ def test_bitcoind_feerate_floor(node_factory, bitcoind):
"penalty": 30000, "penalty": 30000,
"min_acceptable": 7500, "min_acceptable": 7500,
"max_acceptable": 600000, "max_acceptable": 600000,
"floor": 1012,
"estimates": [{"blockcount": 2, "estimates": [{"blockcount": 2,
"feerate": 60000, "feerate": 60000,
"smoothed_feerate": 60000}, "smoothed_feerate": 60000},
@ -1993,6 +1997,7 @@ def test_bitcoind_feerate_floor(node_factory, bitcoind):
# This has increased (rounded up) # This has increased (rounded up)
"min_acceptable": 20004, "min_acceptable": 20004,
"max_acceptable": 600000, "max_acceptable": 600000,
"floor": 20004,
"estimates": [{"blockcount": 2, "estimates": [{"blockcount": 2,
"feerate": 60000, "feerate": 60000,
"smoothed_feerate": 60000}, "smoothed_feerate": 60000},
@ -2034,6 +2039,7 @@ def test_bitcoind_feerate_floor(node_factory, bitcoind):
# This has increased (rounded up) # This has increased (rounded up)
"min_acceptable": 30004, "min_acceptable": 30004,
"max_acceptable": 600000, "max_acceptable": 600000,
"floor": 30004,
"estimates": [{"blockcount": 2, "estimates": [{"blockcount": 2,
"feerate": 60000, "feerate": 60000,
"smoothed_feerate": 60000}, "smoothed_feerate": 60000},
@ -2987,7 +2993,8 @@ def test_force_feerates(node_factory):
"penalty": 1111, "penalty": 1111,
"min_acceptable": 1875, "min_acceptable": 1875,
"max_acceptable": 150000, "max_acceptable": 150000,
"estimates": estimates} "estimates": estimates,
"floor": 253}
l1.stop() l1.stop()
l1.daemon.opts['force-feerates'] = '1111/2222' l1.daemon.opts['force-feerates'] = '1111/2222'
@ -3001,7 +3008,8 @@ def test_force_feerates(node_factory):
"penalty": 2222, "penalty": 2222,
"min_acceptable": 1875, "min_acceptable": 1875,
"max_acceptable": 150000, "max_acceptable": 150000,
"estimates": estimates} "estimates": estimates,
"floor": 253}
l1.stop() l1.stop()
l1.daemon.opts['force-feerates'] = '1111/2222/3333/4444/5555/6666' l1.daemon.opts['force-feerates'] = '1111/2222/3333/4444/5555/6666'
@ -3015,7 +3023,8 @@ def test_force_feerates(node_factory):
"penalty": 6666, "penalty": 6666,
"min_acceptable": 1875, "min_acceptable": 1875,
"max_acceptable": 150000, "max_acceptable": 150000,
"estimates": estimates} "estimates": estimates,
"floor": 253}
def test_datastore_escapeing(node_factory): def test_datastore_escapeing(node_factory):