Skip to content

Commit

Permalink
Merge c5414ec into 5ef077e
Browse files Browse the repository at this point in the history
  • Loading branch information
afederigo committed Aug 16, 2017
2 parents 5ef077e + c5414ec commit dcc31c9
Show file tree
Hide file tree
Showing 9 changed files with 2,045 additions and 778 deletions.
113 changes: 112 additions & 1 deletion cmd/lncli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func printJSON(resp interface{}) {
out.WriteTo(os.Stdout)
}

// printRespJSON converts an rpc response structure to json format.
// Then json string is converted to binary format to write this
// converted data to Stdout to make available usage this data in Stdin
// of the second possible function writen in pipe-style.
func printRespJSON(resp proto.Message) {
jsonMarshaler := &jsonpb.Marshaler{
EmitDefaults: true,
Expand All @@ -50,7 +54,14 @@ func printRespJSON(resp proto.Message) {
return
}

fmt.Println(jsonStr)
// Write jsonStr to Stdout to make available usage this data in Stdin
// of the second possible function writen in pipe-style.
b := []byte(jsonStr)

var out bytes.Buffer
json.Indent(&out, b, "", "\t")
out.WriteString("\n")
out.WriteTo(os.Stdout)
}

var newAddressCommand = cli.Command{
Expand Down Expand Up @@ -881,6 +892,106 @@ func sendPayment(ctx *cli.Context) error {
return nil
}

var sendToRouteCommand = cli.Command{
Name: "sendtoroute",
Usage: "send a payment over predefined routes",
ArgsUsage: "(lncli queryroutes --dest=<dest> --amt=<amt> | lncli sendtoroute --payment_hash=<payment_hash>)",
Flags: []cli.Flag{
cli.StringFlag{
Name: "payment_hash, ph",
Usage: "the hash to use within the payment's HTLC",
},
cli.BoolFlag{
Name: "debug_send",
Usage: "use the debug rHash when sending the HTLC",
},
},
Action: sendToRoute,
}

func sendToRoute(ctx *cli.Context) error {
client, cleanUp := getClient(ctx)
defer cleanUp()

// Show command help if no arguments provieded
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
cli.ShowCommandHelp(ctx, "sendtoroute")
return nil
}

args := ctx.Args()

var (
err error
rHash []byte
)

req := &lnrpc.SendToRouteRequest{}
routes := &lnrpc.QueryRoutesResponse{}

b, err := ioutil.ReadAll(os.Stdin)
if len(b) == 0 {
return fmt.Errorf("queryroutes output is empty")
}

err = jsonpb.UnmarshalString(string(b), routes)
if err != nil {
return fmt.Errorf("unable to unmarshal json string of incomming array of routes: err(%v)", err)
}

req.Routes = routes.Routes

if ctx.Bool("debug_send") && (ctx.IsSet("payment_hash") || args.Present()) {
return fmt.Errorf("do not provide a payment hash with debug send")
} else if !ctx.Bool("debug_send") {
switch {
case ctx.IsSet("payment_hash"):
rHash, err = hex.DecodeString(ctx.String("payment_hash"))
case args.Present():
rHash, err = hex.DecodeString(args.First())
default:
return fmt.Errorf("payment hash argument missing")
}

if err != nil {
return err
}
if len(rHash) != 32 {
return fmt.Errorf("payment hash must be exactly 32 "+
"bytes, is instead %v", len(rHash))
}
req.PaymentHash = rHash
}

paymentStream, err := client.SendToRoute(context.Background())
if err != nil {
return err
}

if err := paymentStream.Send(req); err != nil {
return err
}

resp, err := paymentStream.Recv()
if err != nil {
return err
}

paymentStream.CloseSend()

printJSON(struct {
E string `json:"payment_error"`
P string `json:"payment_preimage"`
R *lnrpc.Route `json:"payment_route"`
}{
E: resp.PaymentError,
P: hex.EncodeToString(resp.PaymentPreimage),
R: resp.PaymentRoute,
})

return nil
}

var addInvoiceCommand = cli.Command{
Name: "addinvoice",
Usage: "add a new invoice.",
Expand Down
1 change: 1 addition & 0 deletions cmd/lncli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func main() {
getInfoCommand,
pendingChannelsCommand,
sendPaymentCommand,
sendToRouteCommand,
addInvoiceCommand,
lookupInvoiceCommand,
listInvoicesCommand,
Expand Down
126 changes: 126 additions & 0 deletions lnd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,128 @@ func testMultiHopPayments(net *networkHarness, t *harnessTest) {
}
}

//
func testSendToRouteErrorPropagation(net *networkHarness, t *harnessTest) {
const chanAmt = btcutil.Amount(100000)
ctxb := context.Background()
timeout := time.Duration(time.Second * 5)

// Open a channel with 100k satoshis between Alice and Bob with Alice
// being the sole funder of the channel.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAlice := openChannelAndAssert(ctxt, t, net, net.Alice,
net.Bob, chanAmt, 0)
ctxt, _ = context.WithTimeout(ctxb, timeout)
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPointAlice)
if err != nil {
t.Fatalf("alice didn't advertise her channel: %v", err)
}

// Create a new nodes (Carol and Charlie), load her with some funds, then establish
// a connection between Carol and Charlie with a channel that has
// identical capacity to the one created above.Then we will get route via queryroutes call
// which will be fake route for Alice -> Bob graph.
//
// The network topology should now look like: Alice -> Bob; Carol -> Charlie.
carol, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create new nodes: %v", err)
}
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, carol)
if err != nil {
t.Fatalf("unable to send coins to carol: %v", err)
}

