mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
lnd: add aux data parser
This commit adds an optional data parser that can inspect and in-place format custom data of certain RPC messages. We don't add an implementation of the interface itself, as that will be provided by external components when packaging up lnd as a bundle with other software.
This commit is contained in:
parent
5e1a98cd43
commit
d49da574e3
@ -178,6 +178,10 @@ type AuxComponents struct {
|
||||
// AuxSigner is an optional signer that can be used to sign auxiliary
|
||||
// leaves for certain custom channel types.
|
||||
AuxSigner fn.Option[lnwallet.AuxSigner]
|
||||
|
||||
// AuxDataParser is an optional data parser that can be used to parse
|
||||
// auxiliary data for certain custom channel types.
|
||||
AuxDataParser fn.Option[AuxDataParser]
|
||||
}
|
||||
|
||||
// DefaultWalletImpl is the default implementation of our normal, btcwallet
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/subscribe"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -104,6 +105,10 @@ type RouterBackend struct {
|
||||
// TODO(yy): remove this config after the new status code is fully
|
||||
// deployed to the network(v0.20.0).
|
||||
UseStatusInitiated bool
|
||||
|
||||
// ParseCustomChannelData is a function that can be used to parse custom
|
||||
// channel data from the first hop of a route.
|
||||
ParseCustomChannelData func(message proto.Message) error
|
||||
}
|
||||
|
||||
// MissionControl defines the mission control dependencies of routerrpc.
|
||||
@ -596,8 +601,14 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error)
|
||||
|
||||
resp.CustomChannelData = customData
|
||||
|
||||
// TODO(guggero): Feed the route into the custom data parser
|
||||
// (part 3 of the mega PR series).
|
||||
// Allow the aux data parser to parse the custom records into
|
||||
// a human-readable JSON (if available).
|
||||
if r.ParseCustomChannelData != nil {
|
||||
err := r.ParseCustomChannelData(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
incomingAmt := route.TotalAmount
|
||||
|
62
rpcserver.go
62
rpcserver.go
@ -87,6 +87,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
)
|
||||
|
||||
@ -579,6 +580,17 @@ func MainRPCServerPermissions() map[string][]bakery.Op {
|
||||
}
|
||||
}
|
||||
|
||||
// AuxDataParser is an interface that is used to parse auxiliary custom data
|
||||
// within RPC messages. This is used to transform binary blobs to human-readable
|
||||
// JSON representations.
|
||||
type AuxDataParser interface {
|
||||
// InlineParseCustomData replaces any custom data binary blob in the
|
||||
// given RPC message with its corresponding JSON formatted data. This
|
||||
// transforms the binary (likely TLV encoded) data to a human-readable
|
||||
// JSON representation (still as byte slice).
|
||||
InlineParseCustomData(msg proto.Message) error
|
||||
}
|
||||
|
||||
// rpcServer is a gRPC, RPC front end to the lnd daemon.
|
||||
// TODO(roasbeef): pagination support for the list-style calls
|
||||
type rpcServer struct {
|
||||
@ -731,6 +743,20 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
||||
},
|
||||
SetChannelAuto: s.chanStatusMgr.RequestAuto,
|
||||
UseStatusInitiated: subServerCgs.RouterRPC.UseStatusInitiated,
|
||||
ParseCustomChannelData: func(msg proto.Message) error {
|
||||
err = fn.MapOptionZ(
|
||||
r.server.implCfg.AuxDataParser,
|
||||
func(parser AuxDataParser) error {
|
||||
return parser.InlineParseCustomData(msg)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing custom data: "+
|
||||
"%w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
genInvoiceFeatures := func() *lnwire.FeatureVector {
|
||||
@ -3596,7 +3622,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context,
|
||||
unsettledRemoteBalance, pendingOpenLocalBalance,
|
||||
pendingOpenRemoteBalance)
|
||||
|
||||
return &lnrpc.ChannelBalanceResponse{
|
||||
resp := &lnrpc.ChannelBalanceResponse{
|
||||
LocalBalance: &lnrpc.Amount{
|
||||
Sat: uint64(localBalance.ToSatoshis()),
|
||||
Msat: uint64(localBalance),
|
||||
@ -3626,7 +3652,19 @@ func (r *rpcServer) ChannelBalance(ctx context.Context,
|
||||
// Deprecated fields.
|
||||
Balance: int64(localBalance.ToSatoshis()),
|
||||
PendingOpenBalance: int64(pendingOpenLocalBalance.ToSatoshis()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = fn.MapOptionZ(
|
||||
r.server.implCfg.AuxDataParser,
|
||||
func(parser AuxDataParser) error {
|
||||
return parser.InlineParseCustomData(resp)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing custom data: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type (
|
||||
@ -4068,6 +4106,16 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
|
||||
resp.WaitingCloseChannels = waitingCloseChannels
|
||||
resp.TotalLimboBalance += limbo
|
||||
|
||||
err = fn.MapOptionZ(
|
||||
r.server.implCfg.AuxDataParser,
|
||||
func(parser AuxDataParser) error {
|
||||
return parser.InlineParseCustomData(resp)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing custom data: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@ -4382,6 +4430,16 @@ func (r *rpcServer) ListChannels(ctx context.Context,
|
||||
resp.Channels = append(resp.Channels, channel)
|
||||
}
|
||||
|
||||
err = fn.MapOptionZ(
|
||||
r.server.implCfg.AuxDataParser,
|
||||
func(parser AuxDataParser) error {
|
||||
return parser.InlineParseCustomData(resp)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing custom data: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,79 @@
|
||||
package lnd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGetAllPermissions(t *testing.T) {
|
||||
perms := GetAllPermissions()
|
||||
|
||||
// Currently there are there are 16 entity:action pairs in use.
|
||||
// Currently there are 16 entity:action pairs in use.
|
||||
assert.Equal(t, len(perms), 16)
|
||||
}
|
||||
|
||||
// mockDataParser is a mock implementation of the AuxDataParser interface.
|
||||
type mockDataParser struct {
|
||||
}
|
||||
|
||||
// InlineParseCustomData replaces any custom data binary blob in the given RPC
|
||||
// message with its corresponding JSON formatted data. This transforms the
|
||||
// binary (likely TLV encoded) data to a human-readable JSON representation
|
||||
// (still as byte slice).
|
||||
func (m *mockDataParser) InlineParseCustomData(msg proto.Message) error {
|
||||
switch m := msg.(type) {
|
||||
case *lnrpc.ChannelBalanceResponse:
|
||||
m.CustomChannelData = []byte(`{"foo": "bar"}`)
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("mock only supports ChannelBalanceResponse")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuxDataParser(t *testing.T) {
|
||||
// We create an empty channeldb, so we can fetch some channels.
|
||||
cdb, err := channeldb.Open(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, cdb.Close())
|
||||
})
|
||||
|
||||
r := &rpcServer{
|
||||
server: &server{
|
||||
chanStateDB: cdb.ChannelStateDB(),
|
||||
implCfg: &ImplementationCfg{
|
||||
AuxComponents: AuxComponents{
|
||||
AuxDataParser: fn.Some[AuxDataParser](
|
||||
&mockDataParser{},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// With the aux data parser in place, we should get a formatted JSON
|
||||
// in the custom channel data field.
|
||||
resp, err := r.ChannelBalance(nil, &lnrpc.ChannelBalanceRequest{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, []byte(`{"foo": "bar"}`), resp.CustomChannelData)
|
||||
|
||||
// If we don't supply the aux data parser, we should get the raw binary
|
||||
// data. Which in this case is just two VarInt fields (1 byte each) that
|
||||
// represent the value of 0 (zero active and zero pending channels).
|
||||
r.server.implCfg.AuxComponents.AuxDataParser = fn.None[AuxDataParser]()
|
||||
|
||||
resp, err = r.ChannelBalance(nil, &lnrpc.ChannelBalanceRequest{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, []byte{0x00, 0x00}, resp.CustomChannelData)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user