Skip to content

Commit 103a194

Browse files
cmd/lncli: update listchannels output fields
Added scid_str as a string representation of chan_id, replacing chan_id with scid, and including chan_id(BOLT02) in lncli listchannels output. Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
1 parent 7f8e89f commit 103a194

File tree

2 files changed

+256
-13
lines changed

2 files changed

+256
-13
lines changed

cmd/commands/commands.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/jessevdk/go-flags"
2222
"github.com/lightningnetwork/lnd"
2323
"github.com/lightningnetwork/lnd/lnrpc"
24+
"github.com/lightningnetwork/lnd/lnwire"
2425
"github.com/lightningnetwork/lnd/routing"
2526
"github.com/lightningnetwork/lnd/routing/route"
2627
"github.com/lightningnetwork/lnd/signal"
@@ -50,6 +51,14 @@ var (
5051
customDataPattern = regexp.MustCompile(
5152
`"custom_channel_data":\s*"([0-9a-f]+)"`,
5253
)
54+
55+
chanIDPattern = regexp.MustCompile(
56+
`"chan_id":\s*"(\d+)"`,
57+
)
58+
59+
channelPointPattern = regexp.MustCompile(
60+
`"channel_point":\s*"([0-9a-fA-F]+:[0-9]+)"`,
61+
)
5362
)
5463

5564
// replaceCustomData replaces the custom channel data hex string with the
@@ -86,6 +95,96 @@ func replaceCustomData(jsonBytes []byte) []byte {
8695
return buf.Bytes()
8796
}
8897