charlie, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create new nodes: %v", err)
}
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, charlie)
if err != nil {
t.Fatalf("unable to send coins to charlie: %v", err)
}

if err := net.ConnectNodes(ctxb, carol, charlie); err != nil {
t.Fatalf("unable to connect carol to alice: %v", err)
}

ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
charlie, chanAmt, 0)
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = carol.WaitForNetworkChannelOpen(ctxt, chanPointCarol)
if err != nil {
t.Fatalf("carol didn't advertise her channel: %v", err)
}

// Create route via queryroutes call which will be fake route
// for Alice -> Bob graph.
fakeReq := &lnrpc.QueryRoutesRequest{
PubKey: charlie.PubKeyStr,
Amt: int64(1),
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
fakeRoute, err := carol.QueryRoutes(ctxt, fakeReq)
if err != nil {
t.Fatalf("unable get fake route: %v", err)
}

// Create 1 invoices for Bob, which expect a payment from Alice for 1k
// satoshis
const paymentAmt = 1000

invoice := &lnrpc.Invoice{
Memo: "testing",
Value: paymentAmt,
}
resp, err := net.Bob.AddInvoice(ctxb, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}

rHash := resp.RHash

// Using Alice as the source, pay to the 5 invoices from Bob created above.
alicePayStream, err := net.Alice.SendToRoute(ctxb)
if err != nil {
t.Fatalf("unable to create payment stream for alice: %v", err)
}

sendReq := &lnrpc.SendToRouteRequest{
PaymentHash: rHash,
Routes: fakeRoute.Routes,
}

if err := alicePayStream.Send(sendReq); err != nil {
t.Fatalf("unable to send payment: %v", err)
}

// At this place we should get an rpc error with notification that edge not found
// on the hop(0)
if _, err := alicePayStream.Recv(); err != nil && strings.Contains(err.Error(),
"edge not found") {

} else if err != nil {
t.Fatalf("payment stream has been closed but fake route has consumed: %v", err)
}

ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)

// Finally, shutdown the nodes we created to make fake route,
// only leaving the two seed nodes (Alice and Bob) within our test
// network.
if err := carol.Shutdown(); err != nil {
t.Fatalf("unable to shutdown carol: %v", err)
}

if err := charlie.Shutdown(); err != nil {
t.Fatalf("unable to shutdown charlie: %v", err)
}
}

func testInvoiceSubscriptions(net *networkHarness, t *harnessTest) {
const chanAmt = btcutil.Amount(500000)
ctxb := context.Background()
Expand Down Expand Up @@ -3080,6 +3202,10 @@ var testsCases = []*testCase{
name: "multi-hop payments",
test: testMultiHopPayments,
},
{
name: "send to route error propagation",
test: testSendToRouteErrorPropagation,
},
{
name: "multiple channel creation",
test: testBasicChannelCreation,
Expand Down

0 comments on commit dcc31c9

Please sign in to comment.