Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[wip] multi: add draft impl for virtual links
- Loading branch information
Showing
9 changed files
with
1,520 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.