From f8c851769fb5910763543f0b01d4465b974a0bf9 Mon Sep 17 00:00:00 2001 From: BitfuryLightning Date: Fri, 15 Jul 2016 07:02:59 -0400 Subject: [PATCH] multi: initial integration of routing module This commit integrates BitFury's current routing functionality into lnd. The primary ochestration point for the routing sub-system in the routingMgr. The routingMgr manages all persistent and volatile state related to routing within the network. Newly opened channels, either when the initiator or responder are inserted into the routing table once the channel is fully open. Once new links are inserted the routingMgr can then perform path selection in order to locate an "optimal" path to a target destination. --- cmd/lncli/commands.go | 20 +++++++++ cmd/lncli/main.go | 1 + fundingmanager.go | 13 ++++++ lnrpc/rpc.proto | 8 ++++ lnwire/message.go | 20 +++++++++ lnwire/neighbor_ack_message.go | 39 ++++++++++++++++++ lnwire/neighbor_hello_message.go | 52 ++++++++++++++++++++++++ lnwire/neighbor_hello_message_test.go | 41 +++++++++++++++++++ lnwire/neighbor_rst_message.go | 39 ++++++++++++++++++ lnwire/neighbor_upd_message.go | 51 +++++++++++++++++++++++ lnwire/routing_messages.go | 28 +++++++++++++ lnwire/routing_table_request_message.go | 40 ++++++++++++++++++ lnwire/routing_table_transfer_message.go | 51 +++++++++++++++++++++++ peer.go | 10 +++++ rpcserver.go | 9 ++++ server.go | 46 ++++++++++++++++++++- 16 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 lnwire/neighbor_ack_message.go create mode 100644 lnwire/neighbor_hello_message.go create mode 100644 lnwire/neighbor_hello_message_test.go create mode 100644 lnwire/neighbor_rst_message.go create mode 100644 lnwire/neighbor_upd_message.go create mode 100644 lnwire/routing_messages.go create mode 100644 lnwire/routing_table_request_message.go create mode 100644 lnwire/routing_table_transfer_message.go diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index e8e944081..08a9f4137 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -503,3 +503,23 @@ func sendPaymentCommand(ctx *cli.Context) error { return nil } + +var ShowRoutingTableCommand = cli.Command{ + Name: "showroutingtable", + Description: "shows routing table for a node", + Action: showRoutingTable, +} + +func showRoutingTable(ctx *cli.Context) error { + ctxb := context.Background() + client := getClient(ctx) + + req := &lnrpc.ShowRoutingTableRequest{} + resp, err := client.ShowRoutingTable(ctxb, req) + if err != nil { + return err + } + + printRespJson(resp) + return nil +} \ No newline at end of file diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index 1ca07d592..c3391ab51 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -60,6 +60,7 @@ func main() { GetInfoCommand, PendingChannelsCommand, SendPaymentCommand, + ShowRoutingTableCommand, } if err := app.Run(os.Args); err != nil { diff --git a/fundingmanager.go b/fundingmanager.go index d904f4a18..2fe6a9a4a 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -10,6 +10,9 @@ import ( "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" + + "github.com/BitfuryLightning/tools/rt" + "github.com/BitfuryLightning/tools/rt/graph" ) const ( @@ -614,6 +617,16 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { fndgLog.Infof("FundingOpen: ChannelPoint(%v) with peerID(%v) is now open", resCtx.reservation.FundingOutpoint, fmsg.peer.id) + // ROUTING ADDED + capacity := float64(resCtx.reservation.OurContribution().FundingAmount + resCtx.reservation.TheirContribution().FundingAmount) + fmsg.peer.server.routingMgr.AddChannel( + graph.NewID(fmsg.peer.server.lightningID), + graph.NewID([32]byte(fmsg.peer.lightningID)), + graph.NewEdgeID(resCtx.reservation.FundingOutpoint().String()), + &rt.ChannelInfo{ + Cpt:capacity, + }, + ) fmsg.peer.newChannels <- openChan } diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index e4ae1fd91..a67198007 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -17,6 +17,7 @@ service Lightning { rpc PendingChannels(PendingChannelRequest) returns (PendingChannelResponse); rpc SendPayment(stream SendRequest) returns (stream SendResponse); + rpc ShowRoutingTable(ShowRoutingTableRequest) returns (ShowRoutingTableResponse); } message SendRequest { @@ -214,3 +215,10 @@ message WalletBalanceRequest { message WalletBalanceResponse { double balance = 1; } + +message ShowRoutingTableRequest { +} + +message ShowRoutingTableResponse { + string rt = 1; +} diff --git a/lnwire/message.go b/lnwire/message.go index 58538d689..4c291d7cc 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -44,6 +44,14 @@ const ( CmdCommitSignature = uint32(2000) CmdCommitRevocation = uint32(2010) + // Commands for routing + CmdNeighborHelloMessage = uint32(3000) + CmdNeighborUpdMessage = uint32(3010) + CmdNeighborAckMessage = uint32(3020) + CmdNeighborRstMessage = uint32(3030) + CmdRoutingTableRequestMessage = uint32(3040) + CmdRoutingTableTransferMessage = uint32(3050) + // Commands for reporting protocol errors. CmdErrorGeneric = uint32(4000) ) @@ -94,6 +102,18 @@ func makeEmptyMessage(command uint32) (Message, error) { msg = &CommitRevocation{} case CmdErrorGeneric: msg = &ErrorGeneric{} + case CmdNeighborHelloMessage: + msg = &NeighborHelloMessage{} + case CmdNeighborUpdMessage: + msg = &NeighborUpdMessage{} + case CmdNeighborAckMessage: + msg = &NeighborAckMessage{} + case CmdNeighborRstMessage: + msg = &NeighborRstMessage{} + case CmdRoutingTableRequestMessage: + msg = &RoutingTableRequestMessage{} + case CmdRoutingTableTransferMessage: + msg = &RoutingTableTransferMessage{} default: return nil, fmt.Errorf("unhandled command [%d]", command) } diff --git a/lnwire/neighbor_ack_message.go b/lnwire/neighbor_ack_message.go new file mode 100644 index 000000000..2a4e30b5c --- /dev/null +++ b/lnwire/neighbor_ack_message.go @@ -0,0 +1,39 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire +import ( + "fmt" + "io" +) + +type NeighborAckMessage struct { + RoutingMessageBase +} + +func (msg *NeighborAckMessage) String() string { + return fmt.Sprintf("NeighborAckMessage{%v %v}", msg.SenderID, msg.ReceiverID) +} + +func (msg *NeighborAckMessage) Command() uint32{ + return CmdNeighborAckMessage +} + +func (msg *NeighborAckMessage) Encode(w io.Writer, pver uint32) error{ + return nil +} + +func (msg *NeighborAckMessage) Decode(r io.Reader, pver uint32) error{ + return nil +} + +func (msg *NeighborAckMessage) MaxPayloadLength(uint32) uint32{ + return 0 +} + +func (msg *NeighborAckMessage) Validate() error{ + return nil +} + +var _ Message = (*NeighborAckMessage)(nil) \ No newline at end of file diff --git a/lnwire/neighbor_hello_message.go b/lnwire/neighbor_hello_message.go new file mode 100644 index 000000000..dbe16f2a2 --- /dev/null +++ b/lnwire/neighbor_hello_message.go @@ -0,0 +1,52 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire + +import ( + "io" + "github.com/BitfuryLightning/tools/rt" + "encoding/gob" + "fmt" +) + +type NeighborHelloMessage struct{ + RoutingMessageBase + RT *rt.RoutingTable +} + +func (msg *NeighborHelloMessage) Decode(r io.Reader, pver uint32) error{ + decoder := gob.NewDecoder(r) + rt1 := rt.NewRoutingTable() + err := decoder.Decode(rt1.G) + msg.RT = rt1 + return err +} + +func (msg *NeighborHelloMessage) Encode(w io.Writer, pver uint32) error{ + encoder := gob.NewEncoder(w) + err := encoder.Encode(msg.RT.G) + return err +} + +func (msg *NeighborHelloMessage) Command() uint32{ + return CmdNeighborHelloMessage +} + +func (msg *NeighborHelloMessage) MaxPayloadLength(uint32) uint32{ + // TODO: Insert some estimations + return 1000000 +} + +func (msg *NeighborHelloMessage) Validate() error{ + // TODO: Add validation + return nil +} + +func (msg *NeighborHelloMessage) String() string{ + return fmt.Sprintf("NeighborHelloMessage{%v %v %v}", msg.SenderID, msg.ReceiverID, msg.RT) +} + + +var _ Message = (*NeighborHelloMessage)(nil) diff --git a/lnwire/neighbor_hello_message_test.go b/lnwire/neighbor_hello_message_test.go new file mode 100644 index 000000000..30964cd49 --- /dev/null +++ b/lnwire/neighbor_hello_message_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire + +import ( + "testing" + "bytes" + "github.com/BitfuryLightning/tools/rt" + "github.com/BitfuryLightning/tools/rt/graph" +) + +func TestNeighborHelloMessageEncodeDecode(t *testing.T){ + Id1 := graph.NewID(1) + Id2 := graph.NewID(2) + rt1 := rt.NewRoutingTable() + rt1.AddChannel(Id1, Id2, graph.NewEdgeID("1"), &rt.ChannelInfo{1, 1}) + b := new(bytes.Buffer) + msg1 := NeighborHelloMessage{RT:rt1} + err := msg1.Encode(b, 0) + if err != nil{ + t.Fatalf("Can't encode message ", err) + } + msg2 := new(NeighborHelloMessage) + err = msg2.Decode(b, 0) + if err != nil{ + t.Fatalf("Can't decode message ", err) + } + if msg2.RT == nil{ + t.Fatal("After decoding RT should not be nil") + } + if !msg2.RT.HasChannel(Id1, Id2, nil){ + t.Errorf("msg2.RT.HasChannel(Id1, Id2) = false, want true") + } + if !msg2.RT.HasChannel(Id2, Id1, nil){ + t.Errorf("msg2.RT.HasChannel(Id2, Id1) = false, want true") + } +} + + diff --git a/lnwire/neighbor_rst_message.go b/lnwire/neighbor_rst_message.go new file mode 100644 index 000000000..5f3f4f40d --- /dev/null +++ b/lnwire/neighbor_rst_message.go @@ -0,0 +1,39 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire +import ( + "fmt" + "io" +) + +type NeighborRstMessage struct { + RoutingMessageBase +} + +func (msg *NeighborRstMessage) String() string { + return fmt.Sprintf("NeighborRstMessage{%v %v}", msg.SenderID, msg.ReceiverID) +} + +func (msg *NeighborRstMessage) Command() uint32{ + return CmdNeighborRstMessage +} + +func (msg *NeighborRstMessage) Encode(w io.Writer, pver uint32) error{ + return nil +} + +func (msg *NeighborRstMessage) Decode(r io.Reader, pver uint32) error{ + return nil +} + +func (msg *NeighborRstMessage) MaxPayloadLength(uint32) uint32{ + return 0 +} + +func (msg *NeighborRstMessage) Validate() error{ + return nil +} + +var _ Message = (*NeighborRstMessage)(nil) \ No newline at end of file diff --git a/lnwire/neighbor_upd_message.go b/lnwire/neighbor_upd_message.go new file mode 100644 index 000000000..fb1cd1228 --- /dev/null +++ b/lnwire/neighbor_upd_message.go @@ -0,0 +1,51 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire + +import ( + "fmt" + "github.com/BitfuryLightning/tools/rt" + "encoding/gob" + "io" +) + +type NeighborUpdMessage struct { + RoutingMessageBase + DiffBuff *rt.DifferenceBuffer +} + +func (msg *NeighborUpdMessage) Decode(r io.Reader, pver uint32) error{ + decoder := gob.NewDecoder(r) + diffBuff := new(rt.DifferenceBuffer) + err := decoder.Decode(diffBuff) + msg.DiffBuff = diffBuff + return err +} + +func (msg *NeighborUpdMessage) Encode(w io.Writer, pver uint32) error{ + encoder := gob.NewEncoder(w) + err := encoder.Encode(msg.DiffBuff) + return err +} + +func (msg *NeighborUpdMessage) Command() uint32{ + return CmdNeighborUpdMessage +} + +func (msg *NeighborUpdMessage) MaxPayloadLength(uint32) uint32{ + // TODO: Insert some estimations + return 1000000 +} + +func (msg *NeighborUpdMessage) Validate() error{ + // TODO: Add validation + return nil +} + +func (msg *NeighborUpdMessage) String() string { + return fmt.Sprintf("NeighborUpdMessage{%v %v %v}", msg.SenderID, msg.ReceiverID, *msg.DiffBuff) +} + +var _ Message = (*NeighborUpdMessage)(nil) \ No newline at end of file diff --git a/lnwire/routing_messages.go b/lnwire/routing_messages.go new file mode 100644 index 000000000..6e09eddf6 --- /dev/null +++ b/lnwire/routing_messages.go @@ -0,0 +1,28 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire + +import ( + "github.com/BitfuryLightning/tools/rt/graph" +) + +type RoutingMessageBase struct { + SenderID graph.ID + ReceiverID graph.ID +} + +func (msg RoutingMessageBase) GetReceiverID() graph.ID{ + return msg.ReceiverID +} + +func (msg RoutingMessageBase) GetSenderID() graph.ID{ + return msg.SenderID +} + +// Interface for all routing messages. All messages have sender and receiver +type RoutingMessage interface { + GetSenderID() graph.ID + GetReceiverID() graph.ID +} diff --git a/lnwire/routing_table_request_message.go b/lnwire/routing_table_request_message.go new file mode 100644 index 000000000..9656ea350 --- /dev/null +++ b/lnwire/routing_table_request_message.go @@ -0,0 +1,40 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire +import ( + "fmt" + "io" +) + +type RoutingTableRequestMessage struct { + RoutingMessageBase +} + +func (msg *RoutingTableRequestMessage) String() string { + return fmt.Sprintf("RoutingTableRequestMessage{%v %v}", msg.SenderID, msg.ReceiverID) +} + + +func (msg *RoutingTableRequestMessage) Command() uint32{ + return CmdRoutingTableRequestMessage +} + +func (msg *RoutingTableRequestMessage) Encode(w io.Writer, pver uint32) error{ + return nil +} + +func (msg *RoutingTableRequestMessage) Decode(r io.Reader, pver uint32) error{ + return nil +} + +func (msg *RoutingTableRequestMessage) MaxPayloadLength(uint32) uint32{ + return 0 +} + +func (msg *RoutingTableRequestMessage) Validate() error{ + return nil +} + +var _ Message = (*RoutingTableRequestMessage)(nil) \ No newline at end of file diff --git a/lnwire/routing_table_transfer_message.go b/lnwire/routing_table_transfer_message.go new file mode 100644 index 000000000..529cf0106 --- /dev/null +++ b/lnwire/routing_table_transfer_message.go @@ -0,0 +1,51 @@ +// Copyright (c) 2016 Bitfury Group Limited +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php + +package lnwire + +import ( + "io" + "github.com/BitfuryLightning/tools/rt" + "encoding/gob" + "fmt" +) + +type RoutingTableTransferMessage struct { + RoutingMessageBase + RT *rt.RoutingTable +} + +func (msg *RoutingTableTransferMessage) String() string { + return fmt.Sprintf("RoutingTableTransferMessage{%v %v %v}", msg.SenderID, msg.ReceiverID, msg.RT) +} + +func (msg *RoutingTableTransferMessage) Decode(r io.Reader, pver uint32) error{ + decoder := gob.NewDecoder(r) + rt1 := rt.NewRoutingTable() + err := decoder.Decode(rt1.G) + msg.RT = rt1 + return err +} + +func (msg *RoutingTableTransferMessage) Encode(w io.Writer, pver uint32) error{ + encoder := gob.NewEncoder(w) + err := encoder.Encode(msg.RT.G) + return err +} + +func (msg *RoutingTableTransferMessage) Command() uint32{ + return CmdRoutingTableTransferMessage +} + +func (msg *RoutingTableTransferMessage) MaxPayloadLength(uint32) uint32{ + // TODO: Insert some estimations + return 1000000 +} + +func (msg *RoutingTableTransferMessage) Validate() error{ + // TODO: Add validation + return nil +} + +var _ Message = (*RoutingTableTransferMessage)(nil) diff --git a/peer.go b/peer.go index c86d1e312..80d641332 100644 --- a/peer.go +++ b/peer.go @@ -17,6 +17,7 @@ import ( "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" + ) var ( @@ -377,6 +378,15 @@ out: case *lnwire.CommitSignature: isChanUpate = true targetChan = msg.ChannelPoint + // ROUTING ADDED + case *lnwire.NeighborAckMessage, + *lnwire.NeighborHelloMessage, + *lnwire.NeighborRstMessage, + *lnwire.NeighborUpdMessage, + *lnwire.RoutingTableRequestMessage, + *lnwire.RoutingTableTransferMessage: + p.server.routingMgr.ChIn <- msg + // TODO(mkl): determine sender and receiver of message } if isChanUpate { diff --git a/rpcserver.go b/rpcserver.go index 4b5d63e55..b4d98b033 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -482,3 +482,12 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) return nil } + +func (r *rpcServer) ShowRoutingTable(ctx context.Context, + in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) { + rpcsLog.Debugf("[ShowRoutingTable]") + rtCopy := r.server.routingMgr.GetRTCopy() + return &lnrpc.ShowRoutingTableResponse{ + Rt: rtCopy.String(), + }, nil +} diff --git a/server.go b/server.go index 1b39e17dc..a049c4a7f 100644 --- a/server.go +++ b/server.go @@ -11,10 +11,14 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lndc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" + "github.com/BitfuryLightning/tools/routing" + "github.com/BitfuryLightning/tools/rt" + "github.com/BitfuryLightning/tools/rt/graph" "github.com/roasbeef/btcwallet/waddrmgr" ) @@ -48,6 +52,9 @@ type server struct { htlcSwitch *htlcSwitch invoices *invoiceRegistry + // ROUTING ADDED + routingMgr *routing.RoutingManager + newPeers chan *peer donePeers chan *peer queries chan interface{} @@ -94,6 +101,10 @@ func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet, // TODO(roasbeef): remove s.invoices.addInvoice(1000*1e8, *debugPre) + // Create a new routing manager with ourself as the sole node within + // the graph. + s.routingMgr = routing.NewRoutingManager(graph.NewID(s.lightningID), nil) + s.rpcServer = newRpcServer(s) return s, nil @@ -115,6 +126,7 @@ func (s *server) Start() { s.fundingMgr.Start() s.htlcSwitch.Start() + s.routingMgr.Start() s.wg.Add(1) go s.queryHandler() @@ -141,6 +153,9 @@ func (s *server) Stop() error { s.lnwallet.Shutdown() s.fundingMgr.Stop() + // ROUTING ADDED + s.routingMgr.Stop() + // Signal all the lingering goroutines to quit. close(s.quit) s.wg.Wait() @@ -255,6 +270,24 @@ out: case *openChanReq: s.handleOpenChanReq(msg) } + case msg := <-s.routingMgr.ChOut: + msg1 := msg.(lnwire.RoutingMessage) + receiverID := msg1.GetReceiverID().ToByte32() + var targetPeer *peer + for _, peer := range s.peers { // TODO: threadsafe api + // We found the the target + if peer.lightningID == receiverID { + targetPeer = peer + break + } + } + if targetPeer != nil { + fndgLog.Info("Peer found. Sending message") + done := make(chan struct{}, 1) + targetPeer.queueMsg(msg.(lnwire.Message), done) + } else { + srvrLog.Errorf("Can't find peer to send message %v", receiverID) + } case <-s.quit: break out } @@ -372,7 +405,18 @@ func (s *server) handleOpenChanReq(req *openChanReq) { go func() { // TODO(roasbeef): server semaphore to restrict num goroutines fundingID, err := s.fundingMgr.initFundingWorkflow(targetPeer, req) - + if err == nil { + // ROUTING ADDED + capacity := float64(req.localFundingAmt + req.remoteFundingAmt) + s.routingMgr.AddChannel( + graph.NewID(s.lightningID), + graph.NewID([32]byte(targetPeer.lightningID)), + graph.NewEdgeID(fundingID.String()), + &rt.ChannelInfo{ + Cpt: capacity, + }, + ) + } req.resp <- &openChanResp{fundingID} req.err <- err }()