98+
// replaceAndAppendScid replaces the chan_id with scid and appends the human
99+
// readable string representation of scid.
100+
func replaceAndAppendScid(jsonBytes []byte) []byte {
101+
// If there's nothing to replace, return the original JSON.
102+
if !chanIDPattern.Match(jsonBytes) {
103+
return jsonBytes
104+
}
105+
106+
replacedBytes := chanIDPattern.ReplaceAllFunc(
107+
jsonBytes, func(match []byte) []byte {
108+
// Extract the captured scid group from the match.
109+
chanID := chanIDPattern.FindStringSubmatch(
110+
string(match),
111+
)[1]
112+
113+
scid, err := strconv.ParseUint(chanID, 10, 64)
114+
if err != nil {
115+
return match
116+
}
117+
118+
// Format a new JSON field for the scid (chan_id),
119+
// including both its numeric representation and its
120+
// string representation (scid_str).
121+
scidStr := lnwire.NewShortChanIDFromInt(scid).
122+
AltString()
123+
updatedField := fmt.Sprintf(
124+
`"scid": "%d", "scid_str": "%s"`, scid, scidStr,
125+
)
126+
127+
// Replace the entire match with the new structure.
128+
return []byte(updatedField)
129+
},
130+
)
131+
132+
var buf bytes.Buffer
133+
err := json.Indent(&buf, replacedBytes, "", " ")
134+
if err != nil {
135+
// If we can't indent the JSON, it likely means the replacement
136+
// data wasn't correct, so we return the original JSON.
137+
return jsonBytes
138+
}
139+
140+
return buf.Bytes()
141+
}
142+
143+
// appendChanID appends the chan_id which is computed using the outpoint
144+
// of the funding transaction (the txid, and output index).
145+
func appendChanID(jsonBytes []byte) []byte {
146+
// If there's nothing to replace, return the original JSON.
147+
if !channelPointPattern.Match(jsonBytes) {
148+
return jsonBytes
149+
}
150+
151+
replacedBytes := channelPointPattern.ReplaceAllFunc(
152+
jsonBytes, func(match []byte) []byte {
153+
chanPoint := channelPointPattern.FindStringSubmatch(
154+
string(match),
155+
)[1]
156+
157+
chanOutpoint, err := wire.NewOutPointFromString(
158+
chanPoint,
159+
)
160+
if err != nil {
161+
return match
162+
}
163+
164+
// Format a new JSON field computed from the
165+
// channel_point (chan_id).
166+
chanID := lnwire.NewChanIDFromOutPoint(*chanOutpoint)
167+
updatedField := fmt.Sprintf(
168+
`"channel_point": "%s", "chan_id": "%s"`,
169+
chanPoint, chanID.String(),
170+
)
171+
172+
// Replace the entire match with the new structure.
173+
return []byte(updatedField)
174+
},
175+
)
176+
177+
var buf bytes.Buffer
178+
err := json.Indent(&buf, replacedBytes, "", " ")
179+
if err != nil {
180+
// If we can't indent the JSON, it likely means the replacement
181+
// data wasn't correct, so we return the original JSON.
182+
return jsonBytes
183+
}
184+
185+
return buf.Bytes()
186+
}
187+
89188
func getContext() context.Context {
90189
shutdownInterceptor, err := signal.Intercept()
91190
if err != nil {
@@ -120,8 +219,15 @@ func printRespJSON(resp proto.Message) {
120219
return
121220
}
122221

222+
// Replace custom_channel_data in the JSON.
123223
jsonBytesReplaced := replaceCustomData(jsonBytes)
124224

225+
// Replace chan_id with scid, and append scid_str and scid fields.
226+
jsonBytesReplaced = replaceAndAppendScid(jsonBytesReplaced)
227+
228+
// Append the chan_id field to the JSON.
229+
jsonBytesReplaced = appendChanID(jsonBytesReplaced)
230+
125231
fmt.Printf("%s\n", jsonBytesReplaced)
126232
}
127233

cmd/commands/commands_test.go

Lines changed: 150 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ func TestReplaceCustomData(t *testing.T) {
127127
t.Parallel()
128128

129129
testCases := []struct {
130-
name string
131-
data string
132-
replaceData string
133-
expected string
130+
name string
131+
data string
132+
expected string
134133
}{
135134
{
136135
name: "no replacement necessary",
@@ -139,10 +138,10 @@ func TestReplaceCustomData(t *testing.T) {
139138
},
140139
{
141140
name: "valid json with replacement",
142-
data: "{\"foo\":\"bar\",\"custom_channel_data\":\"" +
141+
data: `{"foo":"bar","custom_channel_data":"` +
143142
hex.EncodeToString([]byte(
144-
"{\"bar\":\"baz\"}",
145-
)) + "\"}",
143+
`{"bar":"baz"}`,
144+
)) + `"}`,
146145
expected: `{
147146
"foo": "bar",
148147
"custom_channel_data": {
@@ -152,10 +151,10 @@ func TestReplaceCustomData(t *testing.T) {
152151
},
153152
{
154153
name: "valid json with replacement and space",
155-
data: "{\"foo\":\"bar\",\"custom_channel_data\": \"" +
154+
data: `{"foo":"bar","custom_channel_data": "` +
156155
hex.EncodeToString([]byte(
157-
"{\"bar\":\"baz\"}",
158-
)) + "\"}",
156+
`{"bar":"baz"}`,
157+
)) + `"}`,
159158
expected: `{
160159
"foo": "bar",
161160
"custom_channel_data": {
@@ -178,9 +177,11 @@ func TestReplaceCustomData(t *testing.T) {
178177
"\"custom_channel_data\":\"a\"",
179178
},
180179
{
181-
name: "valid json, invalid hex, just formatted",
182-
data: "{\"custom_channel_data\":\"f\"}",
183-
expected: "{\n \"custom_channel_data\": \"f\"\n}",
180+
name: "valid json, invalid hex, just formatted",
181+
data: `{"custom_channel_data":"f"}`,
182+
expected: `{
183+
"custom_channel_data": "f"
184+
}`,
184185
},
185186
}
186187

@@ -191,3 +192,139 @@ func TestReplaceCustomData(t *testing.T) {
191192
})
192193
}
193194
}
195+
196+
// TestReplaceAndAppendScid tests whether chan_id is replaced with scid and
197+
// scid_str in the JSON console output.
198+
func TestReplaceAndAppendScid(t *testing.T) {
199+
t.Parallel()
200+
201+
testCases := []struct {
202+
name string
203+
data string
204+
expected string
205+
}{
206+
{
207+
name: "no replacement necessary",
208+
data: "foo",
209+
expected: "foo",
210+
},
211+
{
212+
name: "valid json with replacement",
213+
data: `{"foo":"bar","chan_id":"829031767408640"}`,
214+
expected: `{
215+
"foo": "bar",
216+
"scid": "829031767408640",
217+
"scid_str": "754x1x0"
218+
}`,
219+
},
220+
{
221+
name: "valid json with replacement and space",
222+
data: `{"foo":"bar","chan_id": "829031767408640"}`,
223+
expected: `{
224+
"foo": "bar",
225+
"scid": "829031767408640",
226+
"scid_str": "754x1x0"
227+
}`,
228+
},
229+
{
230+
name: "doesn't match pattern, returned identical",
231+
data: "this ain't even json, and no chan_id " +
232+
"either",
233+
expected: "this ain't even json, and no chan_id " +
234+
"either",
235+
},
236+
{
237+
name: "invalid json",
238+
data: "this ain't json, " +
239+
"\"chan_id\":\"18446744073709551616\"",
240+
expected: "this ain't json, " +
241+
"\"chan_id\":\"18446744073709551616\"",
242+
},
243+
{
244+
name: "valid json, invalid uint, just formatted",
245+
data: `{"chan_id":"18446744073709551616"}`,
246+
expected: `{
247+
"chan_id": "18446744073709551616"
248+
}`,
249+
},
250+
}
251+
252+
for _, tc := range testCases {
253+
t.Run(tc.name, func(t *testing.T) {
254+
result := replaceAndAppendScid([]byte(tc.data))
255+
require.Equal(t, tc.expected, string(result))
256+
})
257+
}
258+
}
259+
260+
// TestAppendChanID tests whether chan_id (BOLT02) is appended
261+
// to the JSON console output.
262+
func TestAppendChanID(t *testing.T) {
263+
t.Parallel()
264+
265+
testCases := []struct {
266+
name string
267+
data string
268+
expected string
269+
}{
270+
{
271+
name: "no amendment necessary",
272+
data: "foo",
273+
expected: "foo",
274+
},
275+
{
276+
name: "valid json with amendment",
277+
data: `{"foo":"bar","channel_point":"6ab312e3b744e` +
278+
`1b80a33a6541697df88766515c31c08e839bf11dc` +
279+
`9fcc036a19:0"}`,
280+
expected: `{
281+
"foo": "bar",
282+
"channel_point": "6ab312e3b744e1b80a33a6541697df88766515c31c` +
283+
`08e839bf11dc9fcc036a19:0",
284+
"chan_id": "196a03cc9fdc11bf39e8081cc315657688df971654a` +
285+
`6330ab8e144b7e312b36a"
286+
}`,
287+
},
288+
{
289+
name: "valid json with amendment and space",
290+
data: `{"foo":"bar","channel_point": "6ab312e3b744e` +
291+
`1b80a33a6541697df88766515c31c08e839bf11dc` +
292+
`9fcc036a19:0"}`,
293+
expected: `{
294+
"foo": "bar",
295+
"channel_point": "6ab312e3b744e1b80a33a6541697df88766515c31c` +
296+
`08e839bf11dc9fcc036a19:0",
297+
"chan_id": "196a03cc9fdc11bf39e8081cc315657688df971654a` +
298+
`6330ab8e144b7e312b36a"
299+
}`,
300+
},
301+
{
302+
name: "doesn't match pattern, returned identical",
303+
data: "this ain't even json, and no channel_point " +
304+
"either",
305+
expected: "this ain't even json, and no channel_point" +
306+
" either",
307+
},
308+
{
309+
name: "invalid json",
310+
data: "this ain't json, " +
311+
"\"channel_point\":\"f:0\"",
312+
expected: "this ain't json, " +
313+
"\"channel_point\":\"f:0\"",
314+
},
315+
{
316+
name: "valid json with invalid outpoint, formatted",
317+
data: `{"channel_point":"f:0"}`,
318+
expected: `{
319+
"channel_point": "f:0"
320+
}`,
321+
},
322+
}
323+
324+
for _, tc := range testCases {
325+
t.Run(tc.name, func(t *testing.T) {
326+
result := appendChanID([]byte(tc.data))
327+
require.Equal(t, tc.expected, string(result))
328+
})
329+
}
330+
}

0 commit comments

Comments
 (0)