Skip to content

Commit

Permalink
[wip] multi: add draft impl for virtual links
Browse files Browse the repository at this point in the history
  • Loading branch information
champo committed Feb 21, 2020
1 parent aff9bb4 commit b3b2f7f
Show file tree
Hide file tree
Showing 9 changed files with 1,520 additions and 4 deletions.
4 changes: 0 additions & 4 deletions htlcswitch/interfaces.go
@@ -1,7 +1,6 @@
package htlcswitch

import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer"
Expand Down Expand Up @@ -77,9 +76,6 @@ type ChannelLink interface {
// possible).
HandleChannelUpdate(lnwire.Message)

// ChannelPoint returns the channel outpoint for the channel link.
ChannelPoint() *wire.OutPoint

// ChanID returns the channel ID for the channel link. The channel ID
// is a more compact representation of a channel's full outpoint.
ChanID() lnwire.ChannelID
Expand Down
233 changes: 233 additions & 0 deletions htlcswitch/virtual_link.go
@@ -0,0 +1,233 @@
package htlcswitch

import (
"encoding/hex"
"errors"
"fmt"
"net"

"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"

"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
)

type virtualLink struct {
mailBox MailBox
id lnwire.ChannelID
peer *virtualPeer
packet *htlcPacket
forwardPackets func(chan struct{}, ...*htlcPacket) chan error
quit chan struct{}
circuitModifier CircuitModifier
htlcAccepted func(lnwire.ChannelID, lnwire.Message, uint32)
}

type VirtualLink interface {
SettleHtlc(preimage lntypes.Preimage) error
}

func NewVirtualLink(pubKey [33]byte, chanId [32]byte,
fwdPackets func(chan struct{}, ...*htlcPacket) chan error,
circuitModifier CircuitModifier,
htlcAccepted func(lnwire.ChannelID, lnwire.Message, uint32)) ChannelLink {

identityKey, err := btcec.ParsePubKey(pubKey[:], btcec.S256())
if err != nil {
panic(fmt.Sprintf("Failed to parse pubkey for peer: %v", err))
}

// FIXME: I need an HTLCNotifier instance so I can tell it all about the HTLCs here
return &virtualLink{
id: chanId,
peer: &virtualPeer{
pubKey: pubKey,
identityKey: identityKey,
},
forwardPackets: fwdPackets,
circuitModifier: circuitModifier,
htlcAccepted: htlcAccepted,
}
}

func (v *virtualLink) SettleHtlc(preimage lntypes.Preimage) error {
log.Errorf("Settling %v", preimage.String())

log.Errorf("Out %v In %v", v.packet.outgoingChanID, v.packet.incomingChanID)

settlePacket := &htlcPacket{
outgoingChanID: v.ShortChanID(),
outgoingHTLCID: v.packet.outgoingHTLCID,
incomingHTLCID: v.packet.incomingHTLCID,
incomingChanID: v.packet.incomingChanID,
htlc: &lnwire.UpdateFulfillHTLC{
PaymentPreimage: preimage,
},
}

errChan := v.forwardPackets(v.quit, settlePacket)
go func() {
for {
err, ok := <-errChan
if !ok {
// Err chan has been drained or switch is shutting
// down. Either way, return.
return
}

if err == nil {
continue
}

log.Errorf("unhandled error while forwarding htlc packet over htlcswitch: %v", err)
}
}()

return nil
}

func (v *virtualLink) HandleSwitchPacket(packet *htlcPacket) error {
log.Errorf("HandleSwitchPacket %v", packet)
v.packet = packet
v.circuitModifier.OpenCircuits(packet.keystone())

htlc := packet.htlc.(*lnwire.UpdateAddHTLC)
log.Errorf("Payment hash = %v\n", hex.EncodeToString(htlc.PaymentHash[:]))

// This id actually needs to increment! Otherwise we use already existing circuits
// That, or we have to, you know, close circuits :D
packet.outgoingHTLCID = 0
packet.outgoingChanID = v.ShortChanID()

go v.htlcAccepted(v.id, packet.htlc, packet.outgoingTimeout)

// TODO: We need to be able to cancel forwards at some point
return nil
}

func (v *virtualLink) HandleChannelUpdate(msg lnwire.Message) {
log.Errorf("HandleChannelUpdate %v", msg)
}

func (v *virtualLink) ChanID() lnwire.ChannelID {
log.Errorf("Got asked for ChanID")
return v.id
}

func (v *virtualLink) ShortChanID() lnwire.ShortChannelID {
log.Errorf("Got asked for ShortChanID")
short := lnwire.ShortChannelID{
BlockHeight: 1,
TxIndex: 1,
TxPosition: 0,
}
log.Errorf("short chan id = %v", short.ToUint64())

return short
}

func (v *virtualLink) UpdateShortChanID() (lnwire.ShortChannelID, error) {
// Noop
return v.ShortChanID(), nil
}

func (v *virtualLink) UpdateForwardingPolicy(ForwardingPolicy) {
// Noop
}

func (v *virtualLink) CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
amtToForward lnwire.MilliSatoshi, incomingTimeout, outgoingTimeout uint32, heightNow uint32) LinkError {
log.Errorf("CheckHtlcForward %v", hex.EncodeToString(payHash[:]))
return nil
}

func (v *virtualLink) CheckHtlcTransit(payHash [32]byte, amt lnwire.MilliSatoshi,
timeout uint32, heightNow uint32) LinkError {
log.Errorf("CheckHtlcTransit %v", hex.EncodeToString(payHash[:]))
return nil
}

