Skip to content

Commit

Permalink
Merge pull request #1082 from breez/savage-short-channel-id
Browse files Browse the repository at this point in the history
Change short_channel_id to string
  • Loading branch information
dangeross authored Sep 11, 2024
2 parents 1298055 + 1d2055c commit 6b3d705
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 44 deletions.
2 changes: 1 addition & 1 deletion libs/sdk-bindings/src/breez_sdk.udl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dictionary RouteHintHop {
string src_node_id;
u64 short_channel_id;
string short_channel_id;
u32 fees_base_msat;
u32 fees_proportional_millionths;
u64 cltv_expiry_delta;
Expand Down
76 changes: 71 additions & 5 deletions libs/sdk-common/src/invoice.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::num::ParseIntError;
use std::str::FromStr;
use std::time::{SystemTimeError, UNIX_EPOCH};

Expand Down Expand Up @@ -57,6 +58,12 @@ impl From<Bolt11SemanticError> for InvoiceError {
}
}

impl From<ParseIntError> for InvoiceError {
fn from(err: ParseIntError) -> Self {
Self::Generic(err.to_string())
}
}

impl From<regex::Error> for InvoiceError {
fn from(err: regex::Error) -> Self {
Self::Generic(err.to_string())
Expand All @@ -75,6 +82,25 @@ impl From<SystemTimeError> for InvoiceError {
}
}

fn parse_short_channel_id(id_str: &str) -> InvoiceResult<u64> {
let parts: Vec<&str> = id_str.split('x').collect();
if parts.len() != 3 {
return Err(InvoiceError::generic("Invalid short channel id"));
}
let block_num = parts[0].parse::<u64>()?;
let tx_num = parts[1].parse::<u64>()?;
let tx_out = parts[2].parse::<u64>()?;

Ok((block_num & 0xFFFFFF) << 40 | (tx_num & 0xFFFFFF) << 16 | (tx_out & 0xFFFF))
}

fn format_short_channel_id(id: u64) -> String {
let block_num = (id >> 40) as u32;
let tx_num = ((id >> 16) & 0xFFFFFF) as u32;
let tx_out = (id & 0xFFFF) as u16;
format!("{block_num}x{tx_num}x{tx_out}")
}

/// Wrapper for a BOLT11 LN invoice
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct LNInvoice {
Expand Down Expand Up @@ -106,7 +132,7 @@ pub struct RouteHintHop {
/// The node_id of the non-target end of the route
pub src_node_id: String,
/// The short_channel_id of this channel
pub short_channel_id: u64,
pub short_channel_id: String,
/// The fees which must be paid to use this channel
pub fees_base_msat: u32,
pub fees_proportional_millionths: u32,
Expand All @@ -133,7 +159,7 @@ impl RouteHint {

let router_hop = router::RouteHintHop {
src_node_id: pubkey_res,
short_channel_id: hop.short_channel_id,
short_channel_id: parse_short_channel_id(&hop.short_channel_id)?,
fees: RoutingFees {
base_msat: hop.fees_base_msat,
proportional_millionths: hop.fees_proportional_millionths,
Expand All @@ -154,7 +180,7 @@ impl RouteHint {

let router_hop = RouteHintHop {
src_node_id: pubkey_res,
short_channel_id: hop.short_channel_id,
short_channel_id: format_short_channel_id(hop.short_channel_id),
fees_base_msat: hop.fees.base_msat,
fees_proportional_millionths: hop.fees.proportional_millionths,
cltv_expiry_delta: u64::from(hop.cltv_expiry_delta),
Expand Down Expand Up @@ -315,7 +341,7 @@ mod tests {
private_key.copy_from_slice(&private_key_vec[0..32]);
let hint_hop = self::RouteHintHop {
src_node_id: res.payee_pubkey,
short_channel_id: 1234,
short_channel_id: format_short_channel_id(1234),
cltv_expiry_delta: 2000,
htlc_minimum_msat: Some(3000),
htlc_maximum_msat: Some(4000),
Expand All @@ -342,7 +368,7 @@ mod tests {
private_key.copy_from_slice(&private_key_vec[0..32]);
let hint_hop = self::RouteHintHop {
src_node_id: res.payee_pubkey,
short_channel_id: 1234,
short_channel_id: format_short_channel_id(1234),
fees_base_msat: 1000,
fees_proportional_millionths: 100,
cltv_expiry_delta: 2000,
Expand Down Expand Up @@ -373,4 +399,44 @@ mod tests {
assert!(res.is_ok());
assert!(validate_network(res.unwrap(), Network::Bitcoin).is_err());
}

#[test]
fn test_format_short_channel_id() {
let valid_short_channel_ids = vec![
(0, "0x0x0"),
(936_502_917_475_117, "851x12489658x11053"),
(455_944_619_913_684, "414x11395355x29140"),
(u64::MAX, "16777215x16777215x65535"),
];
for (scid, scid_str) in valid_short_channel_ids {
let res = format_short_channel_id(scid);
assert_eq!(res, scid_str);
}
}

#[test]
fn test_parse_short_channel_id() {
let valid_short_channel_ids = vec![
("0x0x0", 0),
("16000000x0x3965", 17_592_186_044_416_003_965),
("94838x10x3", 104_275_483_755_675_651),
("16777215x16777215x65535", u64::MAX),
];
for (scid_str, scid) in valid_short_channel_ids {
let res = parse_short_channel_id(scid_str);
assert!(res.is_ok());
assert_eq!(res.unwrap(), scid);
}

let invalid_short_channel_ids = vec![
"0",
"16000000x0x-3965",
"18446744073709551615",
"16777215x65535",
];
for scid_str in invalid_short_channel_ids {
let res = parse_short_channel_id(scid_str);
assert!(res.is_err());
}
}
}
2 changes: 1 addition & 1 deletion libs/sdk-core/src/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub struct _RouteHint {
#[frb(mirror(RouteHintHop))]
pub struct _RouteHintHop {
pub src_node_id: String,
pub short_channel_id: u64,
pub short_channel_id: String,
pub fees_base_msat: u32,
pub fees_proportional_millionths: u32,
pub cltv_expiry_delta: u64,
Expand Down
13 changes: 5 additions & 8 deletions libs/sdk-core/src/breez_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ use crate::greenlight::{GLBackupTransport, Greenlight};
use crate::lnurl::pay::*;
use crate::lsp::LspInformation;
use crate::models::{
parse_short_channel_id, sanitize::*, ChannelState, ClosedChannelPaymentDetails, Config,
EnvironmentType, LspAPI, NodeState, Payment, PaymentDetails, PaymentType, ReverseSwapPairInfo,
ReverseSwapServiceAPI, SwapInfo, SwapperAPI, INVOICE_PAYMENT_FEE_EXPIRY_SECONDS,
sanitize::*, ChannelState, ClosedChannelPaymentDetails, Config, EnvironmentType, LspAPI,
NodeState, Payment, PaymentDetails, PaymentType, ReverseSwapPairInfo, ReverseSwapServiceAPI,
SwapInfo, SwapperAPI, INVOICE_PAYMENT_FEE_EXPIRY_SECONDS,
};
use crate::node_api::{CreateInvoiceRequest, NodeAPI};
use crate::persist::db::SqliteStorage;
Expand Down Expand Up @@ -2714,7 +2714,7 @@ impl PaymentReceiver {
let open_channel_hint = RouteHint {
hops: vec![RouteHintHop {
src_node_id: lsp_info.pubkey.clone(),
short_channel_id: parse_short_channel_id("1x0x0")?,
short_channel_id: "1x0x0".to_string(),
fees_base_msat: lsp_info.base_fee_msat as u32,
fees_proportional_millionths: (lsp_info.fee_rate * 1000000.0) as u32,
cltv_expiry_delta: lsp_info.time_lock_delta as u64,
Expand Down Expand Up @@ -3252,10 +3252,7 @@ pub(crate) mod tests {
assert_eq!(ln_invoice.routing_hints[0].hops.len(), 1);
let lsp_hop = &ln_invoice.routing_hints[0].hops[0];
assert_eq!(lsp_hop.src_node_id, breez_server.clone().lsp_pub_key());
assert_eq!(
lsp_hop.short_channel_id,
parse_short_channel_id("1x0x0").unwrap()
);
assert_eq!(lsp_hop.short_channel_id, "1x0x0");
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-core/src/bridge_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ const _: fn() = || {
{
let RouteHintHop = None::<RouteHintHop>.unwrap();
let _: String = RouteHintHop.src_node_id;
let _: u64 = RouteHintHop.short_channel_id;
let _: String = RouteHintHop.short_channel_id;
let _: u32 = RouteHintHop.fees_base_msat;
let _: u32 = RouteHintHop.fees_proportional_millionths;
let _: u64 = RouteHintHop.cltv_expiry_delta;
Expand Down
5 changes: 2 additions & 3 deletions libs/sdk-core/src/greenlight/node_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ impl Greenlight {
base_fee_msat: hint.fees_base_msat as u64,
fee_per_millionth: hint.fees_proportional_millionths as u64,
node_id: payee_node_id.clone().unwrap_or_default(),
short_channel_id: format_short_channel_id(hint.short_channel_id),
short_channel_id: hint.short_channel_id.clone(),
channel_delay: hint.cltv_expiry_delta,
}])
}
Expand Down Expand Up @@ -1790,11 +1790,10 @@ impl NodeAPI for Greenlight {
"For peer {}: remote base {} proportional {} cltv_delta {}",
peer_id_str, fees_base_msat, fees_proportional_millionths, cltv_delta,
);
let scid = parse_short_channel_id(&channel_id)?;
let hint = RouteHint {
hops: vec![RouteHintHop {
src_node_id: peer_id_str,
short_channel_id: scid,
short_channel_id: channel_id,
fees_base_msat,
fees_proportional_millionths,
cltv_expiry_delta: cltv_delta as u64,
Expand Down
19 changes: 0 additions & 19 deletions libs/sdk-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1472,25 +1472,6 @@ impl SwapInfo {
}
}

pub(crate) fn parse_short_channel_id(id_str: &str) -> Result<u64> {
let parts: Vec<&str> = id_str.split('x').collect();
if parts.len() != 3 {
return Ok(0);
}
let block_num = parts[0].parse::<u64>()?;
let tx_num = parts[1].parse::<u64>()?;
let tx_out = parts[2].parse::<u64>()?;

Ok((block_num & 0xFFFFFF) << 40 | (tx_num & 0xFFFFFF) << 16 | (tx_out & 0xFFFF))
}

pub(crate) fn format_short_channel_id(id: u64) -> String {
let block_num = (id >> 40) as u32;
let tx_num = ((id >> 16) & 0xFFFFFF) as u32;
let tx_out = (id & 0xFFFF) as u16;
format!("{block_num}x{tx_num}x{tx_out}")
}

/// UTXO known to the LN node
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct UnspentTransactionOutput {
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-core/src/swap_out/boltzswap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl From<BoltzRouteHintHop> for RouteHintHop {
fn from(value: BoltzRouteHintHop) -> Self {
RouteHintHop {
src_node_id: value.node_id,
short_channel_id: 0_u64,
short_channel_id: "0x0x0".to_string(),
fees_base_msat: value.fee_base_msat,
fees_proportional_millionths: value.fee_proportional_millionths,
cltv_expiry_delta: value.cltv_expiry_delta,
Expand Down
4 changes: 2 additions & 2 deletions libs/sdk-flutter/lib/bridge_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ class RouteHint {

class RouteHintHop {
final String srcNodeId;
final int shortChannelId;
final String shortChannelId;
final int feesBaseMsat;
final int feesProportionalMillionths;
final int cltvExpiryDelta;
Expand Down Expand Up @@ -4063,7 +4063,7 @@ class BreezSdkCoreImpl implements BreezSdkCore {
if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
return RouteHintHop(
srcNodeId: _wire2api_String(arr[0]),
shortChannelId: _wire2api_u64(arr[1]),
shortChannelId: _wire2api_String(arr[1]),
feesBaseMsat: _wire2api_u32(arr[2]),
feesProportionalMillionths: _wire2api_u32(arr[3]),
cltvExpiryDelta: _wire2api_u64(arr[4]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3083,7 +3083,7 @@ fun asRouteHintHop(routeHintHop: ReadableMap): RouteHintHop? {
return null
}
val srcNodeId = routeHintHop.getString("srcNodeId")!!
val shortChannelId = routeHintHop.getDouble("shortChannelId").toULong()
val shortChannelId = routeHintHop.getString("shortChannelId")!!
val feesBaseMsat = routeHintHop.getInt("feesBaseMsat").toUInt()
val feesProportionalMillionths = routeHintHop.getInt("feesProportionalMillionths").toUInt()
val cltvExpiryDelta = routeHintHop.getDouble("cltvExpiryDelta").toULong()
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-react-native/ios/BreezSDKMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3455,7 +3455,7 @@ enum BreezSDKMapper {
guard let srcNodeId = routeHintHop["srcNodeId"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "srcNodeId", typeName: "RouteHintHop"))
}
guard let shortChannelId = routeHintHop["shortChannelId"] as? UInt64 else {
guard let shortChannelId = routeHintHop["shortChannelId"] as? String else {
throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "shortChannelId", typeName: "RouteHintHop"))
}
guard let feesBaseMsat = routeHintHop["feesBaseMsat"] as? UInt32 else {
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-react-native/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ export interface RouteHint {

export interface RouteHintHop {
srcNodeId: string
shortChannelId: number
shortChannelId: string
feesBaseMsat: number
feesProportionalMillionths: number
cltvExpiryDelta: number
Expand Down

0 comments on commit 6b3d705

Please sign in to comment.