Merge pull request #1146 from jkczyz/2021-10-score-serialization

Scorer serialization
This commit is contained in:
Matt Corallo 2021-11-02 20:36:22 +00:00 committed by GitHub
commit 094ddb264b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 242 additions and 100 deletions

View file

@ -312,7 +312,6 @@ mod tests {
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
use lightning::routing::scorer::Scorer;
use lightning::routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
use lightning::util::config::UserConfig;
use lightning::util::events::{Event, MessageSendEventsProvider, MessageSendEvent};
@ -635,7 +634,7 @@ mod tests {
let data_dir = nodes[0].persister.get_data_dir();
let persister = move |node: &ChannelManager<InMemorySigner, Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<test_utils::TestLogger>>| FilesystemPersister::persist_manager(data_dir.clone(), node);
let router = DefaultRouter::new(Arc::clone(&nodes[0].network_graph), Arc::clone(&nodes[0].logger));
let scorer = Arc::new(Mutex::new(Scorer::default()));
let scorer = Arc::new(Mutex::new(test_utils::TestScorer::default()));
let invoice_payer = Arc::new(InvoicePayer::new(Arc::clone(&nodes[0].node), router, scorer, Arc::clone(&nodes[0].logger), |_: &_| {}, RetryAttempts(2)));
let event_handler = Arc::clone(&invoice_payer);
let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());

View file

@ -157,7 +157,6 @@ mod test {
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs::ChannelMessageHandler;
use lightning::routing::router::{Payee, RouteParameters, find_route};
use lightning::routing::scorer::Scorer;
use lightning::util::events::MessageSendEventsProvider;
use lightning::util::test_utils;
#[test]
@ -183,7 +182,7 @@ mod test {
let first_hops = nodes[0].node.list_usable_channels();
let network_graph = node_cfgs[0].network_graph;
let logger = test_utils::TestLogger::new();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&nodes[0].node.get_our_node_id(), &params, network_graph,
Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,

View file

@ -6087,9 +6087,9 @@ mod tests {
use ln::msgs;
use ln::msgs::ChannelMessageHandler;
use routing::router::{Payee, RouteParameters, find_route};
use routing::scorer::Scorer;
use util::errors::APIError;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use util::test_utils;
#[cfg(feature = "std")]
#[test]
@ -6325,7 +6325,7 @@ mod tests {
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// To start (1), send a regular payment but don't claim it.
let expected_route = [&nodes[1]];
@ -6430,7 +6430,7 @@ mod tests {
};
let network_graph = nodes[0].network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&payer_pubkey, &params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer
@ -6473,7 +6473,7 @@ mod tests {
};
let network_graph = nodes[0].network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&payer_pubkey, &params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer

View file

@ -17,7 +17,6 @@ use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, PaymentId};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use routing::router::{Payee, Route, get_route};
use routing::scorer::Scorer;
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler};
@ -1016,7 +1015,7 @@ macro_rules! get_route_and_payment_hash {
let payee = $crate::routing::router::Payee::new($recv_node.node.get_our_node_id())
.with_features($crate::ln::features::InvoiceFeatures::known())
.with_route_hints($last_hops);
let scorer = ::routing::scorer::Scorer::with_fixed_penalty(0);
let scorer = ::util::test_utils::TestScorer::with_fixed_penalty(0);
let route = ::routing::router::get_route(
&$send_node.node.get_our_node_id(), &payee, $send_node.network_graph,
Some(&$send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
@ -1353,7 +1352,7 @@ pub const TEST_FINAL_CLTV: u32 = 70;
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id())
.with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = get_route(
&origin_node.node.get_our_node_id(), &payee, &origin_node.network_graph,
Some(&origin_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
@ -1371,7 +1370,7 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id())
.with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = get_route(&origin_node.node.get_our_node_id(), &payee, origin_node.network_graph, None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), expected_route.len());

View file

@ -25,7 +25,6 @@ use ln::{chan_utils, onion_utils};
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
use routing::network_graph::{NetworkUpdate, RoutingFees};
use routing::router::{Payee, Route, RouteHop, RouteHint, RouteHintHop, RouteParameters, find_route, get_route};
use routing::scorer::Scorer;
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
@ -7161,7 +7160,7 @@ fn test_check_htlc_underpaying() {
// Create some initial channels
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
let route = get_route(&nodes[0].node.get_our_node_id(), &payee, nodes[0].network_graph, None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
@ -7561,7 +7560,7 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000, InitFeatures::known(), InitFeatures::known());
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].network_graph, None,
3_000_000, 50, nodes[0].logger, &scorer).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
@ -9061,7 +9060,7 @@ fn test_keysend_payments_to_public_node() {
final_value_msat: 10000,
final_cltv_expiry_delta: 40,
};
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(&payer_pubkey, &params, network_graph, None, nodes[0].logger, &scorer).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
@ -9095,7 +9094,7 @@ fn test_keysend_payments_to_private_node() {
};
let network_graph = nodes[0].network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = find_route(
&payer_pubkey, &params, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer

View file

@ -15,7 +15,6 @@ use ln::{PaymentPreimage, PaymentHash};
use ln::channelmanager::PaymentSendFailure;
use routing::router::{Payee, get_route};
use routing::network_graph::NetworkUpdate;
use routing::scorer::Scorer;
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, ErrorAction};
@ -82,7 +81,7 @@ fn updates_shutdown_wait() {
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
let logger = test_utils::TestLogger::new();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let (our_payment_preimage, our_payment_hash, _) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 100000);

View file

@ -1473,7 +1473,6 @@ mod tests {
use routing;
use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
use routing::router::{get_route, Payee, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees};
use routing::scorer::Scorer;
use chain::transaction::OutPoint;
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
@ -1943,7 +1942,7 @@ mod tests {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Simple route to 2 via 1
@ -1974,7 +1973,7 @@ mod tests {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Simple route to 2 via 1
@ -1993,7 +1992,7 @@ mod tests {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Simple route to 2 via 1
@ -2118,7 +2117,7 @@ mod tests {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// A route to node#2 via two paths.
// One path allows transferring 35-40 sats, another one also allows 35-40 sats.
@ -2254,7 +2253,7 @@ mod tests {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// // Disable channels 4 and 12 by flags=2
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
@ -2312,7 +2311,7 @@ mod tests {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[2]);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Disable nodes 1, 2, and 8 by requiring unknown feature bits
let unknown_features = NodeFeatures::known().set_unknown_feature_required();
@ -2353,7 +2352,7 @@ mod tests {
fn our_chans_test() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Route to 1 via 2 and 3 because our channel to 1 is disabled
let payee = Payee::new(nodes[0]);
@ -2482,7 +2481,7 @@ mod tests {
fn partial_route_hint_test() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Simple test across 2, 3, 5, and 4 via a last_hop channel
// Tests the behaviour when the RouteHint contains a suboptimal hop.
@ -2581,7 +2580,7 @@ mod tests {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[6]).with_route_hints(empty_last_hop(&nodes));
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Test handling of an empty RouteHint passed in Invoice.
@ -2663,7 +2662,7 @@ mod tests {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[6]).with_route_hints(multi_hint_last_hops(&nodes));
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Test through channels 2, 3, 5, 8.
// Test shows that multiple hop hints are considered.
@ -2769,7 +2768,7 @@ mod tests {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let payee = Payee::new(nodes[6]).with_route_hints(last_hops_with_public_channel(&nodes));
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// This test shows that public routes can be present in the invoice
// which would be handled in the same manner.
@ -2818,7 +2817,7 @@ mod tests {
fn our_chans_last_hop_connect_test() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// Simple test with outbound channel to 4 to test that last_hops and first_hops connect
let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
@ -2939,7 +2938,7 @@ mod tests {
}]);
let payee = Payee::new(target_node_id).with_route_hints(vec![last_hops]);
let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)];
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
get_route(&source_node_id, &payee, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), Some(&our_chans.iter().collect::<Vec<_>>()), route_val, 42, &test_utils::TestLogger::new(), &scorer)
}
@ -2993,7 +2992,7 @@ mod tests {
let (secp_ctx, network_graph, mut net_graph_msg_handler, chain_monitor, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We will use a simple single-path route from
@ -3265,7 +3264,7 @@ mod tests {
// one of the latter hops is limited.
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
@ -3388,7 +3387,7 @@ mod tests {
fn ignore_fee_first_hop_test() {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]);
// Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50).
@ -3434,7 +3433,7 @@ mod tests {
fn simple_mpp_route_test() {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
@ -3565,7 +3564,7 @@ mod tests {
fn long_mpp_route_test() {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
@ -3727,7 +3726,7 @@ mod tests {
fn mpp_cheaper_route_test() {
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// This test checks that if we have two cheaper paths and one more expensive path,
@ -3894,7 +3893,7 @@ mod tests {
// if the fee is not properly accounted for, the behavior is different.
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// We need a route consisting of 2 paths:
@ -4063,7 +4062,7 @@ mod tests {
// path finding we realize that we found more capacity than we need.
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
@ -4220,7 +4219,7 @@ mod tests {
let network_graph = Arc::new(NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()));
let net_graph_msg_handler = NetGraphMsgHandler::new(Arc::clone(&network_graph), None, Arc::clone(&logger));
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[6]);
add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
@ -4349,7 +4348,7 @@ mod tests {
// we calculated fees on a higher value, resulting in us ignoring such paths.
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]);
// We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to
@ -4411,7 +4410,7 @@ mod tests {
// resulting in us thinking there is no possible path, even if other paths exist.
let (secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
@ -4478,7 +4477,7 @@ mod tests {
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
let logger = Arc::new(test_utils::TestLogger::new());
let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let payee = Payee::new(nodes[0]).with_features(InvoiceFeatures::known());
{
@ -4519,7 +4518,7 @@ mod tests {
let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes));
// Without penalizing each hop 100 msats, a longer path with lower fees is chosen.
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = get_route(
&our_id, &payee, &network_graph, None, 100, 42,
Arc::clone(&logger), &scorer
@ -4532,7 +4531,7 @@ mod tests {
// Applying a 100 msat penalty to each hop results in taking channels 7 and 10 to nodes[6]
// from nodes[2] rather than channel 6, 11, and 8, even though the longer path is cheaper.
let scorer = Scorer::with_fixed_penalty(100);
let scorer = test_utils::TestScorer::with_fixed_penalty(100);
let route = get_route(
&our_id, &payee, &network_graph, None, 100, 42,
Arc::clone(&logger), &scorer
@ -4575,7 +4574,7 @@ mod tests {
let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes));
// A path to nodes[6] exists when no penalties are applied to any channel.
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
let route = get_route(
&our_id, &payee, &network_graph, None, 100, 42,
Arc::clone(&logger), &scorer
@ -4704,7 +4703,7 @@ mod tests {
},
};
let graph = NetworkGraph::read(&mut d).unwrap();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut seed = random_init_seed() as usize;
@ -4735,7 +4734,7 @@ mod tests {
},
};
let graph = NetworkGraph::read(&mut d).unwrap();
let scorer = Scorer::with_fixed_penalty(0);
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut seed = random_init_seed() as usize;

View file

@ -44,33 +44,55 @@
//! # }
//! ```
//!
//! # Note
//!
//! If persisting [`Scorer`], it must be restored using the same [`Time`] parameterization. Using a
//! different type results in undefined behavior. Specifically, persisting when built with feature
//! `no-std` and restoring without it, or vice versa, uses different types and thus is undefined.
//!
//! [`find_route`]: crate::routing::router::find_route
use routing;
use ln::msgs::DecodeError;
use routing::network_graph::NodeId;
use routing::router::RouteHop;
use util::ser::{Readable, Writeable, Writer};
use prelude::*;
#[cfg(not(feature = "no-std"))]
use core::ops::Sub;
use core::time::Duration;
#[cfg(not(feature = "no-std"))]
use std::time::Instant;
use io::{self, Read};
/// [`routing::Score`] implementation that provides reasonable default behavior.
///
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
/// slightly higher fees are available. May also further penalize failed channels.
/// slightly higher fees are available. Will further penalize channels that fail to relay payments.
///
/// See [module-level documentation] for usage.
///
/// [module-level documentation]: crate::routing::scorer
pub struct Scorer {
pub type Scorer = ScorerUsingTime::<DefaultTime>;
/// Time used by [`Scorer`].
#[cfg(not(feature = "no-std"))]
pub type DefaultTime = std::time::Instant;
/// Time used by [`Scorer`].
#[cfg(feature = "no-std")]
pub type DefaultTime = Eternity;
/// [`routing::Score`] implementation parameterized by [`Time`].
///
/// See [`Scorer`] for details.
///
/// # Note
///
/// Mixing [`Time`] types between serialization and deserialization results in undefined behavior.
pub struct ScorerUsingTime<T: Time> {
params: ScoringParameters,
#[cfg(not(feature = "no-std"))]
channel_failures: HashMap<u64, (u64, Instant)>,
#[cfg(feature = "no-std")]
channel_failures: HashMap<u64, u64>,
// TODO: Remove entries of closed channels.
channel_failures: HashMap<u64, ChannelFailure<T>>,
}
/// Parameters for configuring [`Scorer`].
@ -78,19 +100,58 @@ pub struct ScoringParameters {
/// A fixed penalty in msats to apply to each channel.
pub base_penalty_msat: u64,
/// A penalty in msats to apply to a channel upon failure.
/// A penalty in msats to apply to a channel upon failing to relay a payment.
///
/// This may be reduced over time based on [`failure_penalty_half_life`].
/// This accumulates for each failure but may be reduced over time based on
/// [`failure_penalty_half_life`].
///
/// [`failure_penalty_half_life`]: Self::failure_penalty_half_life
pub failure_penalty_msat: u64,
/// The time needed before any accumulated channel failure penalties are cut in half.
#[cfg(not(feature = "no-std"))]
/// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
/// cut in half.
///
/// # Note
///
/// When time is an [`Eternity`], as is default when enabling feature `no-std`, it will never
/// elapse. Therefore, this penalty will never decay.
///
/// [`failure_penalty_msat`]: Self::failure_penalty_msat
pub failure_penalty_half_life: Duration,
}
impl Scorer {
impl_writeable_tlv_based!(ScoringParameters, {
(0, base_penalty_msat, required),
(2, failure_penalty_msat, required),
(4, failure_penalty_half_life, required),
});
/// Accounting for penalties against a channel for failing to relay any payments.
///
/// Penalties decay over time, though accumulate as more failures occur.
struct ChannelFailure<T: Time> {
/// Accumulated penalty in msats for the channel as of `last_failed`.
undecayed_penalty_msat: u64,
/// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
last_failed: T,
}
/// A measurement of time.
pub trait Time: Sub<Duration, Output = Self> where Self: Sized {
/// Returns an instance corresponding to the current moment.
fn now() -> Self;
/// Returns the amount of time elapsed since `self` was created.
fn elapsed(&self) -> Duration;
/// Returns the amount of time passed since the beginning of [`Time`].
///
/// Used during (de-)serialization.
fn duration_since_epoch() -> Duration;
}
impl<T: Time> ScorerUsingTime<T> {
/// Creates a new scorer using the given scoring parameters.
pub fn new(params: ScoringParameters) -> Self {
Self {
@ -105,20 +166,36 @@ impl Scorer {
Self::new(ScoringParameters {
base_penalty_msat: penalty_msat,
failure_penalty_msat: 0,
#[cfg(not(feature = "no-std"))]
failure_penalty_half_life: Duration::from_secs(0),
})
}
}
#[cfg(not(feature = "no-std"))]
fn decay_from(&self, penalty_msat: u64, last_failure: &Instant) -> u64 {
decay_from(penalty_msat, last_failure, self.params.failure_penalty_half_life)
impl<T: Time> ChannelFailure<T> {
fn new(failure_penalty_msat: u64) -> Self {
Self {
undecayed_penalty_msat: failure_penalty_msat,
last_failed: T::now(),
}
}
fn add_penalty(&mut self, failure_penalty_msat: u64, half_life: Duration) {
self.undecayed_penalty_msat = self.decayed_penalty_msat(half_life) + failure_penalty_msat;
self.last_failed = T::now();
}
fn decayed_penalty_msat(&self, half_life: Duration) -> u64 {
let decays = self.last_failed.elapsed().as_secs().checked_div(half_life.as_secs());
match decays {
Some(decays) => self.undecayed_penalty_msat >> decays,
None => 0,
}
}
}
impl Default for Scorer {
impl<T: Time> Default for ScorerUsingTime<T> {
fn default() -> Self {
Scorer::new(ScoringParameters::default())
Self::new(ScoringParameters::default())
}
}
@ -127,55 +204,105 @@ impl Default for ScoringParameters {
Self {
base_penalty_msat: 500,
failure_penalty_msat: 1024 * 1000,
#[cfg(not(feature = "no-std"))]
failure_penalty_half_life: Duration::from_secs(3600),
}
}
}
impl routing::Score for Scorer {
impl<T: Time> routing::Score for ScorerUsingTime<T> {
fn channel_penalty_msat(
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
) -> u64 {
#[cfg(not(feature = "no-std"))]
let failure_penalty_msat = match self.channel_failures.get(&short_channel_id) {
Some((penalty_msat, last_failure)) => self.decay_from(*penalty_msat, last_failure),
None => 0,
};
#[cfg(feature = "no-std")]
let failure_penalty_msat =
self.channel_failures.get(&short_channel_id).copied().unwrap_or(0);
let failure_penalty_msat = self.channel_failures
.get(&short_channel_id)
.map_or(0, |value| value.decayed_penalty_msat(self.params.failure_penalty_half_life));
self.params.base_penalty_msat + failure_penalty_msat
}
fn payment_path_failed(&mut self, _path: &Vec<RouteHop>, short_channel_id: u64) {
let failure_penalty_msat = self.params.failure_penalty_msat;
#[cfg(not(feature = "no-std"))]
{
let half_life = self.params.failure_penalty_half_life;
self.channel_failures
.entry(short_channel_id)
.and_modify(|(penalty_msat, last_failure)| {
let decayed_penalty = decay_from(*penalty_msat, last_failure, half_life);
*penalty_msat = decayed_penalty + failure_penalty_msat;
*last_failure = Instant::now();
})
.or_insert_with(|| (failure_penalty_msat, Instant::now()));
}
#[cfg(feature = "no-std")]
let half_life = self.params.failure_penalty_half_life;
self.channel_failures
.entry(short_channel_id)
.and_modify(|penalty_msat| *penalty_msat += failure_penalty_msat)
.or_insert(failure_penalty_msat);
.and_modify(|failure| failure.add_penalty(failure_penalty_msat, half_life))
.or_insert_with(|| ChannelFailure::new(failure_penalty_msat));
}
}
#[cfg(not(feature = "no-std"))]
fn decay_from(penalty_msat: u64, last_failure: &Instant, half_life: Duration) -> u64 {
let decays = last_failure.elapsed().as_secs().checked_div(half_life.as_secs());
match decays {
Some(decays) => penalty_msat >> decays,
None => 0,
impl Time for std::time::Instant {
fn now() -> Self {
std::time::Instant::now()
}
fn duration_since_epoch() -> Duration {
use std::time::SystemTime;
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
}
fn elapsed(&self) -> Duration {
std::time::Instant::elapsed(self)
}
}
/// A state in which time has no meaning.
pub struct Eternity;
impl Time for Eternity {
fn now() -> Self {
Self
}
fn duration_since_epoch() -> Duration {
Duration::from_secs(0)
}
fn elapsed(&self) -> Duration {
Duration::from_secs(0)
}
}
impl Sub<Duration> for Eternity {
type Output = Self;
fn sub(self, _other: Duration) -> Self {
self
}
}
impl<T: Time> Writeable for ScorerUsingTime<T> {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.params.write(w)?;
self.channel_failures.write(w)
}
}
impl<T: Time> Readable for ScorerUsingTime<T> {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(Self {
params: Readable::read(r)?,
channel_failures: Readable::read(r)?,
})
}
}
impl<T: Time> Writeable for ChannelFailure<T> {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.undecayed_penalty_msat.write(w)?;
(T::duration_since_epoch() - self.last_failed.elapsed()).write(w)
}
}
impl<T: Time> Readable for ChannelFailure<T> {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(Self {
undecayed_penalty_msat: Readable::read(r)?,
last_failed: T::now() - (T::duration_since_epoch() - Readable::read(r)?),
})
}
}

View file

@ -27,6 +27,7 @@ use bitcoin::consensus::Encodable;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{Txid, BlockHash};
use core::marker::Sized;
use core::time::Duration;
use ln::msgs::DecodeError;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
@ -911,3 +912,19 @@ impl Readable for String {
Ok(ret)
}
}
impl Writeable for Duration {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.as_secs().write(w)?;
self.subsec_nanos().write(w)
}
}
impl Readable for Duration {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let secs = Readable::read(r)?;
let nanos = Readable::read(r)?;
Ok(Duration::new(secs, nanos))
}
}

View file

@ -21,6 +21,7 @@ use ln::features::{ChannelFeatures, InitFeatures};
use ln::msgs;
use ln::msgs::OptionalField;
use ln::script::ShutdownScript;
use routing::scorer::{Eternity, ScorerUsingTime};
use util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use util::events;
use util::logger::{Logger, Level, Record};
@ -690,3 +691,6 @@ impl core::fmt::Debug for OnRegisterOutput {
.finish()
}
}
/// A scorer useful in testing, when the passage of time isn't a concern.
pub type TestScorer = ScorerUsingTime<Eternity>;