func (v *virtualLink) Bandwidth() lnwire.MilliSatoshi {
// There's no good value here, so we just settle for a fixed value
// This is 0.1BTC
return 100000000 * 1000
}

func (v *virtualLink) Stats() (uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
// FIXME: record and return stats
return 0, 0, 0
}

func (v *virtualLink) Peer() lnpeer.Peer {
return v.peer
}

func (v *virtualLink) EligibleToForward() bool {
return true
}

func (v *virtualLink) AttachMailBox(mailBox MailBox) {
// FIXME: The mailbox is a mandatory thing, other subsystems might write directly to it
v.mailBox = mailBox
}

func (v *virtualLink) Start() error {
// TODO: Needs impl
return nil
}

func (v *virtualLink) Stop() {
// TODO: Needs impl
close(v.quit)
}

type virtualPeer struct {
pubKey [33]byte
identityKey *btcec.PublicKey
}

func (vp *virtualPeer) SendMessage(sync bool, msgs ...lnwire.Message) error {
// This would seem to be used by subsystems other than the htlcswitch, so it should be safe to not implement
panic("Cant send messages to virtual peers")
}

func (vp *virtualPeer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error {
// This would seem to be used by subsystems other than the htlcswitch, so it should be safe to not implement
panic("Cant send messages to virtual peers")
}

func (vp *virtualPeer) AddNewChannel(channel *channeldb.OpenChannel, cancel <-chan struct{}) error {
return errors.New("can't add a channel to a virtual link")
}

func (vp *virtualPeer) WipeChannel(*wire.OutPoint) error {
return errors.New("can't wipe a channel from a virtual link")
}

func (vp *virtualPeer) PubKey() [33]byte {
return vp.pubKey
}

func (vp *virtualPeer) IdentityKey() *btcec.PublicKey {
return vp.identityKey
}

func (vp *virtualPeer) Address() net.Addr {
panic("can't ask the Address from a virtualPeer")
}

func (vp *virtualPeer) QuitSignal() <-chan struct{} {
panic("can't ask the QuitSignal from a virtualPeer")
}

func (vp *virtualPeer) LocalFeatures() *lnwire.FeatureVector {
return lnwire.NewFeatureVector(nil, nil)
}

func (vp *virtualPeer) RemoteFeatures() *lnwire.FeatureVector {
// TODO: When we implement MPP we have to declare that here
return lnwire.NewFeatureVector(nil, nil)
}
24 changes: 24 additions & 0 deletions lnrpc/virtualchannelsrpc/config_active.go
@@ -0,0 +1,24 @@
package virtualchannelsrpc

import (
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/macaroons"
)

// Config is the primary configuration struct for the RPC server. It
// contains all the items required for the rpc server to carry out its
// duties. The fields with struct tags are meant to be parsed as normal
// configuration options, while if able to be populated, the latter fields MUST
// also be specified.
type Config struct {
// NetworkDir is the main network directory wherein the rpc
// server will find the macaroon named DefaultVirtualChannelsMacFilename.
NetworkDir string

// MacService is the main macaroon service that we'll use to handle
// authentication for the rpc server.
MacService *macaroons.Service

// Switch is used to register new virtual channels
Switch *htlcswitch.Switch
}
53 changes: 53 additions & 0 deletions lnrpc/virtualchannelsrpc/driver.go
@@ -0,0 +1,53 @@
package virtualchannelsrpc

import (
"fmt"

"github.com/lightningnetwork/lnd/lnrpc"
)

// createNewSubServer is a helper method that will create the new sub server
// given the main config dispatcher method. If we're unable to find the config
// that is meant for us in the config dispatcher, then we'll exit with an
// error.
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {

// We'll attempt to look up the config that we expect, according to our
// subServerName name. If we can't find this, then we'll exit with an
// error, as we're unable to properly initialize ourselves without this
// config.
subServerConf, ok := configRegistry.FetchConfig(subServerName)
if !ok {
return nil, nil, fmt.Errorf("unable to find config for "+
"subserver type %s", subServerName)
}

// Now that we've found an object mapping to our service name, we'll
// ensure that it's the type we need.
config, ok := subServerConf.(*Config)
if !ok {
return nil, nil, fmt.Errorf("wrong type of config for "+
"subserver %s, expected %T got %T", subServerName,
&Config{}, subServerConf)
}

return New(config)
}

func init() {
subServer := &lnrpc.SubServerDriver{
SubServerName: subServerName,
New: func(c lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer,
lnrpc.MacaroonPerms, error) {
return createNewSubServer(c)
},
}

// If the build tag is active, then we'll register ourselves as a
// sub-RPC server within the global lnrpc package namespace.
if err := lnrpc.RegisterSubServer(subServer); err != nil {
panic(fmt.Sprintf("failed to register sub server driver "+
"'%s': %v", subServerName, err))
}
}
45 changes: 45 additions & 0 deletions lnrpc/virtualchannelsrpc/log.go
@@ -0,0 +1,45 @@
package virtualchannelsrpc

import (
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
)

// log is a logger that is initialized with no output filters. This means the
// package will not perform any logging by default until the caller requests
// it.
var log btclog.Logger

// The default amount of logging is none.
func init() {
UseLogger(build.NewSubLogger("VCRPC", nil))
}

// DisableLog disables all library log output. Logging output is disabled by
// by default until UseLogger is called.
func DisableLog() {
UseLogger(btclog.Disabled)
}

// UseLogger uses a specified Logger to output package logging info. This
// should be used in preference to SetLogWriter if the caller is also using
// btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string

// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}

// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

0 comments on commit b3b2f7f

Please sign in to comment.