cln-rpc: Map feerates and backfill methods using it

This commit is contained in:
Christian Decker
2022-04-01 14:42:45 +10:30
committed by Rusty Russell
parent 20704746bc
commit eb2aa8c51c
10 changed files with 122 additions and 0 deletions

View File

@@ -774,6 +774,7 @@
},
"WithdrawRequest": {
"Withdraw.destination": 1,
"Withdraw.feerate": 5,
"Withdraw.minconf": 3,
"Withdraw.satoshi": 2,
"Withdraw.utxos[]": 4

View File

@@ -904,6 +904,7 @@ message NewaddrResponse {
message WithdrawRequest {
bytes destination = 1;
optional Amount satoshi = 2;
optional Feerate feerate = 5;
optional uint32 minconf = 3;
repeated Utxo utxos = 4;
}

View File

@@ -30,3 +30,13 @@ message Utxo {
bytes txid = 1;
uint32 outnum = 2;
}
message Feerate {
oneof style {
bool slow = 1;
bool normal = 2;
bool urgent = 3;
uint32 perkb = 4;
uint32 perkw = 5;
}
}

View File

@@ -1052,6 +1052,7 @@ impl From<&pb::WithdrawRequest> for requests::WithdrawRequest {
Self {
destination: hex::encode(&c.destination), // Rule #1 for type pubkey
satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat?
feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate?
minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16?
utxos: c.utxos.iter().map(|s| s.into()).collect(),
}

View File

@@ -31,3 +31,17 @@ impl From<&Utxo> for JUtxo {
}
}
}
impl From<&Feerate> for cln_rpc::primitives::Feerate {
fn from(f: &Feerate) -> cln_rpc::primitives::Feerate {
use cln_rpc::primitives::Feerate as JFeerate;
use feerate::Style;
match f.style.clone().unwrap() {
Style::Slow(_) => JFeerate::Slow,
Style::Normal(_) => JFeerate::Normal,
Style::Urgent(_) => JFeerate::Urgent,
Style::Perkw(i) => JFeerate::PerKw(i),
Style::Perkb(i) => JFeerate::PerKb(i),
}
}
}

View File

@@ -464,6 +464,8 @@ pub mod requests {
pub destination: String,
#[serde(alias = "satoshi", skip_serializing_if = "Option::is_none")]
pub satoshi: Option<Amount>,
#[serde(alias = "feerate", skip_serializing_if = "Option::is_none")]
pub feerate: Option<Feerate>,
#[serde(alias = "minconf", skip_serializing_if = "Option::is_none")]
pub minconf: Option<u16>,
#[serde(alias = "utxos")]

View File

@@ -218,6 +218,77 @@ impl From<Amount> for String {
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Feerate {
Slow,
Normal,
Urgent,
PerKb(u32),
PerKw(u32),
}
impl TryFrom<&str> for Feerate {
type Error = Error;
fn try_from(s: &str) -> Result<Feerate> {
let number: u32 = s
.chars()
.map(|c| c.to_digit(10))
.take_while(|opt| opt.is_some())
.fold(0, |acc, digit| acc * 10 + (digit.unwrap() as u32));
let s = s.to_lowercase();
if s.ends_with("perkw") {
Ok(Feerate::PerKw(number))
} else if s.ends_with("perkb") {
Ok(Feerate::PerKb(number))
} else if s == "slow" {
Ok(Feerate::Slow)
} else if s == "normal" {
Ok(Feerate::Normal)
} else if s == "urgent" {
Ok(Feerate::Urgent)
} else {
Err(anyhow!("Unable to parse feerate from string: {}", s))
}
}
}
impl From<&Feerate> for String {
fn from(f: &Feerate) -> String {
match f {
Feerate::Slow => "slow".to_string(),
Feerate::Normal => "normal".to_string(),
Feerate::Urgent => "urgent".to_string(),
Feerate::PerKb(v) => format!("{}perkb", v),
Feerate::PerKw(v) => format!("{}perkw", v),
}
}
}
impl<'de> Deserialize<'de> for Feerate {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
let res: Feerate = s
.as_str()
.try_into()
.map_err(|e| serde::de::Error::custom(format!("{}", e)))?;
Ok(res)
}
}
impl Serialize for Feerate {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s: String = self.into();
serializer.serialize_str(&s)
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -283,4 +354,22 @@ mod test {
r#"{"all":"all","not_all":"31337msat","any":"any","not_any":"42msat"}"#
);
}
#[test]
fn test_parse_feerate() {
let tests = vec![
("slow", Feerate::Slow),
("normal", Feerate::Normal),
("urgent", Feerate::Urgent),
("12345perkb", Feerate::PerKb(12345)),
("54321perkw", Feerate::PerKw(54321)),
];
for (input, output) in tests.into_iter() {
let parsed: Feerate = input.try_into().unwrap();
assert_eq!(parsed, output);
let serialized: String = (&parsed).into();
assert_eq!(serialized, input);
}
}
}

View File

@@ -23,6 +23,7 @@ typemap = {
'f32': 'float',
'integer': 'sint64',
"utxo": "Utxo",
"feerate": "Feerate",
}
@@ -394,6 +395,7 @@ class GrpcUnconverterGenerator(GrpcConverterGenerator):
'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))',
'msat': f'c.{name}.as_ref().unwrap().into()',
'msat?': f'c.{name}.as_ref().map(|a| a.into())',
'feerate?': f'c.{name}.as_ref().map(|a| a.into())',
}.get(
typ,
f'c.{name}.clone()' # default to just assignment

View File

@@ -230,6 +230,7 @@ class PrimitiveField(Field):
"integer",
"u16",
"number",
"feerate",
"utxo", # A string representing the tuple (txid, outnum)
]

View File

@@ -40,6 +40,7 @@ typemap = {
'txid': 'String',
'float': 'f32',
'utxo': 'Utxo',
'feerate': 'Feerate',
}
header = f"""#![allow(non_camel_case_types)]