Skip to content

Commit fee7a73

Browse files
committed
Expose feature helper APIs through FFI
We add requires_* counterparts for every supports_* method on InitFeatures, completing the BOLT 9 feature flag coverage for FFI consumers. We export the existing feature helper methods for both InitFeatures and NodeFeatures through UniFFI. NodeFeatures had the same missing export path as InitFeatures and was noticed while addressing the InitFeatures FFI exposure. Additionally, we extend the Python full-cycle test to cover node features and init features on their real runtime paths.
1 parent 8048359 commit fee7a73

2 files changed

Lines changed: 185 additions & 101 deletions

File tree

bindings/python/src/ldk_node/test_ldk_node.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,29 @@ def expect_event(node, expected_event_type):
121121
return event
122122

123123

124+
def assert_feature_helpers_return_bool(test_case, features):
125+
feature_methods = [
126+
method_name for method_name in dir(features)
127+
if method_name.startswith("supports_") or method_name.startswith("requires_")
128+
]
129+
130+
test_case.assertGreater(len(feature_methods), 0)
131+
for method_name in feature_methods:
132+
with test_case.subTest(method_name=method_name):
133+
test_case.assertIsInstance(getattr(features, method_name)(), bool)
134+
135+
136+
def node_features_exposed(test_case, node_features):
137+
test_case.assertIsInstance(node_features, NodeFeatures)
138+
assert_feature_helpers_return_bool(test_case, node_features)
139+
140+
141+
def init_features_exposed(test_case, init_features):
142+
test_case.assertIsInstance(init_features, InitFeatures)
143+
assert_feature_helpers_return_bool(test_case, init_features)
144+
test_case.assertIsInstance(init_features.initial_routing_sync(), bool)
145+
146+
124147

125148
class TestLdkNode(unittest.TestCase):
126149
def setUp(self):
@@ -153,6 +176,10 @@ def test_channel_full_cycle(self):
153176
node_id_2 = node_2.node_id()
154177
print("Node ID 2:", node_id_2)
155178

179+
# Check node-announcement features exposed through NodeStatus.
180+
for node in [node_1, node_2]:
181+
node_features_exposed(self, node.status().node_features)
182+
156183
address_1 = node_1.onchain_payment().new_address()
157184
txid_1 = send_to_address(address_1, 100000)
158185
address_2 = node_2.onchain_payment().new_address()
@@ -200,6 +227,10 @@ def test_channel_full_cycle(self):
200227

201228
channel_ready_event_2 = expect_event(node_2, Event.CHANNEL_READY)
202229

230+
# Check negotiated init features exposed through ChannelDetails.
231+
for channel in [node_1.list_channels()[0], node_2.list_channels()[0]]:
232+
init_features_exposed(self, channel.counterparty.features)
233+
203234
description = Bolt11InvoiceDescription.DIRECT("asdf")
204235
invoice = node_2.bolt11_payment().receive(2500000, description, 9217)
205236
node_1.bolt11_payment().send(invoice, None)

0 commit comments

Comments
 (0)