mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-28 10:41:57 +01:00
Merge pull request #9390 from NishantBansal2003/append-channel
Enhance `lncli` listchannels command with the chan_id and short_chan_id (human readable format)
This commit is contained in:
commit
572784a6a1
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/jessevdk/go-flags"
|
"github.com/jessevdk/go-flags"
|
||||||
"github.com/lightningnetwork/lnd"
|
"github.com/lightningnetwork/lnd"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/signal"
|
"github.com/lightningnetwork/lnd/signal"
|
||||||
@ -50,6 +51,14 @@ var (
|
|||||||
customDataPattern = regexp.MustCompile(
|
customDataPattern = regexp.MustCompile(
|
||||||
`"custom_channel_data":\s*"([0-9a-f]+)"`,
|
`"custom_channel_data":\s*"([0-9a-f]+)"`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
chanIDPattern = regexp.MustCompile(
|
||||||
|
`"chan_id":\s*"(\d+)"`,
|
||||||
|
)
|
||||||
|
|
||||||
|
channelPointPattern = regexp.MustCompile(
|
||||||
|
`"channel_point":\s*"([0-9a-fA-F]+:[0-9]+)"`,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// replaceCustomData replaces the custom channel data hex string with the
|
// replaceCustomData replaces the custom channel data hex string with the
|
||||||
@ -86,6 +95,96 @@ func replaceCustomData(jsonBytes []byte) []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replaceAndAppendScid replaces the chan_id with scid and appends the human
|
||||||
|
// readable string representation of scid.
|
||||||
|
func replaceAndAppendScid(jsonBytes []byte) []byte {
|
||||||
|
// If there's nothing to replace, return the original JSON.
|
||||||
|
if !chanIDPattern.Match(jsonBytes) {
|
||||||
|
return jsonBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
replacedBytes := chanIDPattern.ReplaceAllFunc(
|
||||||
|
jsonBytes, func(match []byte) []byte {
|
||||||
|
// Extract the captured scid group from the match.
|
||||||
|
chanID := chanIDPattern.FindStringSubmatch(
|
||||||
|
string(match),
|
||||||
|
)[1]
|
||||||
|
|
||||||
|
scid, err := strconv.ParseUint(chanID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a new JSON field for the scid (chan_id),
|
||||||
|
// including both its numeric representation and its
|
||||||
|
// string representation (scid_str).
|
||||||
|
scidStr := lnwire.NewShortChanIDFromInt(scid).
|
||||||
|
AltString()
|
||||||
|
updatedField := fmt.Sprintf(
|
||||||
|
`"scid": "%d", "scid_str": "%s"`, scid, scidStr,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace the entire match with the new structure.
|
||||||
|
return []byte(updatedField)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := json.Indent(&buf, replacedBytes, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
// If we can't indent the JSON, it likely means the replacement
|
||||||
|
// data wasn't correct, so we return the original JSON.
|
||||||
|
return jsonBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendChanID appends the chan_id which is computed using the outpoint
|
||||||
|
// of the funding transaction (the txid, and output index).
|
||||||
|
func appendChanID(jsonBytes []byte) []byte {
|
||||||
|
// If there's nothing to replace, return the original JSON.
|
||||||
|
if !channelPointPattern.Match(jsonBytes) {
|
||||||
|
return jsonBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
replacedBytes := channelPointPattern.ReplaceAllFunc(
|
||||||
|
jsonBytes, func(match []byte) []byte {
|
||||||
|
chanPoint := channelPointPattern.FindStringSubmatch(
|
||||||
|
string(match),
|
||||||
|
)[1]
|
||||||
|
|
||||||
|
chanOutpoint, err := wire.NewOutPointFromString(
|
||||||
|
chanPoint,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a new JSON field computed from the
|
||||||
|
// channel_point (chan_id).
|
||||||
|
chanID := lnwire.NewChanIDFromOutPoint(*chanOutpoint)
|
||||||
|
updatedField := fmt.Sprintf(
|
||||||
|
`"channel_point": "%s", "chan_id": "%s"`,
|
||||||
|
chanPoint, chanID.String(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace the entire match with the new structure.
|
||||||
|
return []byte(updatedField)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := json.Indent(&buf, replacedBytes, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
// If we can't indent the JSON, it likely means the replacement
|
||||||
|
// data wasn't correct, so we return the original JSON.
|
||||||
|
return jsonBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
func getContext() context.Context {
|
func getContext() context.Context {
|
||||||
shutdownInterceptor, err := signal.Intercept()
|
shutdownInterceptor, err := signal.Intercept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -120,8 +219,15 @@ func printRespJSON(resp proto.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace custom_channel_data in the JSON.
|
||||||
jsonBytesReplaced := replaceCustomData(jsonBytes)
|
jsonBytesReplaced := replaceCustomData(jsonBytes)
|
||||||
|
|
||||||
|
// Replace chan_id with scid, and append scid_str and scid fields.
|
||||||
|
jsonBytesReplaced = replaceAndAppendScid(jsonBytesReplaced)
|
||||||
|
|
||||||
|
// Append the chan_id field to the JSON.
|
||||||
|
jsonBytesReplaced = appendChanID(jsonBytesReplaced)
|
||||||
|
|
||||||
fmt.Printf("%s\n", jsonBytesReplaced)
|
fmt.Printf("%s\n", jsonBytesReplaced)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,10 +127,9 @@ func TestReplaceCustomData(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
data string
|
data string
|
||||||
replaceData string
|
expected string
|
||||||
expected string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no replacement necessary",
|
name: "no replacement necessary",
|
||||||
@ -139,10 +138,10 @@ func TestReplaceCustomData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid json with replacement",
|
name: "valid json with replacement",
|
||||||
data: "{\"foo\":\"bar\",\"custom_channel_data\":\"" +
|
data: `{"foo":"bar","custom_channel_data":"` +
|
||||||
hex.EncodeToString([]byte(
|
hex.EncodeToString([]byte(
|
||||||
"{\"bar\":\"baz\"}",
|
`{"bar":"baz"}`,
|
||||||
)) + "\"}",
|
)) + `"}`,
|
||||||
expected: `{
|
expected: `{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
"custom_channel_data": {
|
"custom_channel_data": {
|
||||||
@ -152,10 +151,10 @@ func TestReplaceCustomData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid json with replacement and space",
|
name: "valid json with replacement and space",
|
||||||
data: "{\"foo\":\"bar\",\"custom_channel_data\": \"" +
|
data: `{"foo":"bar","custom_channel_data": "` +
|
||||||
hex.EncodeToString([]byte(
|
hex.EncodeToString([]byte(
|
||||||
"{\"bar\":\"baz\"}",
|
`{"bar":"baz"}`,
|
||||||
)) + "\"}",
|
)) + `"}`,
|
||||||
expected: `{
|
expected: `{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
"custom_channel_data": {
|
"custom_channel_data": {
|
||||||
@ -178,9 +177,11 @@ func TestReplaceCustomData(t *testing.T) {
|
|||||||
"\"custom_channel_data\":\"a\"",
|
"\"custom_channel_data\":\"a\"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid json, invalid hex, just formatted",
|
name: "valid json, invalid hex, just formatted",
|
||||||
data: "{\"custom_channel_data\":\"f\"}",
|
data: `{"custom_channel_data":"f"}`,
|
||||||
expected: "{\n \"custom_channel_data\": \"f\"\n}",
|
expected: `{
|
||||||
|
"custom_channel_data": "f"
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,3 +192,139 @@ func TestReplaceCustomData(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReplaceAndAppendScid tests whether chan_id is replaced with scid and
|
||||||
|
// scid_str in the JSON console output.
|
||||||
|
func TestReplaceAndAppendScid(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no replacement necessary",
|
||||||
|
data: "foo",
|
||||||
|
expected: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json with replacement",
|
||||||
|
data: `{"foo":"bar","chan_id":"829031767408640"}`,
|
||||||
|
expected: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"scid": "829031767408640",
|
||||||
|
"scid_str": "754x1x0"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json with replacement and space",
|
||||||
|
data: `{"foo":"bar","chan_id": "829031767408640"}`,
|
||||||
|
expected: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"scid": "829031767408640",
|
||||||
|
"scid_str": "754x1x0"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't match pattern, returned identical",
|
||||||
|
data: "this ain't even json, and no chan_id " +
|
||||||
|
"either",
|
||||||
|
expected: "this ain't even json, and no chan_id " +
|
||||||
|
"either",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid json",
|
||||||
|
data: "this ain't json, " +
|
||||||
|
"\"chan_id\":\"18446744073709551616\"",
|
||||||
|
expected: "this ain't json, " +
|
||||||
|
"\"chan_id\":\"18446744073709551616\"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json, invalid uint, just formatted",
|
||||||
|
data: `{"chan_id":"18446744073709551616"}`,
|
||||||
|
expected: `{
|
||||||
|
"chan_id": "18446744073709551616"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := replaceAndAppendScid([]byte(tc.data))
|
||||||
|
require.Equal(t, tc.expected, string(result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAppendChanID tests whether chan_id (BOLT02) is appended
|
||||||
|
// to the JSON console output.
|
||||||
|
func TestAppendChanID(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no amendment necessary",
|
||||||
|
data: "foo",
|
||||||
|
expected: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json with amendment",
|
||||||
|
data: `{"foo":"bar","channel_point":"6ab312e3b744e` +
|
||||||
|
`1b80a33a6541697df88766515c31c08e839bf11dc` +
|
||||||
|
`9fcc036a19:0"}`,
|
||||||
|
expected: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"channel_point": "6ab312e3b744e1b80a33a6541697df88766515c31c` +
|
||||||
|
`08e839bf11dc9fcc036a19:0",
|
||||||
|
"chan_id": "196a03cc9fdc11bf39e8081cc315657688df971654a` +
|
||||||
|
`6330ab8e144b7e312b36a"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json with amendment and space",
|
||||||
|
data: `{"foo":"bar","channel_point": "6ab312e3b744e` +
|
||||||
|
`1b80a33a6541697df88766515c31c08e839bf11dc` +
|
||||||
|
`9fcc036a19:0"}`,
|
||||||
|
expected: `{
|
||||||
|
"foo": "bar",
|
||||||
|
"channel_point": "6ab312e3b744e1b80a33a6541697df88766515c31c` +
|
||||||
|
`08e839bf11dc9fcc036a19:0",
|
||||||
|
"chan_id": "196a03cc9fdc11bf39e8081cc315657688df971654a` +
|
||||||
|
`6330ab8e144b7e312b36a"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't match pattern, returned identical",
|
||||||
|
data: "this ain't even json, and no channel_point " +
|
||||||
|
"either",
|
||||||
|
expected: "this ain't even json, and no channel_point" +
|
||||||
|
" either",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid json",
|
||||||
|
data: "this ain't json, " +
|
||||||
|
"\"channel_point\":\"f:0\"",
|
||||||
|
expected: "this ain't json, " +
|
||||||
|
"\"channel_point\":\"f:0\"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid json with invalid outpoint, formatted",
|
||||||
|
data: `{"channel_point":"f:0"}`,
|
||||||
|
expected: `{
|
||||||
|
"channel_point": "f:0"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := appendChanID([]byte(tc.data))
|
||||||
|
require.Equal(t, tc.expected, string(result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -111,6 +111,12 @@
|
|||||||
now update the channel policy if the edge was not found in the graph
|
now update the channel policy if the edge was not found in the graph
|
||||||
database if the `create_missing_edge` flag is set.
|
database if the `create_missing_edge` flag is set.
|
||||||
|
|
||||||
|
* [Enhance](https://github.com/lightningnetwork/lnd/pull/9390) the
|
||||||
|
`lncli listchannels` output by adding the human readable short
|
||||||
|
channel id and the channel id defined in BOLT02. Moreover change
|
||||||
|
the misnomer of `chan_id` which was describing the short channel
|
||||||
|
id to `scid` to represent what it really is.
|
||||||
|
|
||||||
# Improvements
|
# Improvements
|
||||||
## Functional Updates
|
## Functional Updates
|
||||||
|
|
||||||
@ -282,6 +288,7 @@ The underlying functionality between those two options remain the same.
|
|||||||
* hieblmi
|
* hieblmi
|
||||||
* Jesse de Wit
|
* Jesse de Wit
|
||||||
* Keagan McClelland
|
* Keagan McClelland
|
||||||
|
* Nishant Bansal
|
||||||
* Oliver Gugger
|
* Oliver Gugger
|
||||||
* Pins
|
* Pins
|
||||||
* Viktor Tigerström
|
* Viktor Tigerström
|
||||||
|
@ -56,6 +56,12 @@ func (c ShortChannelID) String() string {
|
|||||||
return fmt.Sprintf("%d:%d:%d", c.BlockHeight, c.TxIndex, c.TxPosition)
|
return fmt.Sprintf("%d:%d:%d", c.BlockHeight, c.TxIndex, c.TxPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AltString generates a human-readable representation of the channel ID
|
||||||
|
// with 'x' as a separator.
|
||||||
|
func (c ShortChannelID) AltString() string {
|
||||||
|
return fmt.Sprintf("%dx%dx%d", c.BlockHeight, c.TxIndex, c.TxPosition)
|
||||||
|
}
|
||||||
|
|
||||||
// Record returns a TLV record that can be used to encode/decode a
|
// Record returns a TLV record that can be used to encode/decode a
|
||||||
// ShortChannelID to/from a TLV stream.
|
// ShortChannelID to/from a TLV stream.
|
||||||
func (c *ShortChannelID) Record() tlv.Record {
|
func (c *ShortChannelID) Record() tlv.Record {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user