Skip to content

Commit a173ded

Browse files
committed
Make Score : Writeable in c_bindings and impl on LockedScore
Ultimately we likely need to wrap the locked `Score` in a struct that exposes writeable somehow, but because all traits have to be fully concretized for C bindings we'll still need `Writeable` on all `Score` in order to expose `Writeable` on the locked score. Otherwise, we'll only have a `LockedScore` with a `Score` visible that only has the `Score` methods, never the original type.
1 parent 016eb96 commit a173ded

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

lightning-invoice/src/payment.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
//! # use lightning::routing::router::{Route, RouteHop, RouteParameters};
3737
//! # use lightning::util::events::{Event, EventHandler, EventsProvider};
3838
//! # use lightning::util::logger::{Logger, Record};
39+
//! # use lightning::util::ser::{Writeable, Writer};
3940
//! # use lightning_invoice::Invoice;
4041
//! # use lightning_invoice::payment::{InvoicePayer, Payer, RetryAttempts, Router};
4142
//! # use secp256k1::key::PublicKey;
@@ -71,6 +72,9 @@
7172
//! # }
7273
//! #
7374
//! # struct FakeScorer {};
75+
//! # impl Writeable for FakeScorer {
76+
//! # fn write<W: Writer>(&self, w: &mut W) -> Result<(), std::io::Error> { unimplemented!(); }
77+
//! # }
7478
//! # impl Score for FakeScorer {
7579
//! # fn channel_penalty_msat(
7680
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
@@ -1224,6 +1228,10 @@ mod tests {
12241228
}
12251229
}
12261230

1231+
#[cfg(c_bindings)]
1232+
impl lightning::util::ser::Writeable for TestScorer {
1233+
fn write<W: lightning::util::ser::Writer>(&self, _: &mut W) -> Result<(), std::io::Error> { unreachable!(); }
1234+
}
12271235
impl Score for TestScorer {
12281236
fn channel_penalty_msat(
12291237
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId

lightning/src/routing/router.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,8 @@ mod tests {
14881488
use ln::channelmanager;
14891489
use util::test_utils;
14901490
use util::ser::Writeable;
1491+
#[cfg(c_bindings)]
1492+
use util::ser::Writer;
14911493

14921494
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
14931495
use bitcoin::hashes::Hash;
@@ -4653,6 +4655,10 @@ mod tests {
46534655
short_channel_id: u64,
46544656
}
46554657

4658+
#[cfg(c_bindings)]
4659+
impl Writeable for BadChannelScorer {
4660+
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), ::io::Error> { unimplemented!() }
4661+
}
46564662
impl Score for BadChannelScorer {
46574663
fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId) -> u64 {
46584664
if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
@@ -4665,6 +4671,11 @@ mod tests {
46654671
node_id: NodeId,
46664672
}
46674673

4674+
#[cfg(c_bindings)]
4675+
impl Writeable for BadNodeScorer {
4676+
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), ::io::Error> { unimplemented!() }
4677+
}
4678+
46684679
impl Score for BadNodeScorer {
46694680
fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, target: &NodeId) -> u64 {
46704681
if *target == self.node_id { u64::max_value() } else { 0 }

lightning/src/routing/scoring.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,22 @@ use core::ops::{DerefMut, Sub};
6363
use core::time::Duration;
6464
use io::{self, Read}; use sync::{Mutex, MutexGuard};
6565

66+
/// We define Score ever-so-slightly differently based on whether we are being built for C bindings
67+
/// or not. For users, `LockableScore` must somehow be writeable to disk. For Rust users, this is
68+
/// no problem - you move a `Score` that implements `Writeable` into a `Mutex`, lock it, and now
69+
/// you have the original, concrete, `Score` type, which presumably implements `Writeable`.
70+
///
71+
/// For C users, once you've moved the `Score` into a `LockableScore` all you have after locking it
72+
/// is an opaque trait object with an opaque pointer with no type info. Users could take the unsafe
73+
/// approach of blindly casting that opaque pointer to a concrete type and calling `Writeable` from
74+
/// there, but other languages downstream of the C bindings (e.g. Java) can't even do that.
75+
/// Instead, we really want `Score` and `LockableScore` to implement `Writeable` directly, which we
76+
/// do here by defining `Score` differently for `cfg(c_bindings)`.
77+
macro_rules! define_score { ($($supertrait: path)*) => {
6678
/// An interface used to score payment channels for path finding.
6779
///
6880
/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
69-
pub trait Score {
81+
pub trait Score $(: $supertrait)* {
7082
/// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
7183
/// given channel in the direction from `source` to `target`.
7284
///
@@ -85,6 +97,22 @@ pub trait Score {
8597
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
8698
}
8799

100+
impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
101+
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64 {
102+
self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, channel_capacity_msat, source, target)
103+
}
104+
105+
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
106+
self.deref_mut().payment_path_failed(path, short_channel_id)
107+
}
108+
}
109+
} }
110+
111+
#[cfg(c_bindings)]
112+
define_score!(Writeable);
113+
#[cfg(not(c_bindings))]
114+
define_score!();
115+
88116
/// A scorer that is accessed under a lock.
89117
///
90118
/// Needed so that calls to [`Score::channel_penalty_msat`] in [`find_route`] can be made while
@@ -101,6 +129,7 @@ pub trait LockableScore<'a> {
101129
fn lock(&'a self) -> Self::Locked;
102130
}
103131

132+
/// (C-not exported)
104133
impl<'a, T: 'a + Score> LockableScore<'a> for Mutex<T> {
105134
type Locked = MutexGuard<'a, T>;
106135

@@ -117,13 +146,34 @@ impl<'a, T: 'a + Score> LockableScore<'a> for RefCell<T> {
117146
}
118147
}
119148

120-
impl<S: Score, T: DerefMut<Target=S>> Score for T {
121-
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64 {
122-
self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, channel_capacity_msat, source, target)
149+
#[cfg(c_bindings)]
150+
/// A concrete implementation of [`LockableScore`] which supports multi-threading.
151+
pub struct MultiThreadedLockableScore<S: Score> {
152+
score: Mutex<S>,
153+
}
154+
#[cfg(c_bindings)]
155+
/// (C-not exported)
156+
impl<'a, T: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore<T> {
157+
type Locked = MutexGuard<'a, T>;
158+
159+
fn lock(&'a self) -> MutexGuard<'a, T> {
160+
Mutex::lock(&self.score).unwrap()
123161
}
162+
}
124163

125-
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
126-
self.deref_mut().payment_path_failed(path, short_channel_id)
164+
#[cfg(c_bindings)]
165+
/// (C-not exported)
166+
impl<'a, T: Writeable> Writeable for RefMut<'a, T> {
167+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
168+
T::write(&**self, writer)
169+
}
170+
}
171+
172+
#[cfg(c_bindings)]
173+
/// (C-not exported)
174+
impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> {
175+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
176+
S::write(&**self, writer)
127177
}
128178
}
129179

0 commit comments

Comments
 (0)