Merge pull request #846 from wpaulino/private-channel-routing

multi: support multi-hop payments over private channels
This commit is contained in:
Olaoluwa Osuntokun 2018-04-20 17:27:12 -07:00 committed by GitHub
commit 435e68a438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1595 additions and 781 deletions

@ -1713,6 +1713,12 @@ var addInvoiceCommand = cli.Command{
"specified an expiry of 3600 seconds (1 hour) " +
"is implied.",
},
cli.BoolTFlag{
Name: "private",
Usage: "encode routing hints in the invoice with " +
"private channels in order to assist the " +
"payer in reaching you",
},
},
Action: actionDecorator(addInvoice),
}
@ -1771,6 +1777,7 @@ func addInvoice(ctx *cli.Context) error {
DescriptionHash: descHash,
FallbackAddr: ctx.String("fallback_addr"),
Expiry: ctx.Int64("expiry"),
Private: ctx.Bool("private"),
}
resp, err := client.AddInvoice(context.Background(), invoice)

@ -144,19 +144,20 @@ func mineBlocks(t *harnessTest, net *lntest.NetworkHarness, num uint32) []*wire.
// channel.
func openChannelAndAssert(ctx context.Context, t *harnessTest,
net *lntest.NetworkHarness, alice, bob *lntest.HarnessNode,
fundingAmt btcutil.Amount, pushAmt btcutil.Amount) *lnrpc.ChannelPoint {
fundingAmt btcutil.Amount, pushAmt btcutil.Amount,
private bool) *lnrpc.ChannelPoint {
chanOpenUpdate, err := net.OpenChannel(ctx, alice, bob, fundingAmt,
pushAmt, false)
chanOpenUpdate, err := net.OpenChannel(
ctx, alice, bob, fundingAmt, pushAmt, private,
)
if err != nil {
t.Fatalf("unable to open channel: %v", err)
}
// Mine 6 blocks, then wait for Alice's node to notify us that the
// channel has been opened. The funding transaction should be found
// within the first newly mined block. We mine 6 blocks to make sure
// the channel is public, as it will not be announced to the network
// before the funding transaction is 6 blocks deep.
// within the first newly mined block. We mine 6 blocks so that in the
// case that the channel is public, it is announced to the network.
block := mineBlocks(t, net, 6)[0]
fundingChanPoint, err := net.WaitForChannelOpen(ctx, chanOpenUpdate)
@ -458,8 +459,9 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
// assertions will be executed to ensure the funding process completed
// successfully.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, pushAmt)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
@ -517,8 +519,9 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
// Create a channel Alice->Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, pushAmt)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
@ -543,8 +546,9 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint2 := openChannelAndAssert(ctxt, t, net, net.Bob, carol,
chanAmt, pushAmt)
chanPoint2 := openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, pushAmt, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint2)
@ -756,7 +760,7 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint3 := openChannelAndAssert(
ctxt, t, net, net.Alice, carol, chanAmt, pushAmt,
ctxt, t, net, net.Alice, carol, chanAmt, pushAmt, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
@ -1297,8 +1301,9 @@ func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to connect alice and bob: %v", err)
}
chanPoint := openChannelAndAssert(ctx, t, net, net.Alice, net.Bob,
amount, 0)
chanPoint := openChannelAndAssert(
ctx, t, net, net.Alice, net.Bob, amount, 0, false,
)
// Wait for both Alice and Bob to recognize this new channel.
ctxt, _ := context.WithTimeout(context.Background(), timeout)
@ -1467,8 +1472,9 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
carolStartingBalance := carolBalResp.ConfirmedBalance
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, carol,
chanAmt, pushAmt)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, carol, chanAmt, pushAmt, false,
)
// Wait for Alice and Carol to receive the channel edge from the
// funding manager.
@ -2054,8 +2060,9 @@ func testSphinxReplayPersistence(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, false,
)
assertAmountSent := func(amt btcutil.Amount) {
// Both channels should also have properly accounted from the
@ -2201,8 +2208,9 @@ func testSingleHopInvoice(net *lntest.NetworkHarness, t *harnessTest) {
// the sole funder of the channel.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanAmt := btcutil.Amount(100000)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
assertAmountSent := func(amt btcutil.Amount) {
// Both channels should also have properly accounted from the
@ -2356,8 +2364,9 @@ func testListPayments(net *lntest.NetworkHarness, t *harnessTest) {
// being the sole funder of the channel.
chanAmt := btcutil.Amount(100000)
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
// Now that the channel is open, create an invoice for Bob which
// expects a payment of 1000 satoshis from Alice paid via a particular
@ -2537,8 +2546,9 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
// 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)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -2573,8 +2583,9 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, 0)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -2603,8 +2614,9 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, 0)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -2800,8 +2812,9 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
// Open a channel with 200k satoshis between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAlice := openChannelAndAssert(ctxt, t, net, net.Alice,
net.Bob, chanAmt*2, 0)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt*2, 0, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -2830,8 +2843,9 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, 0)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -2860,8 +2874,9 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, 0)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -3116,6 +3131,223 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
}
}
// testMultiHopOverPrivateChannels tests that private channels can be used as
// intermediate hops in a route for payments.
func testMultiHopOverPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
// We'll test that multi-hop payments over private channels work as
// intended. To do so, we'll create the following topology:
// private public private
// Alice <--100k--> Bob <--100k--> Carol <--100k--> Dave
ctxb := context.Background()
timeout := time.Duration(15 * time.Second)
const chanAmt = btcutil.Amount(100000)
// First, we'll open a private channel between Alice and Bob with Alice
// being the funder.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, true,
)
ctxt, _ = context.WithTimeout(ctxb, timeout)
err := net.Alice.WaitForNetworkChannelOpen(ctxb, chanPointAlice)
if err != nil {
t.Fatalf("alice didn't see the channel alice <-> bob before "+
"timeout: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = net.Bob.WaitForNetworkChannelOpen(ctxb, chanPointAlice)
if err != nil {
t.Fatalf("bob didn't see the channel alice <-> bob before "+
"timeout: %v", err)
}
// Retrieve Alice's funding outpoint.
txidHash, err := getChanPointFundingTxid(chanPointAlice)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
aliceChanTXID, err := chainhash.NewHash(txidHash)
if err != nil {
t.Fatalf("unable to create sha hash: %v", err)
}
aliceFundPoint := wire.OutPoint{
Hash: *aliceChanTXID,
Index: chanPointAlice.OutputIndex,
}
// Next, we'll create Carol's node and open a public channel between
// her and Bob with Bob being the funder.
carol, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create carol's node: %v", err)
}
if err := net.ConnectNodes(ctxb, net.Bob, carol); err != nil {
t.Fatalf("unable to connect bob to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointBob := openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, 0, false,
)
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = net.Bob.WaitForNetworkChannelOpen(ctxb, chanPointBob)
if err != nil {
t.Fatalf("bob didn't see the channel bob <-> carol before "+
"timeout: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = carol.WaitForNetworkChannelOpen(ctxb, chanPointBob)
if err != nil {
t.Fatalf("carol didn't see the channel bob <-> carol before "+
"timeout: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = net.Alice.WaitForNetworkChannelOpen(ctxb, chanPointBob)
if err != nil {
t.Fatalf("alice didn't see the channel bob <-> carol before "+
"timeout: %v", err)
}
// Retrieve Bob's funding outpoint.
txidHash, err = getChanPointFundingTxid(chanPointBob)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
bobChanTXID, err := chainhash.NewHash(txidHash)
if err != nil {
t.Fatalf("unable to create sha hash: %v", err)
}
bobFundPoint := wire.OutPoint{
Hash: *bobChanTXID,
Index: chanPointBob.OutputIndex,
}
// Next, we'll create Dave's node and open a private channel between him
// and Carol with Carol being the funder.
dave, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create dave's node: %v", err)
}
if err := net.ConnectNodes(ctxb, carol, dave); err != nil {
t.Fatalf("unable to connect carol to dave: %v", err)
}
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, carol)
if err != nil {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, true,
)
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = carol.WaitForNetworkChannelOpen(ctxb, chanPointCarol)
if err != nil {
t.Fatalf("carol didn't see the channel carol <-> dave before "+
"timeout: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = dave.WaitForNetworkChannelOpen(ctxb, chanPointCarol)
if err != nil {
t.Fatalf("dave didn't see the channel carol <-> dave before "+
"timeout: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = dave.WaitForNetworkChannelOpen(ctxb, chanPointBob)
if err != nil {
t.Fatalf("dave didn't see the channel bob <-> carol before "+
"timeout: %v", err)
}
// Retrieve Carol's funding point.
txidHash, err = getChanPointFundingTxid(chanPointCarol)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
carolChanTXID, err := chainhash.NewHash(txidHash)
if err != nil {
t.Fatalf("unable to create sha hash: %v", err)
}
carolFundPoint := wire.OutPoint{
Hash: *carolChanTXID,
Index: chanPointCarol.OutputIndex,
}
// Now that all the channels are set up according to the topology from
// above, we can proceed to test payments. We'll create an invoice for
// Dave of 20k satoshis and pay it with Alice. Since there is no public
// route from Alice to Dave, we'll need to use the private channel
// between Carol and Dave as a routing hint encoded in the invoice.
const paymentAmt = 20000
// Create the invoice for Dave.
invoice := &lnrpc.Invoice{
Memo: "two hopz!",
Value: paymentAmt,
Private: true,
}
resp, err := dave.AddInvoice(ctxb, invoice)
if err != nil {
t.Fatalf("unable to add invoice for dave: %v", err)
}
// Let Alice pay the invoice.
payReqs := []string{resp.PaymentRequest}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = completePaymentRequests(ctxt, net.Alice, payReqs, true)
if err != nil {
t.Fatalf("unable to send payments from alice to dave: %v", err)
}
// When asserting the amount of satoshis moved, we'll factor in the
// default base fee, as we didn't modify the fee structure when opening
// the channels.
const baseFee = 1
// Dave should have received 20k satoshis from Carol.
assertAmountPaid(t, ctxb, "Carol(local) [private=>] Dave(remote)",
dave, carolFundPoint, 0, paymentAmt)
// Carol should have sent 20k satoshis to Dave.
assertAmountPaid(t, ctxb, "Carol(local) [private=>] Dave(remote)",
carol, carolFundPoint, paymentAmt, 0)
// Carol should have received 20k satoshis + fee for one hop from Bob.
assertAmountPaid(t, ctxb, "Bob(local) => Carol(remote)",
carol, bobFundPoint, 0, paymentAmt+baseFee)
// Bob should have sent 20k satoshis + fee for one hop to Carol.
assertAmountPaid(t, ctxb, "Bob(local) => Carol(remote)",
net.Bob, bobFundPoint, paymentAmt+baseFee, 0)
// Bob should have received 20k satoshis + fee for two hops from Alice.
assertAmountPaid(t, ctxb, "Alice(local) [private=>] Bob(remote)", net.Bob,
aliceFundPoint, 0, paymentAmt+baseFee*2)
// Alice should have sent 20k satoshis + fee for two hops to Bob.
assertAmountPaid(t, ctxb, "Alice(local) [private=>] Bob(remote)", net.Alice,
aliceFundPoint, paymentAmt+baseFee*2, 0)
// At this point, the payment was successful. We can now close all the
// channels and shutdown the nodes created throughout this test.
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Bob, chanPointBob, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
if err := net.ShutdownNode(carol); err != nil {
t.Fatalf("unable to shutdown carol's node: %v", err)
}
if err := net.ShutdownNode(dave); err != nil {
t.Fatalf("unable to shutdown dave's node: %v", err)
}
}
func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
const chanAmt = btcutil.Amount(500000)
ctxb := context.Background()
@ -3124,8 +3356,9 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
// Open a channel with 500k satoshis between Alice and Bob with Alice
// being the sole funder of the channel.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
// Next create a new invoice for Bob requesting 1k satoshis.
// TODO(roasbeef): make global list of invoices for each node to re-use
@ -3233,8 +3466,9 @@ func testBasicChannelCreation(net *lntest.NetworkHarness, t *harnessTest) {
chanPoints := make([]*lnrpc.ChannelPoint, numChannels)
for i := 0; i < numChannels; i++ {
ctx, _ := context.WithTimeout(context.Background(), timeout)
chanPoints[i] = openChannelAndAssert(ctx, t, net, net.Alice,
net.Bob, amount, 0)
chanPoints[i] = openChannelAndAssert(
ctx, t, net, net.Alice, net.Bob, amount, 0, false,
)
}
// Close the channel between Alice and Bob, asserting that the
@ -3460,8 +3694,9 @@ func testRevokedCloseRetribution(net *lntest.NetworkHarness, t *harnessTest) {
// closure by Bob, we'll first open up a channel between them with a
// 0.5 BTC value.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
// With the channel open, we'll create a few invoices for Bob that
// Alice will pay to in order to advance the state of the channel.
@ -3709,8 +3944,9 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(net *lntest.NetworkHarness
// closure by Carol, we'll first open up a channel between them with a
// 0.5 BTC value.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, carol,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, carol, chanAmt, 0, false,
)
// With the channel open, we'll create a few invoices for Carol that
// Alice will pay to in order to advance the state of the channel.
@ -3945,7 +4181,7 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
// maxFundingAmount (2^24) satoshis value.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(
ctxt, t, net, dave, carol, chanAmt, pushAmt,
ctxt, t, net, dave, carol, chanAmt, pushAmt, false,
)
// With the channel open, we'll create a few invoices for Carol that
@ -4268,8 +4504,9 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) {
// First establish a channel with a capacity of 0.5 BTC between Alice
// and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAlice := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
ctxt, _ = context.WithTimeout(ctxb, timeout)
if err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPointAlice); err != nil {
t.Fatalf("channel not seen by alice before timeout: %v", err)
@ -4312,8 +4549,9 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) {
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
const bobChanAmt = maxFundingAmount
chanPointBob := openChannelAndAssert(ctxt, t, net, net.Bob, carol,
chanAmt, 0)
chanPointBob := openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, 0, false,
)
// Ensure that Alice has Carol in her routing table before proceeding.
nodeInfoReq := &lnrpc.NodeInfoRequest{
@ -4600,8 +4838,9 @@ func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest)
// Open a new channel between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
// The channel opening above should have triggered a few notifications
// sent to the notification client. We'll expect two channel updates,
@ -4714,8 +4953,9 @@ func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("unable to connect bob to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint = openChannelAndAssert(ctxt, t, net, net.Bob, carol,
chanAmt, 0)
chanPoint = openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, 0, false,
)
// Reconnect Alice and Bob. This should result in the nodes syncing up
// their respective graph state, with the new addition being the
@ -4811,8 +5051,9 @@ func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
timeout := time.Duration(time.Second * 5)
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Bob, dave,
1000000, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Bob, dave, 1000000, 0, false,
)
// When Alice now connects with Dave, Alice will get his node announcement.
if err := net.ConnectNodes(ctxb, net.Alice, dave); err != nil {
@ -4869,8 +5110,9 @@ func testNodeSignVerify(net *lntest.NetworkHarness, t *harnessTest) {
// Create a channel between alice and bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
aliceBobCh := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, pushAmt)
aliceBobCh := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
aliceMsg := []byte("alice msg")
@ -4968,8 +5210,9 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
// Alice should send all money from her side to Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
channelCapacity := btcutil.Amount(paymentAmt * 2000)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
channelCapacity, 0)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, channelCapacity, 0, false,
)
info, err := getChanInfo(net.Alice)
if err != nil {
@ -5150,8 +5393,10 @@ func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest)
// amount of payments, between Alice and Bob, at the end of the test
// Alice should send all money from her side to Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
paymentAmt*2000, paymentAmt*1000)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, paymentAmt*2000,
paymentAmt*1000, false,
)
info, err := getChanInfo(net.Alice)
if err != nil {
@ -5466,7 +5711,7 @@ func createThreeHopHodlNetwork(t *harnessTest,
timeout := time.Duration(time.Second * 15)
ctxt, _ := context.WithTimeout(ctxb, timeout)
aliceChanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0,
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
@ -5496,7 +5741,7 @@ func createThreeHopHodlNetwork(t *harnessTest,
// open, our topology looks like: A -> B -> C.
ctxt, _ = context.WithTimeout(ctxb, timeout)
bobChanPoint := openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, 0,
ctxt, t, net, net.Bob, carol, chanAmt, 0, false,
)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Bob.WaitForNetworkChannelOpen(ctxt, bobChanPoint)
@ -6763,8 +7008,9 @@ func testSwitchCircuitPersistence(net *lntest.NetworkHarness, t *harnessTest) {
// 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, pushAmt)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -6799,8 +7045,9 @@ func testSwitchCircuitPersistence(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, pushAmt)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -6830,8 +7077,9 @@ func testSwitchCircuitPersistence(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, pushAmt)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -7081,8 +7329,9 @@ func testSwitchOfflineDelivery(net *lntest.NetworkHarness, t *harnessTest) {
// 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, pushAmt)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -7117,8 +7366,9 @@ func testSwitchOfflineDelivery(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, pushAmt)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -7148,8 +7398,9 @@ func testSwitchOfflineDelivery(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, pushAmt)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -7404,8 +7655,9 @@ func testSwitchOfflineDeliveryPersistence(net *lntest.NetworkHarness, t *harness
// 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, pushAmt)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -7440,8 +7692,9 @@ func testSwitchOfflineDeliveryPersistence(net *lntest.NetworkHarness, t *harness
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, pushAmt)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -7471,8 +7724,9 @@ func testSwitchOfflineDeliveryPersistence(net *lntest.NetworkHarness, t *harness
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, pushAmt)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -7728,8 +7982,9 @@ func testSwitchOfflineDeliveryOutgoingOffline(
// 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, pushAmt)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointAlice)
txidHash, err := getChanPointFundingTxid(chanPointAlice)
@ -7764,8 +8019,9 @@ func testSwitchOfflineDeliveryOutgoingOffline(
t.Fatalf("unable to send coins to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointDave := openChannelAndAssert(ctxt, t, net, dave,
net.Alice, chanAmt, pushAmt)
chanPointDave := openChannelAndAssert(
ctxt, t, net, dave, net.Alice, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointDave)
txidHash, err = getChanPointFundingTxid(chanPointDave)
if err != nil {
@ -7795,8 +8051,9 @@ func testSwitchOfflineDeliveryOutgoingOffline(
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, pushAmt)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, pushAmt, false,
)
networkChans = append(networkChans, chanPointCarol)
txidHash, err = getChanPointFundingTxid(chanPointCarol)
@ -8000,8 +8257,9 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
// Open a channel between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAlice := openChannelAndAssert(ctxt, t, net, net.Alice,
net.Bob, chanAmt, 0)
chanPointAlice := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointAlice)
// Create Carol and establish a channel from Bob.
@ -8017,8 +8275,9 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to bob: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointBob := openChannelAndAssert(ctxt, t, net, net.Bob,
carol, chanAmt, 0)
chanPointBob := openChannelAndAssert(
ctxt, t, net, net.Bob, carol, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointBob)
// Create Dave and establish a channel from Carol.
@ -8034,8 +8293,9 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarol := openChannelAndAssert(ctxt, t, net, carol,
dave, chanAmt, 0)
chanPointCarol := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, false,
)
networkChans = append(networkChans, chanPointCarol)
// Wait for all nodes to have seen all channels.
@ -8237,6 +8497,10 @@ var testsCases = []*testCase{
name: "private channels",
test: testPrivateChannels,
},
{
name: "multi-hop payments over private channels",
test: testMultiHopOverPrivateChannels,
},
{
name: "multiple channel creation",
test: testBasicChannelCreation,

@ -82,6 +82,8 @@ It has these top-level messages:
NodeUpdate
ChannelEdgeUpdate
ClosedChannelUpdate
HopHint
RouteHint
Invoice
AddInvoiceResponse
PaymentHash
@ -519,9 +521,7 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri
func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
type isChannelPoint_FundingTxid interface {
isChannelPoint_FundingTxid()
}
type isChannelPoint_FundingTxid interface{ isChannelPoint_FundingTxid() }
type ChannelPoint_FundingTxidBytes struct {
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
@ -1608,9 +1608,7 @@ func (m *CloseStatusUpdate) String() string { return proto.CompactTex
func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
type isCloseStatusUpdate_Update interface {
isCloseStatusUpdate_Update()
}
type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() }
type CloseStatusUpdate_ClosePending struct {
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
@ -1873,9 +1871,7 @@ func (m *OpenStatusUpdate) String() string { return proto.CompactText
func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
type isOpenStatusUpdate_Update interface {
isOpenStatusUpdate_Update()
}
type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() }
type OpenStatusUpdate_ChanPending struct {
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
@ -2808,7 +2804,7 @@ func (m *RoutingPolicy) GetFeeRateMilliMsat() int64 {
// *
// A fully authenticated channel along with all its unique attributes.
// Once an authenticated channel announcement has been processed on the network,
// then a instance of ChannelEdgeInfo encapsulating the channels attributes is
// then an instance of ChannelEdgeInfo encapsulating the channels attributes is
// stored. The other portions relevant to routing policy of a channel are stored
// within a ChannelEdgePolicy for each direction of the channel.
type ChannelEdge struct {
@ -3230,6 +3226,80 @@ func (m *ClosedChannelUpdate) GetChanPoint() *ChannelPoint {
return nil
}
type HopHint struct {
// / The public key of the node at the start of the channel.
NodeId string `protobuf:"bytes,1,opt,name=node_id" json:"node_id,omitempty"`
// / The unique identifier of the channel.
ChanId uint64 `protobuf:"varint,2,opt,name=chan_id" json:"chan_id,omitempty"`
// / The base fee of the channel denominated in millisatoshis.
FeeBaseMsat uint32 `protobuf:"varint,3,opt,name=fee_base_msat" json:"fee_base_msat,omitempty"`
// *
// The fee rate of the channel for sending one satoshi across it denominated in
// millionths of a satoshi.
FeeProportionalMillionths uint32 `protobuf:"varint,4,opt,name=fee_proportional_millionths" json:"fee_proportional_millionths,omitempty"`
// / The time-lock delta of the channel.
CltvExpiryDelta uint32 `protobuf:"varint,5,opt,name=cltv_expiry_delta" json:"cltv_expiry_delta,omitempty"`
}
func (m *HopHint) Reset() { *m = HopHint{} }
func (m *HopHint) String() string { return proto.CompactTextString(m) }
func (*HopHint) ProtoMessage() {}
func (*HopHint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} }
func (m *HopHint) GetNodeId() string {
if m != nil {
return m.NodeId
}
return ""
}
func (m *HopHint) GetChanId() uint64 {
if m != nil {
return m.ChanId
}
return 0
}
func (m *HopHint) GetFeeBaseMsat() uint32 {
if m != nil {
return m.FeeBaseMsat
}
return 0
}
func (m *HopHint) GetFeeProportionalMillionths() uint32 {
if m != nil {
return m.FeeProportionalMillionths
}
return 0
}
func (m *HopHint) GetCltvExpiryDelta() uint32 {
if m != nil {
return m.CltvExpiryDelta
}
return 0
}
type RouteHint struct {
// *
// A list of hop hints that when chained together can assist in reaching a
// specific destination.
HopHints []*HopHint `protobuf:"bytes,1,rep,name=hop_hints" json:"hop_hints,omitempty"`
}
func (m *RouteHint) Reset() { *m = RouteHint{} }
func (m *RouteHint) String() string { return proto.CompactTextString(m) }
func (*RouteHint) ProtoMessage() {}
func (*RouteHint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} }
func (m *RouteHint) GetHopHints() []*HopHint {
if m != nil {
return m.HopHints
}
return nil
}
type Invoice struct {
// *
// An optional memo to attach along with the invoice. Used for record keeping
@ -3269,12 +3339,18 @@ type Invoice struct {
FallbackAddr string `protobuf:"bytes,12,opt,name=fallback_addr" json:"fallback_addr,omitempty"`
// / Delta to use for the time-lock of the CLTV extended to the final hop.
CltvExpiry uint64 `protobuf:"varint,13,opt,name=cltv_expiry" json:"cltv_expiry,omitempty"`
// *
// Route hints that can each be individually used to assist in reaching the
// invoice's destination.
RouteHints []*RouteHint `protobuf:"bytes,14,rep,name=route_hints" json:"route_hints,omitempty"`
// / Whether this invoice should include routing hints for private channels.
Private bool `protobuf:"varint,15,opt,name=private" json:"private,omitempty"`
}
func (m *Invoice) Reset() { *m = Invoice{} }
func (m *Invoice) String() string { return proto.CompactTextString(m) }
func (*Invoice) ProtoMessage() {}
func (*Invoice) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} }
func (*Invoice) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} }
func (m *Invoice) GetMemo() string {
if m != nil {
@ -3367,6 +3443,20 @@ func (m *Invoice) GetCltvExpiry() uint64 {
return 0
}
func (m *Invoice) GetRouteHints() []*RouteHint {
if m != nil {
return m.RouteHints
}
return nil
}
func (m *Invoice) GetPrivate() bool {
if m != nil {
return m.Private
}
return false
}
type AddInvoiceResponse struct {
RHash []byte `protobuf:"bytes,1,opt,name=r_hash,proto3" json:"r_hash,omitempty"`
// *
@ -3379,7 +3469,7 @@ type AddInvoiceResponse struct {
func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} }
func (m *AddInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*AddInvoiceResponse) ProtoMessage() {}
func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} }
func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} }
func (m *AddInvoiceResponse) GetRHash() []byte {
if m != nil {
@ -3407,7 +3497,7 @@ type PaymentHash struct {
func (m *PaymentHash) Reset() { *m = PaymentHash{} }
func (m *PaymentHash) String() string { return proto.CompactTextString(m) }
func (*PaymentHash) ProtoMessage() {}
func (*PaymentHash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} }
func (*PaymentHash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} }
func (m *PaymentHash) GetRHashStr() string {
if m != nil {
@ -3431,7 +3521,7 @@ type ListInvoiceRequest struct {
func (m *ListInvoiceRequest) Reset() { *m = ListInvoiceRequest{} }
func (m *ListInvoiceRequest) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceRequest) ProtoMessage() {}
func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} }
func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} }
func (m *ListInvoiceRequest) GetPendingOnly() bool {
if m != nil {
@ -3447,7 +3537,7 @@ type ListInvoiceResponse struct {
func (m *ListInvoiceResponse) Reset() { *m = ListInvoiceResponse{} }
func (m *ListInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceResponse) ProtoMessage() {}
func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} }
func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} }
func (m *ListInvoiceResponse) GetInvoices() []*Invoice {
if m != nil {
@ -3462,7 +3552,7 @@ type InvoiceSubscription struct {
func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} }
func (m *InvoiceSubscription) String() string { return proto.CompactTextString(m) }
func (*InvoiceSubscription) ProtoMessage() {}
func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} }
func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} }
type Payment struct {
// / The payment hash
@ -3482,7 +3572,7 @@ type Payment struct {
func (m *Payment) Reset() { *m = Payment{} }
func (m *Payment) String() string { return proto.CompactTextString(m) }
func (*Payment) ProtoMessage() {}
func (*Payment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} }
func (*Payment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} }
func (m *Payment) GetPaymentHash() string {
if m != nil {
@ -3532,7 +3622,7 @@ type ListPaymentsRequest struct {
func (m *ListPaymentsRequest) Reset() { *m = ListPaymentsRequest{} }
func (m *ListPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsRequest) ProtoMessage() {}
func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} }
func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} }
type ListPaymentsResponse struct {
// / The list of payments
@ -3542,7 +3632,7 @@ type ListPaymentsResponse struct {
func (m *ListPaymentsResponse) Reset() { *m = ListPaymentsResponse{} }
func (m *ListPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsResponse) ProtoMessage() {}
func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} }
func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} }
func (m *ListPaymentsResponse) GetPayments() []*Payment {
if m != nil {
@ -3557,7 +3647,7 @@ type DeleteAllPaymentsRequest struct {
func (m *DeleteAllPaymentsRequest) Reset() { *m = DeleteAllPaymentsRequest{} }
func (m *DeleteAllPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsRequest) ProtoMessage() {}
func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} }
func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} }
type DeleteAllPaymentsResponse struct {
}
@ -3565,7 +3655,7 @@ type DeleteAllPaymentsResponse struct {
func (m *DeleteAllPaymentsResponse) Reset() { *m = DeleteAllPaymentsResponse{} }
func (m *DeleteAllPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsResponse) ProtoMessage() {}
func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} }
func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} }
type DebugLevelRequest struct {
Show bool `protobuf:"varint,1,opt,name=show" json:"show,omitempty"`
@ -3575,7 +3665,7 @@ type DebugLevelRequest struct {
func (m *DebugLevelRequest) Reset() { *m = DebugLevelRequest{} }
func (m *DebugLevelRequest) String() string { return proto.CompactTextString(m) }
func (*DebugLevelRequest) ProtoMessage() {}
func (*DebugLevelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} }
func (*DebugLevelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} }
func (m *DebugLevelRequest) GetShow() bool {
if m != nil {
@ -3598,7 +3688,7 @@ type DebugLevelResponse struct {
func (m *DebugLevelResponse) Reset() { *m = DebugLevelResponse{} }
func (m *DebugLevelResponse) String() string { return proto.CompactTextString(m) }
func (*DebugLevelResponse) ProtoMessage() {}
func (*DebugLevelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} }
func (*DebugLevelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} }
func (m *DebugLevelResponse) GetSubSystems() string {
if m != nil {
@ -3615,7 +3705,7 @@ type PayReqString struct {
func (m *PayReqString) Reset() { *m = PayReqString{} }
func (m *PayReqString) String() string { return proto.CompactTextString(m) }
func (*PayReqString) ProtoMessage() {}
func (*PayReqString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} }
func (*PayReqString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} }
func (m *PayReqString) GetPayReq() string {
if m != nil {
@ -3634,12 +3724,13 @@ type PayReq struct {
DescriptionHash string `protobuf:"bytes,7,opt,name=description_hash" json:"description_hash,omitempty"`
FallbackAddr string `protobuf:"bytes,8,opt,name=fallback_addr" json:"fallback_addr,omitempty"`
CltvExpiry int64 `protobuf:"varint,9,opt,name=cltv_expiry" json:"cltv_expiry,omitempty"`
RouteHints []*RouteHint `protobuf:"bytes,10,rep,name=route_hints" json:"route_hints,omitempty"`
}
func (m *PayReq) Reset() { *m = PayReq{} }
func (m *PayReq) String() string { return proto.CompactTextString(m) }
func (*PayReq) ProtoMessage() {}
func (*PayReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} }
func (*PayReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} }
func (m *PayReq) GetDestination() string {
if m != nil {
@ -3704,13 +3795,20 @@ func (m *PayReq) GetCltvExpiry() int64 {
return 0
}
func (m *PayReq) GetRouteHints() []*RouteHint {
if m != nil {
return m.RouteHints
}
return nil
}
type FeeReportRequest struct {
}
func (m *FeeReportRequest) Reset() { *m = FeeReportRequest{} }
func (m *FeeReportRequest) String() string { return proto.CompactTextString(m) }
func (*FeeReportRequest) ProtoMessage() {}
func (*FeeReportRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} }
func (*FeeReportRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} }
type ChannelFeeReport struct {
// / The channel that this fee report belongs to.
@ -3726,7 +3824,7 @@ type ChannelFeeReport struct {
func (m *ChannelFeeReport) Reset() { *m = ChannelFeeReport{} }
func (m *ChannelFeeReport) String() string { return proto.CompactTextString(m) }
func (*ChannelFeeReport) ProtoMessage() {}
func (*ChannelFeeReport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} }
func (*ChannelFeeReport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{92} }
func (m *ChannelFeeReport) GetChanPoint() string {
if m != nil {
@ -3770,7 +3868,7 @@ type FeeReportResponse struct {
func (m *FeeReportResponse) Reset() { *m = FeeReportResponse{} }
func (m *FeeReportResponse) String() string { return proto.CompactTextString(m) }
func (*FeeReportResponse) ProtoMessage() {}
func (*FeeReportResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} }
func (*FeeReportResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} }
func (m *FeeReportResponse) GetChannelFees() []*ChannelFeeReport {
if m != nil {
@ -3816,11 +3914,9 @@ type PolicyUpdateRequest struct {
func (m *PolicyUpdateRequest) Reset() { *m = PolicyUpdateRequest{} }
func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateRequest) ProtoMessage() {}
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{92} }
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
type isPolicyUpdateRequest_Scope interface {
isPolicyUpdateRequest_Scope()
}
type isPolicyUpdateRequest_Scope interface{ isPolicyUpdateRequest_Scope() }
type PolicyUpdateRequest_Global struct {
Global bool `protobuf:"varint,1,opt,name=global,oneof"`
@ -3953,7 +4049,7 @@ type PolicyUpdateResponse struct {
func (m *PolicyUpdateResponse) Reset() { *m = PolicyUpdateResponse{} }
func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateResponse) ProtoMessage() {}
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} }
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{95} }
type ForwardingHistoryRequest struct {
// / Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset.
@ -3969,7 +4065,7 @@ type ForwardingHistoryRequest struct {
func (m *ForwardingHistoryRequest) Reset() { *m = ForwardingHistoryRequest{} }
func (m *ForwardingHistoryRequest) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryRequest) ProtoMessage() {}
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{96} }
func (m *ForwardingHistoryRequest) GetStartTime() uint64 {
if m != nil {
@ -4017,7 +4113,7 @@ type ForwardingEvent struct {
func (m *ForwardingEvent) Reset() { *m = ForwardingEvent{} }
func (m *ForwardingEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardingEvent) ProtoMessage() {}
func (*ForwardingEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{95} }
func (*ForwardingEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{97} }
func (m *ForwardingEvent) GetTimestamp() uint64 {
if m != nil {
@ -4071,7 +4167,7 @@ type ForwardingHistoryResponse struct {
func (m *ForwardingHistoryResponse) Reset() { *m = ForwardingHistoryResponse{} }
func (m *ForwardingHistoryResponse) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryResponse) ProtoMessage() {}
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{96} }
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{98} }
func (m *ForwardingHistoryResponse) GetForwardingEvents() []*ForwardingEvent {
if m != nil {
@ -4166,6 +4262,8 @@ func init() {
proto.RegisterType((*NodeUpdate)(nil), "lnrpc.NodeUpdate")
proto.RegisterType((*ChannelEdgeUpdate)(nil), "lnrpc.ChannelEdgeUpdate")
proto.RegisterType((*ClosedChannelUpdate)(nil), "lnrpc.ClosedChannelUpdate")
proto.RegisterType((*HopHint)(nil), "lnrpc.HopHint")
proto.RegisterType((*RouteHint)(nil), "lnrpc.RouteHint")
proto.RegisterType((*Invoice)(nil), "lnrpc.Invoice")
proto.RegisterType((*AddInvoiceResponse)(nil), "lnrpc.AddInvoiceResponse")
proto.RegisterType((*PaymentHash)(nil), "lnrpc.PaymentHash")
@ -6203,346 +6301,353 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 5446 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0xcd, 0x93, 0x1c, 0xc9,
0x55, 0xb8, 0xaa, 0xa7, 0xe7, 0xa3, 0x5f, 0xf7, 0x7c, 0xe5, 0x8c, 0x46, 0xad, 0x5e, 0x59, 0xab,
0x4d, 0x6f, 0x58, 0xfa, 0xcd, 0x6f, 0xd1, 0x68, 0xc7, 0xf6, 0xb2, 0xac, 0xc0, 0x0e, 0x7d, 0xcf,
0xda, 0x5a, 0x79, 0x5c, 0x23, 0x79, 0xc1, 0x0b, 0xb4, 0x6b, 0xba, 0x73, 0x7a, 0xca, 0xea, 0xae,
0xaa, 0xad, 0xaa, 0x9e, 0x51, 0xef, 0xa2, 0x08, 0x3e, 0x22, 0x38, 0xe1, 0xe0, 0x00, 0x17, 0x43,
0x38, 0x88, 0xb0, 0x2f, 0x70, 0xe0, 0xc8, 0xc9, 0x04, 0x7f, 0x80, 0x23, 0x08, 0x0e, 0x3e, 0x39,
0xb8, 0xf1, 0x71, 0x00, 0x82, 0x23, 0x17, 0x0e, 0x04, 0xf1, 0x5e, 0x7e, 0x54, 0x66, 0x55, 0x8d,
0x24, 0xdb, 0xc0, 0xad, 0xf3, 0xe5, 0xcb, 0x97, 0x5f, 0xef, 0xbd, 0x7c, 0x5f, 0xd5, 0xd0, 0x4a,
0x93, 0xc1, 0xf5, 0x24, 0x8d, 0xf3, 0x98, 0xcd, 0x8f, 0xa3, 0x34, 0x19, 0xf4, 0x2e, 0x8d, 0xe2,
0x78, 0x34, 0x16, 0x3b, 0x41, 0x12, 0xee, 0x04, 0x51, 0x14, 0xe7, 0x41, 0x1e, 0xc6, 0x51, 0x26,
0x91, 0xf8, 0xb7, 0x60, 0xe5, 0x81, 0x88, 0x0e, 0x84, 0x18, 0xfa, 0xe2, 0xe3, 0xa9, 0xc8, 0x72,
0xf6, 0xff, 0x61, 0x3d, 0x10, 0x9f, 0x08, 0x31, 0xec, 0x27, 0x41, 0x96, 0x25, 0xc7, 0x69, 0x90,
0x89, 0xae, 0x77, 0xc5, 0xbb, 0xd6, 0xf1, 0xd7, 0x64, 0xc7, 0xbe, 0x81, 0xb3, 0x37, 0xa0, 0x93,
0x21, 0xaa, 0x88, 0xf2, 0x34, 0x4e, 0x66, 0xdd, 0x06, 0xe1, 0xb5, 0x11, 0x76, 0x4f, 0x82, 0xf8,
0x18, 0x56, 0xcd, 0x0c, 0x59, 0x12, 0x47, 0x99, 0x60, 0x37, 0x60, 0x73, 0x10, 0x26, 0xc7, 0x22,
0xed, 0xd3, 0xe0, 0x49, 0x24, 0x26, 0x71, 0x14, 0x0e, 0xba, 0xde, 0x95, 0xb9, 0x6b, 0x2d, 0x9f,
0xc9, 0x3e, 0x1c, 0xf1, 0x81, 0xea, 0x61, 0x57, 0x61, 0x55, 0x44, 0x12, 0x2e, 0x86, 0x34, 0x4a,
0x4d, 0xb5, 0x52, 0x80, 0x71, 0x00, 0xff, 0x53, 0x0f, 0xd6, 0xdf, 0x8f, 0xc2, 0xfc, 0xc3, 0x60,
0x3c, 0x16, 0xb9, 0xde, 0xd3, 0x55, 0x58, 0x3d, 0x25, 0x00, 0xed, 0xe9, 0x34, 0x4e, 0x87, 0x6a,
0x47, 0x2b, 0x12, 0xbc, 0xaf, 0xa0, 0x67, 0xae, 0xac, 0x71, 0xe6, 0xca, 0x6a, 0x8f, 0x6b, 0xae,
0xfe, 0xb8, 0xf8, 0x26, 0x30, 0x7b, 0x71, 0xf2, 0x38, 0xf8, 0x97, 0x60, 0xe3, 0x49, 0x34, 0x8e,
0x07, 0x4f, 0x7f, 0xb6, 0x45, 0xf3, 0x2d, 0xd8, 0x74, 0xc7, 0x2b, 0xba, 0xdf, 0x6d, 0x40, 0xfb,
0x71, 0x1a, 0x44, 0x59, 0x30, 0xc0, 0x2b, 0x67, 0x5d, 0x58, 0xcc, 0x9f, 0xf5, 0x8f, 0x83, 0xec,
0x98, 0x08, 0xb5, 0x7c, 0xdd, 0x64, 0x5b, 0xb0, 0x10, 0x4c, 0xe2, 0x69, 0x94, 0xd3, 0xa9, 0xce,
0xf9, 0xaa, 0xc5, 0xde, 0x82, 0xf5, 0x68, 0x3a, 0xe9, 0x0f, 0xe2, 0xe8, 0x28, 0x4c, 0x27, 0x92,
0x71, 0x68, 0x73, 0xf3, 0x7e, 0xb5, 0x83, 0x5d, 0x06, 0x38, 0xc4, 0x65, 0xc8, 0x29, 0x9a, 0x34,
0x85, 0x05, 0x61, 0x1c, 0x3a, 0xaa, 0x25, 0xc2, 0xd1, 0x71, 0xde, 0x9d, 0x27, 0x42, 0x0e, 0x0c,
0x69, 0xe4, 0xe1, 0x44, 0xf4, 0xb3, 0x3c, 0x98, 0x24, 0xdd, 0x05, 0x5a, 0x8d, 0x05, 0xa1, 0xfe,
0x38, 0x0f, 0xc6, 0xfd, 0x23, 0x21, 0xb2, 0xee, 0xa2, 0xea, 0x37, 0x10, 0xf6, 0x39, 0x58, 0x19,
0x8a, 0x2c, 0xef, 0x07, 0xc3, 0x61, 0x2a, 0xb2, 0x4c, 0x64, 0xdd, 0x25, 0xba, 0xba, 0x12, 0x94,
0x77, 0x61, 0xeb, 0x81, 0xc8, 0xad, 0xd3, 0xc9, 0xd4, 0xb1, 0xf3, 0x87, 0xc0, 0x2c, 0xf0, 0x5d,
0x91, 0x07, 0xe1, 0x38, 0x63, 0xef, 0x40, 0x27, 0xb7, 0x90, 0x89, 0x55, 0xdb, 0xbb, 0xec, 0x3a,
0xc9, 0xd8, 0x75, 0x6b, 0x80, 0xef, 0xe0, 0xf1, 0xff, 0xf4, 0xa0, 0x7d, 0x20, 0x22, 0x23, 0x5d,
0x0c, 0x9a, 0xb8, 0x12, 0x75, 0x93, 0xf4, 0x9b, 0xbd, 0x0e, 0x6d, 0x5a, 0x5d, 0x96, 0xa7, 0x61,
0x34, 0xa2, 0x2b, 0x68, 0xf9, 0x80, 0xa0, 0x03, 0x82, 0xb0, 0x35, 0x98, 0x0b, 0x26, 0x39, 0x1d,
0xfc, 0x9c, 0x8f, 0x3f, 0x51, 0xee, 0x92, 0x60, 0x36, 0x11, 0x51, 0x5e, 0x1c, 0x76, 0xc7, 0x6f,
0x2b, 0xd8, 0x1e, 0x9e, 0xf6, 0x75, 0xd8, 0xb0, 0x51, 0x34, 0xf5, 0x79, 0xa2, 0xbe, 0x6e, 0x61,
0xaa, 0x49, 0xae, 0xc2, 0xaa, 0xc6, 0x4f, 0xe5, 0x62, 0xe9, 0xf8, 0x5b, 0xfe, 0x8a, 0x02, 0xeb,
0x2d, 0x5c, 0x83, 0xb5, 0xa3, 0x30, 0x0a, 0xc6, 0xfd, 0xc1, 0x38, 0x3f, 0xe9, 0x0f, 0xc5, 0x38,
0x0f, 0xe8, 0x22, 0xe6, 0xfd, 0x15, 0x82, 0xdf, 0x19, 0xe7, 0x27, 0x77, 0x11, 0xca, 0xff, 0xd8,
0x83, 0x8e, 0xdc, 0xbc, 0x12, 0xfc, 0x37, 0x61, 0x59, 0xcf, 0x21, 0xd2, 0x34, 0x4e, 0x15, 0x1f,
0xba, 0x40, 0xb6, 0x0d, 0x6b, 0x1a, 0x90, 0xa4, 0x22, 0x9c, 0x04, 0x23, 0xa1, 0xa4, 0xbd, 0x02,
0x67, 0xbb, 0x05, 0xc5, 0x34, 0x9e, 0xe6, 0x52, 0xf4, 0xda, 0xbb, 0x1d, 0x75, 0x31, 0x3e, 0xc2,
0x7c, 0x17, 0x85, 0x7f, 0xdf, 0x83, 0xce, 0x9d, 0xe3, 0x20, 0x8a, 0xc4, 0x78, 0x3f, 0x0e, 0xa3,
0x9c, 0xdd, 0x00, 0x76, 0x34, 0x8d, 0x86, 0x61, 0x34, 0xea, 0xe7, 0xcf, 0xc2, 0x61, 0xff, 0x70,
0x96, 0x8b, 0x4c, 0x5e, 0xd1, 0xde, 0x39, 0xbf, 0xa6, 0x8f, 0xbd, 0x05, 0x6b, 0x0e, 0x34, 0xcb,
0x53, 0x79, 0x6f, 0x7b, 0xe7, 0xfc, 0x4a, 0x0f, 0x32, 0x7e, 0x3c, 0xcd, 0x93, 0x69, 0xde, 0x0f,
0xa3, 0xa1, 0x78, 0x46, 0x6b, 0x5c, 0xf6, 0x1d, 0xd8, 0xed, 0x15, 0xe8, 0xd8, 0xe3, 0xf8, 0x97,
0x60, 0xed, 0x21, 0x4a, 0x44, 0x14, 0x46, 0xa3, 0x5b, 0x92, 0x6d, 0x51, 0x4c, 0x93, 0xe9, 0xe1,
0x53, 0x31, 0x53, 0xe7, 0xa6, 0x5a, 0xc8, 0x54, 0xc7, 0x71, 0x96, 0x2b, 0xce, 0xa1, 0xdf, 0xfc,
0x1f, 0x3d, 0x58, 0xc5, 0xb3, 0xff, 0x20, 0x88, 0x66, 0xfa, 0xe6, 0x1e, 0x42, 0x07, 0x49, 0x3d,
0x8e, 0x6f, 0x49, 0x61, 0x97, 0x4c, 0x7c, 0x4d, 0x9d, 0x55, 0x09, 0xfb, 0xba, 0x8d, 0x8a, 0xca,
0x7c, 0xe6, 0x3b, 0xa3, 0x91, 0x6d, 0xf3, 0x20, 0x1d, 0x89, 0x9c, 0xd4, 0x80, 0x52, 0x0b, 0x20,
0x41, 0x77, 0xe2, 0xe8, 0x88, 0x5d, 0x81, 0x4e, 0x16, 0xe4, 0xfd, 0x44, 0xa4, 0x74, 0x6a, 0xc4,
0x7a, 0x73, 0x3e, 0x64, 0x41, 0xbe, 0x2f, 0xd2, 0xdb, 0xb3, 0x5c, 0xf4, 0xbe, 0x0c, 0xeb, 0x95,
0x59, 0x90, 0xdb, 0x8b, 0x2d, 0xe2, 0x4f, 0xb6, 0x09, 0xf3, 0x27, 0xc1, 0x78, 0x2a, 0x94, 0x76,
0x92, 0x8d, 0xf7, 0x1a, 0xef, 0x7a, 0xfc, 0x73, 0xb0, 0x56, 0x2c, 0x5b, 0x31, 0x19, 0x83, 0x26,
0x9e, 0xa0, 0x22, 0x40, 0xbf, 0xf9, 0xef, 0x78, 0x12, 0xf1, 0x4e, 0x1c, 0x1a, 0x49, 0x47, 0x44,
0x54, 0x08, 0x1a, 0x11, 0x7f, 0x9f, 0xa9, 0x09, 0x7f, 0xfe, 0xcd, 0xf2, 0xab, 0xb0, 0x6e, 0x2d,
0xe1, 0x05, 0x8b, 0xfd, 0x8e, 0x07, 0xeb, 0x8f, 0xc4, 0xa9, 0xba, 0x75, 0xbd, 0xda, 0x77, 0xa1,
0x99, 0xcf, 0x12, 0xf9, 0x14, 0xaf, 0xec, 0xbe, 0xa9, 0x2e, 0xad, 0x82, 0x77, 0x5d, 0x35, 0x1f,
0xcf, 0x12, 0xe1, 0xd3, 0x08, 0xfe, 0x25, 0x68, 0x5b, 0x40, 0x76, 0x01, 0x36, 0x3e, 0x7c, 0xff,
0xf1, 0xa3, 0x7b, 0x07, 0x07, 0xfd, 0xfd, 0x27, 0xb7, 0xbf, 0x7a, 0xef, 0xd7, 0xfa, 0x7b, 0xb7,
0x0e, 0xf6, 0xd6, 0xce, 0xb1, 0x2d, 0x60, 0x8f, 0xee, 0x1d, 0x3c, 0xbe, 0x77, 0xd7, 0x81, 0x7b,
0xbc, 0x07, 0xdd, 0x47, 0xe2, 0xf4, 0xc3, 0x30, 0x8f, 0x44, 0x96, 0xb9, 0xb3, 0xf1, 0xeb, 0xc0,
0xec, 0x25, 0xa8, 0x5d, 0x75, 0x61, 0x51, 0xa9, 0x5a, 0xfd, 0xd2, 0xa8, 0x26, 0xff, 0x1c, 0xb0,
0x83, 0x70, 0x14, 0x7d, 0x20, 0xb2, 0x2c, 0x18, 0x09, 0xbd, 0xb7, 0x35, 0x98, 0x9b, 0x64, 0x23,
0xa5, 0x14, 0xf1, 0x27, 0xff, 0x3c, 0x6c, 0x38, 0x78, 0x8a, 0xf0, 0x25, 0x68, 0x65, 0xe1, 0x28,
0x0a, 0xf2, 0x69, 0x2a, 0x14, 0xe9, 0x02, 0xc0, 0xef, 0xc3, 0xe6, 0x37, 0x44, 0x1a, 0x1e, 0xcd,
0x5e, 0x46, 0xde, 0xa5, 0xd3, 0x28, 0xd3, 0xb9, 0x07, 0xe7, 0x4b, 0x74, 0xd4, 0xf4, 0x92, 0x11,
0xd5, 0x75, 0x2d, 0xf9, 0xb2, 0x61, 0x89, 0x65, 0xc3, 0x16, 0x4b, 0xfe, 0x04, 0xd8, 0x9d, 0x38,
0x8a, 0xc4, 0x20, 0xdf, 0x17, 0x22, 0x2d, 0xec, 0xab, 0x82, 0xeb, 0xda, 0xbb, 0x17, 0xd4, 0x3d,
0x96, 0x65, 0x5d, 0xb1, 0x23, 0x83, 0x66, 0x22, 0xd2, 0x09, 0x11, 0x5e, 0xf2, 0xe9, 0x37, 0x3f,
0x0f, 0x1b, 0x0e, 0x59, 0xf5, 0xda, 0xbf, 0x0d, 0xe7, 0xef, 0x86, 0xd9, 0xa0, 0x3a, 0x61, 0x17,
0x16, 0x93, 0xe9, 0x61, 0xbf, 0x90, 0x29, 0xdd, 0xc4, 0x47, 0xb0, 0x3c, 0x44, 0x11, 0xfb, 0x7d,
0x0f, 0x9a, 0x7b, 0x8f, 0x1f, 0xde, 0x61, 0x3d, 0x58, 0x0a, 0xa3, 0x41, 0x3c, 0xc1, 0xa7, 0x43,
0x6e, 0xda, 0xb4, 0xcf, 0x94, 0x95, 0x4b, 0xd0, 0xa2, 0x17, 0x07, 0xdf, 0x75, 0x65, 0x0a, 0x15,
0x00, 0xb4, 0x29, 0xc4, 0xb3, 0x24, 0x4c, 0xc9, 0x68, 0xd0, 0xa6, 0x40, 0x93, 0x34, 0x62, 0xb5,
0x83, 0xff, 0x57, 0x13, 0x16, 0x95, 0xae, 0xa6, 0xf9, 0x06, 0x79, 0x78, 0x22, 0xd4, 0x4a, 0x54,
0x0b, 0x5f, 0x95, 0x54, 0x4c, 0xe2, 0x5c, 0xf4, 0x9d, 0x6b, 0x70, 0x81, 0x88, 0x35, 0x90, 0x84,
0xfa, 0x09, 0x6a, 0x7d, 0x5a, 0x59, 0xcb, 0x77, 0x81, 0x78, 0x58, 0x08, 0xe8, 0x87, 0x43, 0x5a,
0x53, 0xd3, 0xd7, 0x4d, 0x3c, 0x89, 0x41, 0x90, 0x04, 0x83, 0x30, 0x9f, 0x29, 0xe1, 0x36, 0x6d,
0xa4, 0x3d, 0x8e, 0x07, 0xc1, 0xb8, 0x7f, 0x18, 0x8c, 0x83, 0x68, 0x20, 0x94, 0xe1, 0xe2, 0x02,
0xd1, 0x36, 0x51, 0x4b, 0xd2, 0x68, 0xd2, 0x7e, 0x29, 0x41, 0xd1, 0xc6, 0x19, 0xc4, 0x93, 0x49,
0x98, 0xa3, 0x49, 0xd3, 0x5d, 0x92, 0x8a, 0xa4, 0x80, 0xd0, 0x4e, 0x64, 0xeb, 0x54, 0x9e, 0x5e,
0x4b, 0xce, 0xe6, 0x00, 0x91, 0xca, 0x91, 0x10, 0xa4, 0x90, 0x9e, 0x9e, 0x76, 0x41, 0x52, 0x29,
0x20, 0x78, 0x0f, 0xd3, 0x28, 0x13, 0x79, 0x3e, 0x16, 0x43, 0xb3, 0xa0, 0x36, 0xa1, 0x55, 0x3b,
0xd8, 0x0d, 0xd8, 0x90, 0x56, 0x56, 0x16, 0xe4, 0x71, 0x76, 0x1c, 0x66, 0xfd, 0x4c, 0x44, 0x79,
0xb7, 0x43, 0xf8, 0x75, 0x5d, 0xec, 0x5d, 0xb8, 0x50, 0x02, 0xa7, 0x62, 0x20, 0xc2, 0x13, 0x31,
0xec, 0x2e, 0xd3, 0xa8, 0xb3, 0xba, 0xd9, 0x15, 0x68, 0xa3, 0x71, 0x39, 0x4d, 0x86, 0x01, 0xbe,
0xc3, 0x2b, 0x74, 0x0f, 0x36, 0x88, 0xbd, 0x0d, 0xcb, 0x89, 0x90, 0x8f, 0xe5, 0x71, 0x3e, 0x1e,
0x64, 0xdd, 0x55, 0x7a, 0xc9, 0xda, 0x4a, 0x98, 0x90, 0x73, 0x7d, 0x17, 0x03, 0x99, 0x72, 0x90,
0x91, 0xb9, 0x12, 0xcc, 0xba, 0x6b, 0xc4, 0x6e, 0x05, 0x80, 0x64, 0x24, 0x0d, 0x4f, 0x82, 0x5c,
0x74, 0xd7, 0x89, 0xb7, 0x74, 0x93, 0xff, 0x99, 0x07, 0x1b, 0x0f, 0xc3, 0x2c, 0x57, 0x4c, 0x68,
0xd4, 0xf1, 0xeb, 0xd0, 0x96, 0xec, 0xd7, 0x8f, 0xa3, 0xf1, 0x4c, 0x71, 0x24, 0x48, 0xd0, 0xd7,
0xa2, 0xf1, 0x8c, 0x7d, 0x16, 0x96, 0xc3, 0xc8, 0x46, 0x91, 0x32, 0xdc, 0xd1, 0x40, 0x42, 0x7a,
0x1d, 0xda, 0xc9, 0xf4, 0x70, 0x1c, 0x0e, 0x24, 0xca, 0x9c, 0xa4, 0x22, 0x41, 0x84, 0x80, 0x86,
0x9e, 0x5c, 0x89, 0xc4, 0x68, 0x12, 0x46, 0x5b, 0xc1, 0x10, 0x85, 0xdf, 0x86, 0x4d, 0x77, 0x81,
0x4a, 0x59, 0x6d, 0xc3, 0x92, 0xe2, 0xed, 0xac, 0xdb, 0xa6, 0xf3, 0x59, 0x51, 0xe7, 0xa3, 0x50,
0x7d, 0xd3, 0xcf, 0xff, 0xd5, 0x83, 0x26, 0x2a, 0x80, 0xb3, 0x95, 0x85, 0xad, 0xd3, 0xe7, 0x1c,
0x9d, 0x4e, 0x76, 0x3f, 0x5a, 0x45, 0x92, 0x25, 0xa4, 0xd8, 0x58, 0x90, 0xa2, 0x3f, 0x15, 0x83,
0x13, 0x92, 0x1d, 0xd3, 0x8f, 0x10, 0x94, 0x2c, 0x7c, 0x3a, 0x69, 0xb4, 0x14, 0x1c, 0xd3, 0xd6,
0x7d, 0x34, 0x72, 0xb1, 0xe8, 0xa3, 0x71, 0x5d, 0x58, 0x0c, 0xa3, 0xc3, 0x78, 0x1a, 0x0d, 0x49,
0x48, 0x96, 0x7c, 0xdd, 0xc4, 0xcb, 0x4e, 0xc8, 0x92, 0x0a, 0x27, 0x42, 0x49, 0x47, 0x01, 0xe0,
0x0c, 0x4d, 0xab, 0x8c, 0x14, 0x9e, 0x79, 0xc7, 0xde, 0x81, 0x75, 0x0b, 0xa6, 0x4e, 0xf0, 0x0d,
0x98, 0x4f, 0x10, 0xa0, 0x0c, 0x25, 0xcd, 0x5e, 0xa4, 0x29, 0x65, 0x0f, 0x5f, 0x43, 0xff, 0x39,
0x7f, 0x3f, 0x3a, 0x8a, 0x35, 0xa5, 0x9f, 0xcc, 0xa1, 0xc3, 0xab, 0x40, 0x8a, 0xd0, 0x35, 0x58,
0x0d, 0x87, 0x22, 0xca, 0xc3, 0x7c, 0xd6, 0x77, 0x2c, 0xb8, 0x32, 0x18, 0x5f, 0x98, 0x60, 0x1c,
0x06, 0x99, 0xd2, 0x61, 0xb2, 0xc1, 0x76, 0x61, 0x13, 0xd9, 0x5f, 0x73, 0xb4, 0xb9, 0x56, 0x69,
0x48, 0xd6, 0xf6, 0xa1, 0xc4, 0x22, 0x5c, 0x71, 0xa0, 0x19, 0x22, 0x35, 0x6d, 0x5d, 0x17, 0x9e,
0x9a, 0xa4, 0x84, 0x5b, 0x9e, 0x97, 0x22, 0x62, 0x00, 0x15, 0xef, 0x6d, 0x41, 0x1a, 0xb1, 0x65,
0xef, 0xcd, 0xf2, 0x00, 0x97, 0x2a, 0x1e, 0xe0, 0x35, 0x58, 0xcd, 0x66, 0xd1, 0x40, 0x0c, 0xfb,
0x79, 0x8c, 0xf3, 0x86, 0x11, 0xdd, 0xce, 0x92, 0x5f, 0x06, 0x93, 0xaf, 0x2a, 0xb2, 0x3c, 0x12,
0x39, 0xa9, 0xae, 0x25, 0x5f, 0x37, 0xf1, 0x15, 0x20, 0x14, 0xc9, 0xd4, 0x2d, 0x5f, 0xb5, 0xf0,
0xa9, 0x9c, 0xa6, 0x61, 0xd6, 0xed, 0x10, 0x94, 0x7e, 0xb3, 0x2f, 0xc0, 0xf9, 0x43, 0xf4, 0xac,
0x8e, 0x45, 0x30, 0x14, 0x29, 0xdd, 0xbe, 0x74, 0x2c, 0xa5, 0x06, 0xaa, 0xef, 0xc4, 0xb9, 0x4f,
0x44, 0x9a, 0x85, 0x71, 0x44, 0xba, 0xa7, 0xe5, 0xeb, 0x26, 0xff, 0x84, 0x5e, 0x74, 0xe3, 0xf2,
0x3e, 0x21, 0x75, 0xc4, 0x5e, 0x83, 0x96, 0xdc, 0x63, 0x76, 0x1c, 0x28, 0x23, 0x63, 0x89, 0x00,
0x07, 0xc7, 0x01, 0x0a, 0xb0, 0x73, 0x6c, 0x0d, 0xb2, 0x1c, 0xdb, 0x04, 0xdb, 0x93, 0xa7, 0xf6,
0x26, 0xac, 0x68, 0x67, 0x3a, 0xeb, 0x8f, 0xc5, 0x51, 0xae, 0x1d, 0x84, 0x68, 0x3a, 0xc1, 0xe9,
0xb2, 0x87, 0xe2, 0x28, 0xe7, 0x8f, 0x60, 0x5d, 0xc9, 0xed, 0xd7, 0x12, 0xa1, 0xa7, 0xfe, 0xa5,
0xf2, 0xa3, 0x26, 0xad, 0x8a, 0x0d, 0x57, 0xd0, 0xc9, 0xcb, 0x29, 0xbd, 0x74, 0xdc, 0x07, 0xa6,
0xba, 0xef, 0x8c, 0xe3, 0x4c, 0x28, 0x82, 0x1c, 0x3a, 0x83, 0x71, 0x9c, 0x69, 0x37, 0x44, 0x6d,
0xc7, 0x81, 0xe1, 0xf9, 0x64, 0xd3, 0xc1, 0x00, 0x35, 0x81, 0xd4, 0x69, 0xba, 0xc9, 0xff, 0xdc,
0x83, 0x0d, 0xa2, 0xa6, 0x35, 0x8c, 0xb1, 0x5d, 0x5f, 0x7d, 0x99, 0x9d, 0x81, 0xed, 0x9a, 0x6d,
0xc2, 0xfc, 0x51, 0x9c, 0x0e, 0x84, 0x9a, 0x49, 0x36, 0x7e, 0x7a, 0x6b, 0xbc, 0x59, 0xb1, 0xc6,
0x7f, 0xe2, 0xc1, 0x3a, 0x2d, 0xf5, 0x20, 0x0f, 0xf2, 0x69, 0xa6, 0xb6, 0xff, 0xcb, 0xb0, 0x8c,
0x5b, 0x15, 0x5a, 0x9c, 0xd4, 0x42, 0x37, 0x8d, 0xe4, 0x13, 0x54, 0x22, 0xef, 0x9d, 0xf3, 0x5d,
0x64, 0xf6, 0x65, 0xe8, 0xd8, 0x11, 0x11, 0x5a, 0x73, 0x7b, 0xf7, 0xa2, 0xde, 0x65, 0x85, 0x73,
0xf6, 0xce, 0xf9, 0xce, 0x00, 0x76, 0x13, 0x80, 0xcc, 0x0d, 0x22, 0xab, 0x5c, 0xd9, 0x8b, 0xee,
0x21, 0x59, 0x97, 0xb5, 0x77, 0xce, 0xb7, 0xd0, 0x6f, 0x2f, 0xc1, 0x82, 0x7c, 0x1f, 0xf9, 0x03,
0x58, 0x76, 0x56, 0xea, 0x78, 0x19, 0x1d, 0xe9, 0x65, 0x54, 0x9c, 0xd2, 0x46, 0xd5, 0x29, 0xe5,
0xff, 0xdc, 0x00, 0x86, 0xdc, 0x56, 0xba, 0x4e, 0x7c, 0xa0, 0xe3, 0xa1, 0x63, 0x6e, 0x75, 0x7c,
0x1b, 0xc4, 0xae, 0x03, 0xb3, 0x9a, 0x3a, 0xf6, 0x20, 0xdf, 0x8d, 0x9a, 0x1e, 0x54, 0x70, 0xd2,
0x56, 0xd2, 0x3e, 0xb0, 0x32, 0x2c, 0xe5, 0xbd, 0xd5, 0xf6, 0xe1, 0xd3, 0x90, 0x4c, 0xb3, 0x63,
0x34, 0x20, 0xb4, 0x41, 0xa6, 0xdb, 0x65, 0x06, 0x59, 0x78, 0x29, 0x83, 0x2c, 0x96, 0x19, 0xc4,
0x36, 0x09, 0x96, 0x1c, 0x93, 0x00, 0xed, 0xaf, 0x49, 0x18, 0x91, 0x5d, 0xd1, 0x9f, 0xe0, 0xec,
0xca, 0xfe, 0x72, 0x80, 0x6c, 0x1b, 0xd6, 0x94, 0x5d, 0x57, 0xd8, 0x1d, 0x40, 0x67, 0x5c, 0x81,
0xf3, 0x1f, 0x7b, 0xb0, 0x86, 0xe7, 0xec, 0xf0, 0xe2, 0x7b, 0x40, 0xa2, 0xf0, 0x8a, 0xac, 0xe8,
0xe0, 0xfe, 0xfc, 0x9c, 0xf8, 0x2e, 0xb4, 0x88, 0x60, 0x9c, 0x88, 0x48, 0x31, 0x62, 0xd7, 0x65,
0xc4, 0x42, 0x0b, 0xed, 0x9d, 0xf3, 0x0b, 0x64, 0x8b, 0x0d, 0xff, 0xce, 0x83, 0xb6, 0x5a, 0xe6,
0xcf, 0xec, 0x4b, 0xf4, 0x60, 0x09, 0x39, 0xd2, 0x32, 0xd8, 0x4d, 0x1b, 0x5f, 0x93, 0x09, 0x3a,
0x6c, 0xf8, 0x7c, 0x3a, 0x7e, 0x44, 0x19, 0x8c, 0x6f, 0x21, 0x29, 0xdc, 0xac, 0x9f, 0x87, 0xe3,
0xbe, 0xee, 0x55, 0x01, 0xc8, 0xba, 0x2e, 0xd4, 0x3b, 0x59, 0x1e, 0x8c, 0x84, 0x7a, 0xe6, 0x64,
0x03, 0x1d, 0x26, 0xb5, 0xa1, 0x92, 0x39, 0xc8, 0x7f, 0x04, 0x70, 0xa1, 0xd2, 0x65, 0xc2, 0xdd,
0xca, 0x40, 0x1e, 0x87, 0x93, 0xc3, 0xd8, 0xd8, 0xda, 0x9e, 0x6d, 0x3b, 0x3b, 0x5d, 0x6c, 0x04,
0xe7, 0xf5, 0x7b, 0x8e, 0x67, 0x5a, 0xbc, 0xde, 0x0d, 0x32, 0x44, 0xde, 0x76, 0x79, 0xa0, 0x3c,
0xa1, 0x86, 0xdb, 0x92, 0x5b, 0x4f, 0x8f, 0x1d, 0x43, 0xd7, 0x18, 0x0e, 0x4a, 0xc5, 0x5b, 0xc6,
0x05, 0xce, 0xf5, 0xd6, 0x4b, 0xe6, 0x22, 0x7d, 0x34, 0xd4, 0xd3, 0x9c, 0x49, 0x8d, 0xcd, 0xe0,
0xb2, 0xee, 0x23, 0x1d, 0x5e, 0x9d, 0xaf, 0xf9, 0x4a, 0x7b, 0xbb, 0x8f, 0x83, 0xdd, 0x49, 0x5f,
0x42, 0xb8, 0xf7, 0x23, 0x0f, 0x56, 0x5c, 0x72, 0xc8, 0x3a, 0x4a, 0x08, 0xb5, 0x32, 0xd2, 0x06,
0x59, 0x09, 0x5c, 0x75, 0x1b, 0x1b, 0x75, 0x6e, 0xa3, 0xed, 0x1c, 0xce, 0xbd, 0xcc, 0x39, 0x6c,
0xbe, 0x9a, 0x73, 0x38, 0x5f, 0xe7, 0x1c, 0xf6, 0xfe, 0xc3, 0x03, 0x56, 0xbd, 0x5f, 0xf6, 0x40,
0xfa, 0xad, 0x91, 0x18, 0x2b, 0x3d, 0xf1, 0x0b, 0xaf, 0xc6, 0x23, 0xfa, 0x0c, 0xf5, 0x68, 0x64,
0x56, 0x5b, 0x11, 0xd8, 0x66, 0xcb, 0xb2, 0x5f, 0xd7, 0x55, 0x72, 0x57, 0x9b, 0x2f, 0x77, 0x57,
0xe7, 0x5f, 0xee, 0xae, 0x2e, 0x94, 0xdd, 0xd5, 0xde, 0x6f, 0xc1, 0xb2, 0x73, 0xeb, 0xff, 0x73,
0x3b, 0x2e, 0x9b, 0x3c, 0xf2, 0x82, 0x1d, 0x58, 0xef, 0xdf, 0x1a, 0xc0, 0xaa, 0x9c, 0xf7, 0x7f,
0xba, 0x06, 0xe2, 0x23, 0x47, 0x81, 0xcc, 0x29, 0x3e, 0x72, 0x54, 0xc7, 0xff, 0xa6, 0x52, 0x7c,
0x0b, 0xd6, 0x53, 0x31, 0x88, 0x4f, 0x28, 0x09, 0xe7, 0x86, 0x3a, 0xaa, 0x1d, 0x68, 0xf4, 0xb9,
0x4e, 0xfa, 0x92, 0x93, 0x33, 0xb1, 0x5e, 0x86, 0x92, 0xaf, 0xce, 0xb7, 0x60, 0x53, 0xa6, 0xb2,
0x6e, 0x4b, 0x52, 0x5a, 0xc9, 0x7e, 0xcf, 0x83, 0xf3, 0xa5, 0x8e, 0x22, 0xb1, 0x20, 0xf5, 0xa8,
0xab, 0x5c, 0x5d, 0x20, 0xae, 0x5f, 0x31, 0xb0, 0xb5, 0x7e, 0xf9, 0xde, 0x54, 0x3b, 0xf0, 0x7c,
0xa6, 0x51, 0x15, 0x5f, 0x9e, 0x7a, 0x5d, 0x17, 0xbf, 0x00, 0xe7, 0xd5, 0xcd, 0x96, 0x16, 0x7e,
0x04, 0x5b, 0xe5, 0x8e, 0x22, 0x52, 0xea, 0x2e, 0x59, 0x37, 0xd1, 0x24, 0x72, 0x74, 0xb6, 0xbb,
0xde, 0xda, 0x3e, 0xfe, 0x9b, 0xc0, 0xbe, 0x3e, 0x15, 0xe9, 0x8c, 0xd2, 0x1e, 0x26, 0x54, 0x71,
0xa1, 0xec, 0xd3, 0x2f, 0x24, 0xd3, 0xc3, 0xaf, 0x8a, 0x99, 0xce, 0x2b, 0x35, 0x8a, 0xbc, 0xd2,
0x67, 0x00, 0xd0, 0x15, 0xa1, 0x3c, 0x89, 0xce, 0xf4, 0xa1, 0x0f, 0x28, 0x09, 0xf2, 0x9b, 0xb0,
0xe1, 0xd0, 0x37, 0xa7, 0xbf, 0xa0, 0x46, 0x48, 0x47, 0xd9, 0xcd, 0xbe, 0xa8, 0x3e, 0xfe, 0xef,
0x1e, 0xcc, 0xed, 0xc5, 0x89, 0x1d, 0x62, 0xf3, 0xdc, 0x10, 0x9b, 0xd2, 0xb5, 0x7d, 0xa3, 0x4a,
0x1b, 0x4a, 0x53, 0xd8, 0x40, 0xb6, 0x0d, 0x2b, 0xc1, 0x24, 0x47, 0x57, 0xf1, 0x28, 0x4e, 0x4f,
0x83, 0x74, 0x28, 0xaf, 0xe4, 0x76, 0xa3, 0xeb, 0xf9, 0xa5, 0x1e, 0xb6, 0x09, 0x73, 0x46, 0x29,
0x11, 0x02, 0x36, 0xd1, 0xd8, 0xa0, 0x48, 0xe3, 0x4c, 0x79, 0xb9, 0xaa, 0x85, 0x37, 0xee, 0x8e,
0x97, 0xe6, 0x9d, 0xe4, 0xf0, 0xba, 0x2e, 0xd4, 0xfb, 0xa8, 0xa3, 0x08, 0x4d, 0x85, 0x27, 0x74,
0x9b, 0xff, 0x8b, 0x07, 0xf3, 0x74, 0x02, 0x28, 0x93, 0x92, 0x11, 0x29, 0x91, 0x49, 0x61, 0x51,
0x4f, 0xca, 0x64, 0x09, 0xcc, 0xb8, 0x93, 0xde, 0x6c, 0x98, 0x65, 0xdb, 0x29, 0xce, 0x2b, 0xd0,
0x92, 0x2d, 0x93, 0x13, 0x24, 0x94, 0x02, 0xc8, 0x2e, 0x43, 0xf3, 0x38, 0x4e, 0xf4, 0x8b, 0x0a,
0x3a, 0x2a, 0x16, 0x27, 0x3e, 0xc1, 0x8b, 0xf5, 0x20, 0x3d, 0xb9, 0x78, 0xa9, 0x93, 0xcb, 0x60,
0x7c, 0x95, 0x0c, 0x59, 0xfb, 0x30, 0x4a, 0x50, 0xbe, 0x0d, 0xab, 0x8f, 0xe2, 0xa1, 0xb0, 0xe2,
0x20, 0x67, 0x72, 0x1d, 0xff, 0x6d, 0x0f, 0x96, 0x34, 0x32, 0xbb, 0x06, 0x4d, 0x7c, 0x6a, 0x4b,
0xc6, 0xad, 0x89, 0x86, 0x23, 0x9e, 0x4f, 0x18, 0xa8, 0x22, 0xc9, 0x4b, 0x2e, 0x4c, 0x21, 0xed,
0x23, 0x17, 0x46, 0x86, 0x59, 0x6e, 0xe9, 0x31, 0x2e, 0x41, 0xf9, 0x5f, 0x78, 0xb0, 0xec, 0xcc,
0x81, 0x2e, 0xcd, 0x38, 0xc8, 0x72, 0x15, 0x61, 0x54, 0xd7, 0x63, 0x83, 0xec, 0xc8, 0x58, 0xc3,
0x8d, 0x8c, 0x99, 0x98, 0xcd, 0x9c, 0x1d, 0xb3, 0xb9, 0x01, 0xad, 0x22, 0x09, 0xdd, 0x74, 0x54,
0x1f, 0xce, 0xa8, 0xe3, 0xfc, 0x05, 0x12, 0xd2, 0x19, 0xc4, 0xe3, 0x38, 0x55, 0x39, 0x5a, 0xd9,
0xe0, 0x37, 0xa1, 0x6d, 0xe1, 0xe3, 0x32, 0x22, 0x91, 0x9f, 0xc6, 0xe9, 0x53, 0x1d, 0xa0, 0x53,
0x4d, 0x93, 0xce, 0x6a, 0x14, 0xe9, 0x2c, 0xfe, 0x97, 0x1e, 0x2c, 0x23, 0x0f, 0x86, 0xd1, 0x68,
0x3f, 0x1e, 0x87, 0x83, 0x19, 0xdd, 0xbd, 0x66, 0x37, 0x95, 0xbc, 0xd5, 0xbc, 0xe8, 0x82, 0x91,
0xb7, 0xb5, 0x47, 0xa3, 0x04, 0xd1, 0xb4, 0x51, 0x52, 0x91, 0xcf, 0x0f, 0x83, 0x4c, 0x31, 0xbf,
0x7a, 0x8b, 0x1c, 0x20, 0xca, 0x13, 0x02, 0xd2, 0x20, 0x17, 0xfd, 0x49, 0x38, 0x1e, 0x87, 0x12,
0x57, 0x9a, 0x08, 0x75, 0x5d, 0xfc, 0x87, 0x0d, 0x68, 0x2b, 0x4d, 0x79, 0x6f, 0x38, 0x92, 0xa1,
0x70, 0x65, 0x68, 0x19, 0x75, 0x61, 0x41, 0x74, 0xbf, 0x63, 0x9a, 0x59, 0x90, 0xf2, 0xb5, 0xce,
0x55, 0xaf, 0xf5, 0x12, 0xb4, 0x90, 0xbd, 0xde, 0x26, 0x1b, 0x50, 0xd6, 0x2c, 0x14, 0x00, 0xdd,
0xbb, 0x4b, 0xbd, 0xf3, 0x45, 0x2f, 0x01, 0x1c, 0xab, 0x6f, 0xa1, 0x64, 0xf5, 0xbd, 0x0b, 0x1d,
0x45, 0x86, 0xce, 0x9d, 0xb4, 0x43, 0xc1, 0xe0, 0xce, 0x9d, 0xf8, 0x0e, 0xa6, 0x1e, 0xb9, 0xab,
0x47, 0x2e, 0xbd, 0x6c, 0xa4, 0xc6, 0xa4, 0xcc, 0x90, 0x3c, 0x9b, 0x07, 0x69, 0x90, 0x1c, 0xeb,
0xd7, 0x67, 0x68, 0xd2, 0xdd, 0x04, 0x66, 0xdb, 0x30, 0x8f, 0xc3, 0xb4, 0xb6, 0xae, 0x17, 0x3a,
0x89, 0xc2, 0xae, 0xc1, 0xbc, 0x18, 0x8e, 0x84, 0xf6, 0x3c, 0x98, 0xeb, 0x03, 0xe2, 0x1d, 0xf9,
0x12, 0x01, 0x55, 0x00, 0x42, 0x4b, 0x2a, 0xc0, 0xd5, 0xf4, 0x0b, 0xd8, 0x7c, 0x7f, 0xc8, 0x37,
0x81, 0x3d, 0x92, 0x5c, 0x6b, 0x47, 0x4e, 0x7f, 0x6f, 0x0e, 0xda, 0x16, 0x18, 0xa5, 0x79, 0x84,
0x0b, 0xee, 0x0f, 0xc3, 0x60, 0x22, 0x72, 0x91, 0x2a, 0x4e, 0x2d, 0x41, 0x11, 0x2f, 0x38, 0x19,
0xf5, 0xe3, 0x69, 0xde, 0x1f, 0x8a, 0x51, 0x2a, 0xe4, 0x1b, 0x89, 0x8f, 0x81, 0x03, 0x45, 0xbc,
0x49, 0xf0, 0xcc, 0xc6, 0x93, 0xfc, 0x50, 0x82, 0xea, 0x38, 0xa8, 0x3c, 0xa3, 0x66, 0x11, 0x07,
0x95, 0x27, 0x52, 0xd6, 0x43, 0xf3, 0x35, 0x7a, 0xe8, 0x1d, 0xd8, 0x92, 0x1a, 0x47, 0xc9, 0x66,
0xbf, 0xc4, 0x26, 0x67, 0xf4, 0xb2, 0x6d, 0x58, 0xc3, 0x35, 0x6b, 0x06, 0xcf, 0xc2, 0x4f, 0x64,
0x64, 0xc2, 0xf3, 0x2b, 0x70, 0xc4, 0x45, 0x71, 0x74, 0x70, 0x65, 0xae, 0xa8, 0x02, 0x27, 0xdc,
0xe0, 0x99, 0x8b, 0xdb, 0x52, 0xb8, 0x25, 0x38, 0x5f, 0x86, 0xf6, 0x41, 0x1e, 0x27, 0xfa, 0x52,
0x56, 0xa0, 0x23, 0x9b, 0x2a, 0x33, 0xf8, 0x1a, 0x5c, 0x24, 0x2e, 0x7a, 0x1c, 0x27, 0xf1, 0x38,
0x1e, 0xcd, 0x0e, 0xa6, 0x87, 0xd9, 0x20, 0x0d, 0x13, 0xf4, 0x08, 0xf8, 0xdf, 0x7a, 0xb0, 0xe1,
0xf4, 0xaa, 0x50, 0xc6, 0x17, 0x24, 0x4b, 0x9b, 0x94, 0x8e, 0x64, 0xbc, 0x75, 0x4b, 0x1d, 0x4a,
0x44, 0x19, 0x44, 0x7a, 0xa2, 0xb2, 0x3c, 0xb7, 0x60, 0x55, 0xaf, 0x4c, 0x0f, 0x94, 0x5c, 0xd8,
0xad, 0x72, 0xa1, 0x1a, 0xbf, 0xa2, 0x06, 0x68, 0x12, 0xbf, 0x22, 0xed, 0x6a, 0x31, 0xa4, 0x3d,
0x6a, 0x9f, 0xb6, 0xa7, 0xc7, 0xdb, 0xc6, 0xbc, 0x5e, 0xc1, 0xc0, 0x00, 0x33, 0xfe, 0x07, 0x1e,
0x40, 0xb1, 0x3a, 0x64, 0x8c, 0x42, 0xa5, 0xcb, 0x62, 0x35, 0x4b, 0x7d, 0xbf, 0x01, 0x1d, 0x13,
0xcd, 0x2f, 0x5e, 0x89, 0xb6, 0x86, 0xa1, 0xc1, 0x75, 0x15, 0x56, 0x47, 0xe3, 0xf8, 0x90, 0x9e,
0x58, 0x4a, 0x35, 0x67, 0x2a, 0x3f, 0xba, 0x22, 0xc1, 0xf7, 0x15, 0xb4, 0x78, 0x52, 0x9a, 0xd6,
0x93, 0xc2, 0xbf, 0xd3, 0x30, 0x31, 0xe0, 0x62, 0xcf, 0x67, 0x4a, 0x19, 0xdb, 0xad, 0x28, 0xc7,
0x33, 0x42, 0xae, 0x14, 0xbd, 0xd9, 0x7f, 0xa9, 0x23, 0x7b, 0x13, 0x56, 0x52, 0xa9, 0x7d, 0xb4,
0x6a, 0x6a, 0xbe, 0x40, 0x35, 0x2d, 0xa7, 0xce, 0xbb, 0xf3, 0xff, 0x60, 0x2d, 0x18, 0x9e, 0x88,
0x34, 0x0f, 0xc9, 0xa3, 0xa1, 0x47, 0x5f, 0x2a, 0xd4, 0x55, 0x0b, 0x4e, 0x6f, 0xf1, 0x55, 0x58,
0x55, 0x39, 0x69, 0x83, 0xa9, 0x2a, 0x91, 0x0a, 0x30, 0x22, 0xf2, 0x1f, 0xe8, 0x70, 0xb3, 0x7b,
0x87, 0x67, 0x9f, 0x88, 0xbd, 0xbb, 0x46, 0x69, 0x77, 0x9f, 0x55, 0xa1, 0xdf, 0xa1, 0x76, 0x9b,
0x54, 0x10, 0x5e, 0x02, 0x55, 0xa8, 0xde, 0x3d, 0xd2, 0xe6, 0xab, 0x1c, 0x29, 0xff, 0xde, 0x1c,
0x2c, 0xbe, 0x1f, 0x9d, 0xc4, 0xe1, 0x80, 0x02, 0xb1, 0x13, 0x31, 0x89, 0x75, 0xb9, 0x07, 0xfe,
0xc6, 0x17, 0x9d, 0x52, 0x9f, 0x49, 0xae, 0x22, 0xa9, 0xba, 0x89, 0xaf, 0x5b, 0x5a, 0x94, 0x40,
0x49, 0x4e, 0xb1, 0x20, 0x68, 0xc7, 0xa6, 0x76, 0xfd, 0x97, 0x6a, 0x15, 0xf5, 0x32, 0xf3, 0x56,
0xbd, 0x0c, 0x85, 0xed, 0x65, 0x56, 0x97, 0x8e, 0x73, 0xc9, 0xd7, 0x4d, 0xb2, 0xbb, 0x53, 0x21,
0x9d, 0x7a, 0x7a, 0x27, 0x17, 0x95, 0xdd, 0x6d, 0x03, 0xf1, 0x2d, 0x95, 0x03, 0x24, 0x8e, 0xd4,
0x35, 0x36, 0x08, 0x6d, 0x8b, 0x72, 0x09, 0x59, 0x4b, 0x5e, 0x71, 0x09, 0x8c, 0x0a, 0x69, 0x28,
0x8c, 0xde, 0x90, 0x7b, 0x00, 0x59, 0xe2, 0x55, 0x86, 0x5b, 0xd6, 0xba, 0xcc, 0x4e, 0x6b, 0x6b,
0x1d, 0x6d, 0x90, 0x60, 0x3c, 0x3e, 0x0c, 0x06, 0x4f, 0xa9, 0xb0, 0x8f, 0x92, 0xd1, 0x2d, 0xdf,
0x05, 0xe2, 0xaa, 0xa9, 0x4e, 0x4d, 0x91, 0x58, 0x96, 0xc9, 0x64, 0x0b, 0xc4, 0xbf, 0x01, 0xec,
0xd6, 0x70, 0xa8, 0x6e, 0xc8, 0xf8, 0x34, 0xc5, 0xd9, 0x7a, 0xce, 0xd9, 0xd6, 0xec, 0xb1, 0x51,
0xbb, 0x47, 0x7e, 0x0f, 0xda, 0xfb, 0x56, 0x3d, 0x1e, 0x5d, 0xa6, 0xae, 0xc4, 0x53, 0x0c, 0x60,
0x41, 0xac, 0x09, 0x1b, 0xf6, 0x84, 0xfc, 0x17, 0x81, 0x3d, 0x0c, 0xb3, 0xdc, 0xac, 0x4f, 0x1e,
0xe0, 0x1b, 0xd0, 0x31, 0x1e, 0x60, 0x91, 0x7f, 0x6e, 0x2b, 0x18, 0xe5, 0x85, 0x6f, 0xc9, 0xc4,
0x75, 0x79, 0x63, 0xdb, 0xb0, 0x14, 0x4a, 0x90, 0xd6, 0xc3, 0x3a, 0x2d, 0xac, 0x31, 0x4d, 0x3f,
0x1a, 0x14, 0x0a, 0xe8, 0xa8, 0xf9, 0x1f, 0x7a, 0xb0, 0xa8, 0xb6, 0x86, 0xcf, 0xa1, 0x53, 0x89,
0x28, 0x37, 0xe6, 0xc0, 0xea, 0xeb, 0xb7, 0xaa, 0x5c, 0x37, 0x57, 0xc7, 0x75, 0x0c, 0x9a, 0x49,
0x90, 0x1f, 0x93, 0x05, 0xdd, 0xf2, 0xe9, 0x37, 0xfa, 0xad, 0xe8, 0xd5, 0x49, 0xee, 0x26, 0x8f,
0xae, 0xae, 0x64, 0x50, 0xea, 0x8c, 0x0a, 0x1c, 0x37, 0x45, 0xa9, 0x5e, 0x09, 0x37, 0x11, 0x5c,
0x95, 0x46, 0x2f, 0xc0, 0xc5, 0x79, 0x29, 0x12, 0xe5, 0xf3, 0x52, 0xa8, 0xbe, 0xe9, 0xe7, 0x3d,
0xe8, 0xde, 0x15, 0x63, 0x91, 0x8b, 0x5b, 0xe3, 0x71, 0x99, 0xfe, 0x6b, 0x70, 0xb1, 0xa6, 0x4f,
0xbd, 0xaa, 0xf7, 0x61, 0xfd, 0xae, 0x38, 0x9c, 0x8e, 0x1e, 0x8a, 0x93, 0x22, 0xcd, 0xc2, 0xa0,
0x99, 0x1d, 0xc7, 0xa7, 0xea, 0x6e, 0xe9, 0x37, 0x3a, 0xe8, 0x63, 0xc4, 0xe9, 0x67, 0x89, 0x18,
0xe8, 0xca, 0x25, 0x82, 0x1c, 0x24, 0x62, 0xc0, 0xdf, 0x01, 0x66, 0xd3, 0x51, 0x5b, 0x40, 0xc9,
0x9d, 0x1e, 0xf6, 0xb3, 0x59, 0x96, 0x8b, 0x89, 0x2e, 0xc9, 0xb2, 0x41, 0xfc, 0x2a, 0x74, 0xf6,
0x83, 0x99, 0x2f, 0x3e, 0x56, 0xc5, 0xa0, 0xe8, 0xbc, 0x05, 0x33, 0x64, 0x65, 0xe3, 0xbc, 0x51,
0x37, 0xff, 0x9b, 0x06, 0x2c, 0x48, 0x4c, 0xa4, 0x3a, 0x14, 0x59, 0x1e, 0x46, 0x32, 0xc5, 0xa0,
0xa8, 0x5a, 0xa0, 0x0a, 0x6f, 0x34, 0x6a, 0x78, 0x43, 0x99, 0x53, 0xba, 0x0a, 0x44, 0x31, 0x81,
0x03, 0xc3, 0x77, 0xb7, 0x48, 0xdd, 0x4a, 0xef, 0xa1, 0x00, 0x94, 0xbc, 0xf9, 0x42, 0x3f, 0xc8,
0xf5, 0x69, 0xa6, 0x55, 0xec, 0x60, 0x83, 0x6a, 0xb5, 0xd0, 0xa2, 0xe4, 0x9a, 0x8a, 0x16, 0xaa,
0x68, 0x9b, 0xa5, 0x57, 0xd0, 0x36, 0xd2, 0xc6, 0x72, 0xb4, 0x0d, 0x83, 0xb5, 0xfb, 0x42, 0xf8,
0x22, 0x89, 0x53, 0x5d, 0x51, 0xcb, 0xbf, 0xeb, 0xc1, 0x9a, 0x7a, 0x3d, 0x4c, 0x1f, 0x7b, 0xc3,
0x79, 0x6a, 0xbc, 0xba, 0xa8, 0xf3, 0x9b, 0xb0, 0x4c, 0xce, 0x96, 0x09, 0x41, 0xa8, 0x78, 0x89,
0x03, 0xc4, 0x35, 0xe9, 0x38, 0xea, 0x24, 0x1c, 0xab, 0x03, 0xb6, 0x41, 0x3a, 0x8a, 0x81, 0xce,
0x18, 0x1d, 0xaf, 0xe7, 0x9b, 0x36, 0xff, 0x6b, 0x0f, 0xd6, 0xad, 0x05, 0x2b, 0x8e, 0xba, 0x09,
0x3a, 0x4d, 0x2b, 0x23, 0x15, 0x52, 0x30, 0x2e, 0xb8, 0x2f, 0x61, 0x31, 0xcc, 0x41, 0xa6, 0x8b,
0x09, 0x66, 0xb4, 0xc0, 0x6c, 0x2a, 0x6b, 0xdb, 0x9a, 0xbe, 0x0d, 0x42, 0xa6, 0x38, 0x15, 0xe2,
0xa9, 0x41, 0x99, 0x23, 0x14, 0x07, 0x46, 0x59, 0xb8, 0x38, 0xca, 0x8f, 0x0d, 0x92, 0x2c, 0x3c,
0x71, 0x81, 0xfc, 0xef, 0x3d, 0xd8, 0x90, 0x16, 0x88, 0xb2, 0xef, 0x4c, 0x51, 0xdc, 0x82, 0x34,
0xb9, 0xa4, 0x74, 0xed, 0x9d, 0xf3, 0x55, 0x9b, 0x7d, 0xf1, 0x15, 0xad, 0x26, 0x93, 0x7d, 0x3d,
0xe3, 0x2e, 0xe6, 0xea, 0xee, 0xe2, 0x05, 0x27, 0x5d, 0xe7, 0x99, 0xcf, 0xd7, 0x7a, 0xe6, 0xb7,
0x17, 0x61, 0x3e, 0x1b, 0xc4, 0x89, 0xe0, 0x5b, 0xb0, 0xe9, 0x6e, 0x4e, 0xa9, 0x93, 0xef, 0x7b,
0xd0, 0xbd, 0x2f, 0xe3, 0x54, 0x61, 0x34, 0xda, 0x0b, 0xb3, 0x3c, 0x4e, 0x4d, 0x15, 0xf0, 0x65,
0x80, 0x2c, 0x0f, 0xd2, 0x5c, 0x56, 0xc7, 0x28, 0x9f, 0xba, 0x80, 0xe0, 0x1a, 0x45, 0x34, 0x94,
0xbd, 0xf2, 0x6e, 0x4c, 0x1b, 0x2f, 0x86, 0x32, 0xc3, 0xfd, 0xf8, 0xe8, 0x28, 0x13, 0xc6, 0x46,
0xb2, 0x61, 0xe8, 0x66, 0xa1, 0xf4, 0xa2, 0x63, 0x21, 0x4e, 0x48, 0x6d, 0x4a, 0x1f, 0xaa, 0x04,
0xe5, 0x7f, 0xe5, 0xc1, 0x6a, 0xb1, 0xc8, 0x7b, 0x08, 0x74, 0x25, 0x5d, 0x2e, 0xcd, 0x92, 0x74,
0xed, 0xed, 0x87, 0xc3, 0x7e, 0x18, 0xa9, 0xb5, 0x59, 0x10, 0x92, 0x3e, 0xd5, 0x8a, 0xa7, 0xba,
0x12, 0xc9, 0x06, 0xc9, 0x34, 0x63, 0x8e, 0xa3, 0x65, 0x19, 0x92, 0x6a, 0x51, 0x71, 0xd3, 0x24,
0xa7, 0x51, 0x0b, 0x32, 0x26, 0xa9, 0x9a, 0xfa, 0xad, 0x59, 0x24, 0x28, 0xfe, 0xe4, 0x7f, 0xe8,
0xc1, 0xc5, 0x9a, 0xc3, 0x55, 0x92, 0x71, 0x17, 0xd6, 0x8f, 0x4c, 0xa7, 0x3e, 0x00, 0x29, 0x1e,
0x5b, 0x8a, 0x8b, 0x4a, 0x9b, 0xf6, 0xab, 0x03, 0xd8, 0x5b, 0xb0, 0x4e, 0x41, 0x0a, 0x79, 0xa4,
0x4e, 0x86, 0xbe, 0xda, 0xb1, 0xfb, 0x83, 0x06, 0xac, 0xc8, 0xb8, 0xb8, 0xfc, 0x0e, 0x44, 0xa4,
0xec, 0x03, 0x58, 0x54, 0x5f, 0xdd, 0xb0, 0xf3, 0x6a, 0x5a, 0xf7, 0x3b, 0x9f, 0xde, 0x56, 0x19,
0xac, 0x78, 0x67, 0xe3, 0x77, 0x7f, 0xfc, 0x4f, 0x7f, 0xd4, 0x58, 0x66, 0xed, 0x9d, 0x93, 0xb7,
0x77, 0x46, 0x22, 0xca, 0x90, 0xc6, 0xaf, 0x03, 0x14, 0x1f, 0xae, 0xb0, 0xae, 0x31, 0x18, 0x4a,
0x1f, 0xda, 0xf4, 0x2e, 0xd6, 0xf4, 0x28, 0xba, 0x17, 0x89, 0xee, 0x06, 0x5f, 0x41, 0xba, 0x61,
0x14, 0xe6, 0xf2, 0x2b, 0x96, 0xf7, 0xbc, 0x6d, 0x36, 0x84, 0x8e, 0xfd, 0x01, 0x0b, 0xd3, 0xfe,
0x59, 0xcd, 0x57, 0x31, 0xbd, 0xd7, 0x6a, 0xfb, 0xb4, 0x73, 0x4a, 0x73, 0x9c, 0xe7, 0x6b, 0x38,
0xc7, 0x94, 0x30, 0xcc, 0x2c, 0xbb, 0xff, 0xf0, 0x1a, 0xb4, 0x4c, 0x8c, 0x83, 0x7d, 0x1b, 0x96,
0x9d, 0x54, 0x02, 0xd3, 0x84, 0xeb, 0x32, 0x0f, 0xbd, 0x4b, 0xf5, 0x9d, 0x6a, 0xda, 0xcb, 0x34,
0x6d, 0x97, 0x6d, 0xe1, 0xb4, 0x2a, 0x16, 0xbf, 0x43, 0x09, 0x14, 0x59, 0xcc, 0xf4, 0x14, 0x56,
0xdc, 0xf0, 0x3f, 0xbb, 0xe4, 0x2a, 0x94, 0xd2, 0x6c, 0x9f, 0x39, 0xa3, 0x57, 0x4d, 0x77, 0x89,
0xa6, 0xdb, 0x62, 0x9b, 0xf6, 0x74, 0x26, 0xf6, 0x20, 0xa8, 0xfc, 0xcc, 0xfe, 0xb2, 0x85, 0x7d,
0xc6, 0x5c, 0x75, 0xdd, 0x17, 0x2f, 0xe6, 0xd2, 0xaa, 0x9f, 0xbd, 0xf0, 0x2e, 0x4d, 0xc5, 0x18,
0x1d, 0xa8, 0xfd, 0x61, 0x0b, 0xfb, 0x08, 0x5a, 0xa6, 0x9a, 0x9d, 0x5d, 0xb0, 0x3e, 0x21, 0xb0,
0x4b, 0xec, 0x7b, 0xdd, 0x6a, 0x47, 0xdd, 0x55, 0xd9, 0x94, 0x91, 0x21, 0x1e, 0xc2, 0x79, 0x65,
0x70, 0x1e, 0x8a, 0x9f, 0x66, 0x27, 0x35, 0xdf, 0xe3, 0xdc, 0xf0, 0xd8, 0x4d, 0x58, 0xd2, 0x1f,
0x09, 0xb0, 0xad, 0xfa, 0x8f, 0x1d, 0x7a, 0x17, 0x2a, 0x70, 0x25, 0xcf, 0xb7, 0x00, 0x8a, 0x02,
0x77, 0xc3, 0xf9, 0x95, 0xb2, 0x7b, 0x73, 0x88, 0x35, 0xd5, 0xf0, 0x23, 0x2a, 0xe7, 0x77, 0xeb,
0xe7, 0xd9, 0xeb, 0x05, 0x7e, 0x6d, 0x65, 0xfd, 0x0b, 0x08, 0xf2, 0x2d, 0x3a, 0xbb, 0x35, 0x46,
0xa2, 0x14, 0x89, 0x53, 0x5d, 0x88, 0x79, 0x17, 0xda, 0x56, 0xd1, 0x3c, 0xd3, 0x14, 0xaa, 0x05,
0xf7, 0xbd, 0x5e, 0x5d, 0x97, 0x5a, 0xee, 0x57, 0x60, 0xd9, 0xa9, 0x7e, 0x37, 0x92, 0x51, 0x57,
0x5b, 0x6f, 0x24, 0xa3, 0xbe, 0x60, 0xfe, 0x9b, 0xd0, 0xb6, 0x6a, 0xd5, 0x99, 0x55, 0x80, 0x52,
0xaa, 0x52, 0x37, 0x2b, 0xaa, 0x2b, 0x6d, 0xdf, 0xa4, 0xfd, 0xae, 0xf0, 0x16, 0xee, 0x97, 0xaa,
0x11, 0x91, 0x49, 0xbe, 0x0d, 0x2b, 0x6e, 0xf5, 0xba, 0x91, 0xaa, 0xda, 0x3a, 0x78, 0x23, 0x55,
0x67, 0x94, 0xbc, 0x2b, 0x86, 0xdc, 0xde, 0x30, 0x93, 0xec, 0x7c, 0xaa, 0x22, 0xfc, 0xcf, 0xd9,
0xd7, 0x51, 0x75, 0xa8, 0xf2, 0x50, 0x56, 0xd4, 0xec, 0xbb, 0x45, 0xa4, 0x86, 0xdb, 0x2b, 0x95,
0xa4, 0x7c, 0x9d, 0x88, 0xb7, 0x59, 0xb1, 0x03, 0xa9, 0xa1, 0xa9, 0x4c, 0xd4, 0xd2, 0xd0, 0x76,
0x25, 0xa9, 0xa5, 0xa1, 0x9d, 0x6a, 0xd2, 0xb2, 0x86, 0xce, 0x43, 0xa4, 0x11, 0xc1, 0x6a, 0x29,
0xe9, 0x6c, 0x84, 0xa5, 0xbe, 0x64, 0xa5, 0x77, 0xf9, 0xc5, 0xb9, 0x6a, 0x57, 0xcd, 0x68, 0xf5,
0xb2, 0xa3, 0x2b, 0x8c, 0x7e, 0x03, 0x3a, 0x76, 0xd5, 0xb1, 0xd1, 0xd9, 0x35, 0xb5, 0xd2, 0x46,
0x67, 0xd7, 0x95, 0x29, 0xeb, 0xcb, 0x65, 0x1d, 0x7b, 0x1a, 0xf6, 0x4d, 0x58, 0xb5, 0xca, 0x1b,
0x0e, 0x66, 0xd1, 0xc0, 0x30, 0x4f, 0xb5, 0x20, 0xad, 0x57, 0x67, 0x9f, 0xf1, 0x0b, 0x44, 0x78,
0x9d, 0x3b, 0x84, 0x91, 0x71, 0xee, 0x40, 0xdb, 0x2e, 0x9d, 0x78, 0x01, 0xdd, 0x0b, 0x56, 0x97,
0x5d, 0x9b, 0x75, 0xc3, 0x63, 0x7f, 0xe2, 0x41, 0xc7, 0x2e, 0x75, 0x64, 0x4e, 0x50, 0xb1, 0x44,
0xa7, 0x6b, 0xf7, 0xd9, 0x84, 0xb8, 0x4f, 0x8b, 0x7c, 0xb8, 0xfd, 0x15, 0xe7, 0x90, 0x3f, 0x75,
0xec, 0xfc, 0xeb, 0xe5, 0x0f, 0xca, 0x9e, 0x97, 0x11, 0xec, 0xa2, 0xbd, 0xe7, 0x37, 0x3c, 0xf6,
0x9e, 0xfc, 0xe8, 0x50, 0xfb, 0xe8, 0xcc, 0x52, 0x6e, 0xe5, 0x23, 0xb3, 0xbf, 0xcf, 0xbb, 0xe6,
0xdd, 0xf0, 0xd8, 0xb7, 0xe4, 0x77, 0x63, 0x6a, 0x2c, 0x9d, 0xfc, 0xab, 0x8e, 0xe7, 0x6f, 0xd2,
0x6e, 0x2e, 0xf3, 0x8b, 0xce, 0x6e, 0xca, 0xda, 0x7d, 0x1f, 0xa0, 0x08, 0xb8, 0xb0, 0x52, 0xf4,
0xc1, 0xe8, 0xbd, 0x6a, 0x4c, 0xc6, 0xbd, 0x51, 0x1d, 0xa4, 0x40, 0x8a, 0x1f, 0x49, 0x66, 0x54,
0xf8, 0x99, 0xb9, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7, 0xab, 0xeb, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d,
0x81, 0xe5, 0x87, 0x71, 0xfc, 0x74, 0x9a, 0x98, 0x20, 0x9e, 0xeb, 0xff, 0xef, 0x05, 0xd9, 0x71,
0xaf, 0xb4, 0x0b, 0x7e, 0x85, 0x48, 0xf5, 0x58, 0xd7, 0x22, 0xb5, 0xf3, 0x69, 0x11, 0xee, 0x79,
0xce, 0x02, 0x58, 0x37, 0x6f, 0x9c, 0x59, 0x78, 0xcf, 0x25, 0x63, 0x47, 0x5d, 0x2a, 0x53, 0x38,
0x56, 0x87, 0x5e, 0xed, 0x4e, 0xa6, 0x69, 0xde, 0xf0, 0xd8, 0x3e, 0x74, 0xee, 0x8a, 0x41, 0x3c,
0x14, 0xca, 0x63, 0xdf, 0x28, 0x16, 0x6e, 0x5c, 0xfd, 0xde, 0xb2, 0x03, 0x74, 0xa5, 0x3e, 0x09,
0x66, 0xa9, 0xf8, 0x78, 0xe7, 0x53, 0x15, 0x0b, 0x78, 0xae, 0xa5, 0x5e, 0xc7, 0x2f, 0x1c, 0xa9,
0x2f, 0x05, 0x3c, 0x1c, 0xa9, 0xaf, 0x04, 0x3c, 0x9c, 0xa3, 0xd6, 0xf1, 0x13, 0x36, 0x86, 0xf5,
0x4a, 0x8c, 0xc4, 0xbc, 0x94, 0x67, 0x45, 0x56, 0x7a, 0x57, 0xce, 0x46, 0x70, 0x67, 0xdb, 0x76,
0x67, 0x3b, 0x80, 0xe5, 0xbb, 0x42, 0x1e, 0x96, 0x4c, 0x8c, 0xf5, 0x5c, 0x35, 0x62, 0x27, 0xd1,
0xca, 0x2a, 0x86, 0xfa, 0x5c, 0xb5, 0x4e, 0x59, 0x29, 0xf6, 0x11, 0xb4, 0x1f, 0x88, 0x5c, 0x67,
0xc2, 0x8c, 0xbd, 0x51, 0x4a, 0x8d, 0xf5, 0x6a, 0x12, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x1d, 0x31,
0x1c, 0x09, 0x29, 0xec, 0xfd, 0x70, 0xf8, 0x9c, 0xfd, 0x2a, 0x11, 0x37, 0xc9, 0xf3, 0x2d, 0x2b,
0x81, 0x62, 0x13, 0x5f, 0x2d, 0xc1, 0xeb, 0x28, 0x47, 0xf1, 0x50, 0x58, 0x0f, 0x5c, 0x04, 0x6d,
0xab, 0xb2, 0xc3, 0x08, 0x50, 0xb5, 0x9a, 0xc4, 0x08, 0x50, 0x4d, 0x21, 0x08, 0xbf, 0x46, 0xf3,
0x70, 0x76, 0xa5, 0x98, 0x47, 0x16, 0x7f, 0x14, 0x33, 0xed, 0x7c, 0x1a, 0x4c, 0xf2, 0xe7, 0xec,
0x43, 0xfa, 0x6e, 0xc2, 0xce, 0xf6, 0x15, 0xf6, 0x4e, 0x39, 0x31, 0x68, 0x0e, 0xcb, 0xea, 0x72,
0x6d, 0x20, 0x39, 0x15, 0xbd, 0x83, 0x5f, 0x04, 0x38, 0xc8, 0xe3, 0xe4, 0x6e, 0x20, 0x26, 0x71,
0x54, 0x68, 0xae, 0x22, 0xa3, 0x55, 0x68, 0x2e, 0x2b, 0xad, 0xc5, 0x3e, 0xb4, 0x2c, 0x4e, 0x27,
0x59, 0xaa, 0x99, 0xeb, 0xcc, 0xa4, 0x97, 0x39, 0x90, 0x9a, 0xc4, 0xd7, 0x0d, 0x0f, 0xed, 0xc7,
0x22, 0x22, 0x67, 0xec, 0xc7, 0x4a, 0xb0, 0xcf, 0xa8, 0xbd, 0x9a, 0xf0, 0xdd, 0x3e, 0xb4, 0x8a,
0xb0, 0x90, 0x7e, 0x92, 0xca, 0x41, 0x24, 0xf3, 0xc6, 0x54, 0x82, 0x35, 0x7c, 0x8d, 0x8e, 0x0a,
0xd8, 0x12, 0x1e, 0x15, 0x45, 0x60, 0x42, 0xd8, 0x90, 0x0b, 0x34, 0x0f, 0x26, 0xe5, 0x68, 0xf4,
0x4e, 0x6a, 0x02, 0x26, 0x46, 0x9a, 0x6b, 0xe3, 0x0d, 0x8e, 0x6f, 0x87, 0xdc, 0x2a, 0xf3, 0x43,
0xa8, 0x9a, 0x27, 0xb0, 0x5e, 0x71, 0x96, 0x8d, 0x48, 0x9f, 0x15, 0xa3, 0x30, 0x22, 0x7d, 0xa6,
0x9f, 0xcd, 0xcf, 0xd3, 0x94, 0xab, 0x1c, 0x70, 0xca, 0xec, 0x34, 0xcc, 0x07, 0xc7, 0xef, 0x79,
0xdb, 0x87, 0x0b, 0xf4, 0xb7, 0x16, 0x9f, 0xff, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbc, 0xb9,
0x7b, 0x12, 0x08, 0x43, 0x00, 0x00,
// 5566 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xcd, 0x93, 0x1c, 0xc9,
0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x9e, 0x9e, 0x99, 0x9c, 0xd1, 0xa8, 0xd5, 0xd2, 0x6a,
0xb5, 0xe5, 0x8d, 0x95, 0x18, 0x16, 0x8d, 0x76, 0x6c, 0x2f, 0xeb, 0x15, 0xac, 0xd1, 0xf7, 0xac,
0xad, 0x95, 0xc7, 0x35, 0x92, 0x17, 0xbc, 0x40, 0xbb, 0xa6, 0x3b, 0xa7, 0xa7, 0xac, 0xea, 0xaa,
0xda, 0xaa, 0xea, 0x19, 0xf5, 0x2e, 0x8a, 0xe0, 0x23, 0x82, 0x13, 0x0e, 0x0e, 0x70, 0x31, 0x04,
0x41, 0x84, 0x7d, 0x81, 0x03, 0x47, 0x4e, 0xe6, 0x2f, 0x70, 0x04, 0xc1, 0x61, 0x4f, 0x0e, 0x6e,
0x7c, 0x1c, 0xc0, 0xc1, 0x85, 0x08, 0x2e, 0x1c, 0x08, 0xe2, 0xbd, 0xfc, 0xa8, 0xcc, 0xaa, 0x1a,
0x49, 0xb6, 0xc1, 0xb7, 0xce, 0x5f, 0xbe, 0x7a, 0xf9, 0xf5, 0xde, 0xcb, 0xf7, 0x5e, 0x66, 0x36,
0xb4, 0xd2, 0x64, 0x78, 0x2d, 0x49, 0xe3, 0x3c, 0x66, 0xf3, 0x61, 0x94, 0x26, 0xc3, 0xfe, 0xc5,
0x71, 0x1c, 0x8f, 0x43, 0xbe, 0xed, 0x27, 0xc1, 0xb6, 0x1f, 0x45, 0x71, 0xee, 0xe7, 0x41, 0x1c,
0x65, 0x82, 0xc8, 0xfd, 0x16, 0x74, 0xef, 0xf3, 0x68, 0x9f, 0xf3, 0x91, 0xc7, 0x3f, 0x9e, 0xf2,
0x2c, 0x67, 0xbf, 0x08, 0x6b, 0x3e, 0xff, 0x84, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, 0x4a,
0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0x5e, 0x83,
0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, 0x80,
0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, 0x47,
0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, 0x5b,
0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, 0xe8,
0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0xcf, 0x1d, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, 0x0f,
0xfd, 0x30, 0xe4, 0xb9, 0x1a, 0xd3, 0x15, 0x58, 0x39, 0x21, 0x80, 0xc6, 0x74, 0x12, 0xa7, 0x23,
0x39, 0xa2, 0xae, 0x80, 0xf7, 0x24, 0x7a, 0x6a, 0xcf, 0x1a, 0xa7, 0xf6, 0xac, 0x76, 0xba, 0xe6,
0xea, 0xa7, 0xcb, 0xdd, 0x00, 0x66, 0x76, 0x4e, 0x4c, 0x87, 0xfb, 0x1e, 0xac, 0x3f, 0x8e, 0xc2,
0x78, 0xf8, 0xe4, 0xa7, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, 0xd0,
0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0xa7, 0x83, 0x23, 0x3f,
0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, 0xab,
0x73, 0x9e, 0x2c, 0xb1, 0x37, 0x61, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, 0x89,
0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x26,
0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x85,
0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0xaa,
0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x1b, 0xd0, 0x1d,
0xf1, 0x2c, 0x1f, 0xf8, 0xa3, 0x51, 0xca, 0xb3, 0x8c, 0x67, 0xbd, 0x25, 0x5a, 0xba, 0x12, 0xea,
0xf6, 0x60, 0xf3, 0x3e, 0xcf, 0x8d, 0xd9, 0xc9, 0xe4, 0xb4, 0xbb, 0x0f, 0x80, 0x19, 0xf0, 0x1d,
0x9e, 0xfb, 0x41, 0x98, 0xb1, 0xb7, 0xa1, 0x93, 0x1b, 0xc4, 0x24, 0xaa, 0xed, 0x1d, 0x76, 0x8d,
0x74, 0xec, 0x9a, 0xf1, 0x81, 0x67, 0xd1, 0xb9, 0xff, 0xed, 0x40, 0x7b, 0x9f, 0x47, 0x5a, 0xbb,
0x18, 0x34, 0xb1, 0x27, 0x72, 0x25, 0xe9, 0x37, 0x7b, 0x15, 0xda, 0xd4, 0xbb, 0x2c, 0x4f, 0x83,
0x68, 0x4c, 0x4b, 0xd0, 0xf2, 0x00, 0xa1, 0x7d, 0x42, 0xd8, 0x2a, 0xcc, 0xf9, 0x93, 0x9c, 0x26,
0x7e, 0xce, 0xc3, 0x9f, 0xa8, 0x77, 0x89, 0x3f, 0x9b, 0xf0, 0x28, 0x2f, 0x26, 0xbb, 0xe3, 0xb5,
0x25, 0xb6, 0x8b, 0xb3, 0x7d, 0x0d, 0xd6, 0x4d, 0x12, 0xc5, 0x7d, 0x9e, 0xb8, 0xaf, 0x19, 0x94,
0xb2, 0x91, 0x2b, 0xb0, 0xa2, 0xe8, 0x53, 0xd1, 0x59, 0x9a, 0xfe, 0x96, 0xd7, 0x95, 0xb0, 0x1a,
0xc2, 0x55, 0x58, 0x3d, 0x0c, 0x22, 0x3f, 0x1c, 0x0c, 0xc3, 0xfc, 0x78, 0x30, 0xe2, 0x61, 0xee,
0xd3, 0x42, 0xcc, 0x7b, 0x5d, 0xc2, 0x6f, 0x87, 0xf9, 0xf1, 0x1d, 0x44, 0xdd, 0x3f, 0x75, 0xa0,
0x23, 0x06, 0x2f, 0x15, 0xff, 0x75, 0x58, 0x56, 0x6d, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36,
0xc8, 0xb6, 0x60, 0x55, 0x01, 0x49, 0xca, 0x83, 0x89, 0x3f, 0xe6, 0x52, 0xdb, 0x2b, 0x38, 0xdb,
0x29, 0x38, 0xa6, 0xf1, 0x34, 0x17, 0xaa, 0xd7, 0xde, 0xe9, 0xc8, 0x85, 0xf1, 0x10, 0xf3, 0x6c,
0x12, 0xf7, 0x7b, 0x0e, 0x74, 0x6e, 0x1f, 0xf9, 0x51, 0xc4, 0xc3, 0xbd, 0x38, 0x88, 0x72, 0x76,
0x1d, 0xd8, 0xe1, 0x34, 0x1a, 0x05, 0xd1, 0x78, 0x90, 0x3f, 0x0d, 0x46, 0x83, 0x83, 0x59, 0xce,
0x33, 0xb1, 0x44, 0xbb, 0x67, 0xbc, 0x9a, 0x3a, 0xf6, 0x26, 0xac, 0x5a, 0x68, 0x96, 0xa7, 0x62,
0xdd, 0x76, 0xcf, 0x78, 0x95, 0x1a, 0x14, 0xfc, 0x78, 0x9a, 0x27, 0xd3, 0x7c, 0x10, 0x44, 0x23,
0xfe, 0x94, 0xfa, 0xb8, 0xec, 0x59, 0xd8, 0xad, 0x2e, 0x74, 0xcc, 0xef, 0xdc, 0xf7, 0x60, 0xf5,
0x01, 0x6a, 0x44, 0x14, 0x44, 0xe3, 0x9b, 0x42, 0x6c, 0x51, 0x4d, 0x93, 0xe9, 0xc1, 0x13, 0x3e,
0x93, 0xf3, 0x26, 0x4b, 0x28, 0x54, 0x47, 0x71, 0x96, 0x4b, 0xc9, 0xa1, 0xdf, 0xee, 0x3f, 0x3b,
0xb0, 0x82, 0x73, 0xff, 0x81, 0x1f, 0xcd, 0xd4, 0xca, 0x3d, 0x80, 0x0e, 0xb2, 0x7a, 0x14, 0xdf,
0x14, 0xca, 0x2e, 0x84, 0xf8, 0xaa, 0x9c, 0xab, 0x12, 0xf5, 0x35, 0x93, 0x14, 0x8d, 0xf9, 0xcc,
0xb3, 0xbe, 0x46, 0xb1, 0xcd, 0xfd, 0x74, 0xcc, 0x73, 0x32, 0x03, 0xd2, 0x2c, 0x80, 0x80, 0x6e,
0xc7, 0xd1, 0x21, 0xbb, 0x0c, 0x9d, 0xcc, 0xcf, 0x07, 0x09, 0x4f, 0x69, 0xd6, 0x48, 0xf4, 0xe6,
0x3c, 0xc8, 0xfc, 0x7c, 0x8f, 0xa7, 0xb7, 0x66, 0x39, 0xef, 0x7f, 0x19, 0xd6, 0x2a, 0xad, 0xa0,
0xb4, 0x17, 0x43, 0xc4, 0x9f, 0x6c, 0x03, 0xe6, 0x8f, 0xfd, 0x70, 0xca, 0xa5, 0x75, 0x12, 0x85,
0x77, 0x1b, 0xef, 0x38, 0xee, 0x1b, 0xb0, 0x5a, 0x74, 0x5b, 0x0a, 0x19, 0x83, 0x26, 0xce, 0xa0,
0x64, 0x40, 0xbf, 0xdd, 0xdf, 0x73, 0x04, 0xe1, 0xed, 0x38, 0xd0, 0x9a, 0x8e, 0x84, 0x68, 0x10,
0x14, 0x21, 0xfe, 0x3e, 0xd5, 0x12, 0xfe, 0xec, 0x83, 0x75, 0xaf, 0xc0, 0x9a, 0xd1, 0x85, 0xe7,
0x74, 0xf6, 0x3b, 0x0e, 0xac, 0x3d, 0xe4, 0x27, 0x72, 0xd5, 0x55, 0x6f, 0xdf, 0x81, 0x66, 0x3e,
0x4b, 0xc4, 0x56, 0xdc, 0xdd, 0x79, 0x5d, 0x2e, 0x5a, 0x85, 0xee, 0x9a, 0x2c, 0x3e, 0x9a, 0x25,
0xdc, 0xa3, 0x2f, 0xdc, 0xf7, 0xa0, 0x6d, 0x80, 0xec, 0x1c, 0xac, 0x7f, 0xf8, 0xfe, 0xa3, 0x87,
0x77, 0xf7, 0xf7, 0x07, 0x7b, 0x8f, 0x6f, 0x7d, 0xf5, 0xee, 0x6f, 0x0c, 0x76, 0x6f, 0xee, 0xef,
0xae, 0x9e, 0x61, 0x9b, 0xc0, 0x1e, 0xde, 0xdd, 0x7f, 0x74, 0xf7, 0x8e, 0x85, 0x3b, 0x6e, 0x1f,
0x7a, 0x0f, 0xf9, 0xc9, 0x87, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, 0x5b, 0x73, 0xaf, 0x01, 0x33, 0xbb,
0x20, 0x47, 0xd5, 0x83, 0x45, 0x69, 0x6a, 0xd5, 0x4e, 0x23, 0x8b, 0xee, 0x1b, 0xc0, 0xf6, 0x83,
0x71, 0xf4, 0x01, 0xcf, 0x32, 0x7f, 0xcc, 0xd5, 0xd8, 0x56, 0x61, 0x6e, 0x92, 0x8d, 0xa5, 0x51,
0xc4, 0x9f, 0xee, 0xe7, 0x61, 0xdd, 0xa2, 0x93, 0x8c, 0x2f, 0x42, 0x2b, 0x0b, 0xc6, 0x91, 0x9f,
0x4f, 0x53, 0x2e, 0x59, 0x17, 0x80, 0x7b, 0x0f, 0x36, 0xbe, 0xc1, 0xd3, 0xe0, 0x70, 0xf6, 0x22,
0xf6, 0x36, 0x9f, 0x46, 0x99, 0xcf, 0x5d, 0x38, 0x5b, 0xe2, 0x23, 0x9b, 0x17, 0x82, 0x28, 0x97,
0x6b, 0xc9, 0x13, 0x05, 0x43, 0x2d, 0x1b, 0xa6, 0x5a, 0xba, 0x8f, 0x81, 0xdd, 0x8e, 0xa3, 0x88,
0x0f, 0xf3, 0x3d, 0xce, 0xd3, 0xc2, 0xbf, 0x2a, 0xa4, 0xae, 0xbd, 0x73, 0x4e, 0xae, 0x63, 0x59,
0xd7, 0xa5, 0x38, 0x32, 0x68, 0x26, 0x3c, 0x9d, 0x10, 0xe3, 0x25, 0x8f, 0x7e, 0xbb, 0x67, 0x61,
0xdd, 0x62, 0x2b, 0x77, 0xfb, 0xb7, 0xe0, 0xec, 0x9d, 0x20, 0x1b, 0x56, 0x1b, 0xec, 0xc1, 0x62,
0x32, 0x3d, 0x18, 0x14, 0x3a, 0xa5, 0x8a, 0xb8, 0x09, 0x96, 0x3f, 0x91, 0xcc, 0xfe, 0xd0, 0x81,
0xe6, 0xee, 0xa3, 0x07, 0xb7, 0x59, 0x1f, 0x96, 0x82, 0x68, 0x18, 0x4f, 0x70, 0xeb, 0x10, 0x83,
0xd6, 0xe5, 0x53, 0x75, 0xe5, 0x22, 0xb4, 0x68, 0xc7, 0xc1, 0x7d, 0x5d, 0xba, 0x42, 0x05, 0x80,
0x3e, 0x05, 0x7f, 0x9a, 0x04, 0x29, 0x39, 0x0d, 0xca, 0x15, 0x68, 0x92, 0x45, 0xac, 0x56, 0xb8,
0xff, 0xd3, 0x84, 0x45, 0x69, 0xab, 0xa9, 0xbd, 0x61, 0x1e, 0x1c, 0x73, 0xd9, 0x13, 0x59, 0xc2,
0x5d, 0x25, 0xe5, 0x93, 0x38, 0xe7, 0x03, 0x6b, 0x19, 0x6c, 0x10, 0xa9, 0x86, 0x82, 0xd1, 0x20,
0x41, 0xab, 0x4f, 0x3d, 0x6b, 0x79, 0x36, 0x88, 0x93, 0x85, 0xc0, 0x20, 0x18, 0x51, 0x9f, 0x9a,
0x9e, 0x2a, 0xe2, 0x4c, 0x0c, 0xfd, 0xc4, 0x1f, 0x06, 0xf9, 0x4c, 0x2a, 0xb7, 0x2e, 0x23, 0xef,
0x30, 0x1e, 0xfa, 0xe1, 0xe0, 0xc0, 0x0f, 0xfd, 0x68, 0xc8, 0xa5, 0xe3, 0x62, 0x83, 0xe8, 0x9b,
0xc8, 0x2e, 0x29, 0x32, 0xe1, 0xbf, 0x94, 0x50, 0xf4, 0x71, 0x86, 0xf1, 0x64, 0x12, 0xe4, 0xe8,
0xd2, 0xf4, 0x96, 0x84, 0x21, 0x29, 0x10, 0x1a, 0x89, 0x28, 0x9d, 0x88, 0xd9, 0x6b, 0x89, 0xd6,
0x2c, 0x10, 0xb9, 0x1c, 0x72, 0x4e, 0x06, 0xe9, 0xc9, 0x49, 0x0f, 0x04, 0x97, 0x02, 0xc1, 0x75,
0x98, 0x46, 0x19, 0xcf, 0xf3, 0x90, 0x8f, 0x74, 0x87, 0xda, 0x44, 0x56, 0xad, 0x60, 0xd7, 0x61,
0x5d, 0x78, 0x59, 0x99, 0x9f, 0xc7, 0xd9, 0x51, 0x90, 0x0d, 0x32, 0x1e, 0xe5, 0xbd, 0x0e, 0xd1,
0xd7, 0x55, 0xb1, 0x77, 0xe0, 0x5c, 0x09, 0x4e, 0xf9, 0x90, 0x07, 0xc7, 0x7c, 0xd4, 0x5b, 0xa6,
0xaf, 0x4e, 0xab, 0x66, 0x97, 0xa1, 0x8d, 0xce, 0xe5, 0x34, 0x19, 0xf9, 0xb8, 0x0f, 0x77, 0x69,
0x1d, 0x4c, 0x88, 0xbd, 0x05, 0xcb, 0x09, 0x17, 0x9b, 0xe5, 0x51, 0x1e, 0x0e, 0xb3, 0xde, 0x0a,
0xed, 0x64, 0x6d, 0xa9, 0x4c, 0x28, 0xb9, 0x9e, 0x4d, 0x81, 0x42, 0x39, 0xcc, 0xc8, 0x5d, 0xf1,
0x67, 0xbd, 0x55, 0x12, 0xb7, 0x02, 0x20, 0x1d, 0x49, 0x83, 0x63, 0x3f, 0xe7, 0xbd, 0x35, 0x92,
0x2d, 0x55, 0x74, 0xff, 0xd2, 0x81, 0xf5, 0x07, 0x41, 0x96, 0x4b, 0x21, 0xd4, 0xe6, 0xf8, 0x55,
0x68, 0x0b, 0xf1, 0x1b, 0xc4, 0x51, 0x38, 0x93, 0x12, 0x09, 0x02, 0xfa, 0x5a, 0x14, 0xce, 0xd8,
0xe7, 0x60, 0x39, 0x88, 0x4c, 0x12, 0xa1, 0xc3, 0x1d, 0x05, 0x12, 0xd1, 0xab, 0xd0, 0x4e, 0xa6,
0x07, 0x61, 0x30, 0x14, 0x24, 0x73, 0x82, 0x8b, 0x80, 0x88, 0x00, 0x1d, 0x3d, 0xd1, 0x13, 0x41,
0xd1, 0x24, 0x8a, 0xb6, 0xc4, 0x90, 0xc4, 0xbd, 0x05, 0x1b, 0x76, 0x07, 0xa5, 0xb1, 0xda, 0x82,
0x25, 0x29, 0xdb, 0x59, 0xaf, 0x4d, 0xf3, 0xd3, 0x95, 0xf3, 0x23, 0x49, 0x3d, 0x5d, 0xef, 0xfe,
0xbb, 0x03, 0x4d, 0x34, 0x00, 0xa7, 0x1b, 0x0b, 0xd3, 0xa6, 0xcf, 0x59, 0x36, 0x9d, 0xfc, 0x7e,
0xf4, 0x8a, 0x84, 0x48, 0x08, 0xb5, 0x31, 0x90, 0xa2, 0x3e, 0xe5, 0xc3, 0x63, 0xd2, 0x1d, 0x5d,
0x8f, 0x08, 0x6a, 0x16, 0x6e, 0x9d, 0xf4, 0xb5, 0x50, 0x1c, 0x5d, 0x56, 0x75, 0xf4, 0xe5, 0x62,
0x51, 0x47, 0xdf, 0xf5, 0x60, 0x31, 0x88, 0x0e, 0xe2, 0x69, 0x34, 0x22, 0x25, 0x59, 0xf2, 0x54,
0x11, 0x17, 0x3b, 0x21, 0x4f, 0x2a, 0x98, 0x70, 0xa9, 0x1d, 0x05, 0xe0, 0x32, 0x74, 0xad, 0x32,
0x32, 0x78, 0x7a, 0x1f, 0x7b, 0x1b, 0xd6, 0x0c, 0x4c, 0xce, 0xe0, 0x6b, 0x30, 0x9f, 0x20, 0x20,
0x1d, 0x25, 0x25, 0x5e, 0x64, 0x29, 0x45, 0x8d, 0xbb, 0x8a, 0xf1, 0x73, 0xfe, 0x7e, 0x74, 0x18,
0x2b, 0x4e, 0x3f, 0x9a, 0xc3, 0x80, 0x57, 0x42, 0x92, 0xd1, 0x55, 0x58, 0x09, 0x46, 0x3c, 0xca,
0x83, 0x7c, 0x36, 0xb0, 0x3c, 0xb8, 0x32, 0x8c, 0x3b, 0x8c, 0x1f, 0x06, 0x7e, 0x26, 0x6d, 0x98,
0x28, 0xb0, 0x1d, 0xd8, 0x40, 0xf1, 0x57, 0x12, 0xad, 0x97, 0x55, 0x38, 0x92, 0xb5, 0x75, 0xa8,
0xb1, 0x88, 0x4b, 0x09, 0xd4, 0x9f, 0x08, 0x4b, 0x5b, 0x57, 0x85, 0xb3, 0x26, 0x38, 0xe1, 0x90,
0xe7, 0x85, 0x8a, 0x68, 0xa0, 0x12, 0xbd, 0x2d, 0x08, 0x27, 0xb6, 0x1c, 0xbd, 0x19, 0x11, 0xe0,
0x52, 0x25, 0x02, 0xbc, 0x0a, 0x2b, 0xd9, 0x2c, 0x1a, 0xf2, 0xd1, 0x20, 0x8f, 0xb1, 0xdd, 0x20,
0xa2, 0xd5, 0x59, 0xf2, 0xca, 0x30, 0xc5, 0xaa, 0x3c, 0xcb, 0x23, 0x9e, 0x93, 0xe9, 0x5a, 0xf2,
0x54, 0x11, 0x77, 0x01, 0x22, 0x11, 0x42, 0xdd, 0xf2, 0x64, 0x09, 0xb7, 0xca, 0x69, 0x1a, 0x64,
0xbd, 0x0e, 0xa1, 0xf4, 0x9b, 0x7d, 0x01, 0xce, 0x1e, 0x60, 0x64, 0x75, 0xc4, 0xfd, 0x11, 0x4f,
0x69, 0xf5, 0x45, 0x60, 0x29, 0x2c, 0x50, 0x7d, 0x25, 0xb6, 0x7d, 0xcc, 0xd3, 0x2c, 0x88, 0x23,
0xb2, 0x3d, 0x2d, 0x4f, 0x15, 0xdd, 0x4f, 0x68, 0x47, 0xd7, 0x21, 0xef, 0x63, 0x32, 0x47, 0xec,
0x02, 0xb4, 0xc4, 0x18, 0xb3, 0x23, 0x5f, 0x3a, 0x19, 0x4b, 0x04, 0xec, 0x1f, 0xf9, 0xa8, 0xc0,
0xd6, 0xb4, 0x35, 0xc8, 0x73, 0x6c, 0x13, 0xb6, 0x2b, 0x66, 0xed, 0x75, 0xe8, 0xaa, 0x60, 0x3a,
0x1b, 0x84, 0xfc, 0x30, 0x57, 0x01, 0x42, 0x34, 0x9d, 0x60, 0x73, 0xd9, 0x03, 0x7e, 0x98, 0xbb,
0x0f, 0x61, 0x4d, 0xea, 0xed, 0xd7, 0x12, 0xae, 0x9a, 0xfe, 0x52, 0x79, 0x53, 0x13, 0x5e, 0xc5,
0xba, 0xad, 0xe8, 0x14, 0xe5, 0x94, 0x76, 0x3a, 0xd7, 0x03, 0x26, 0xab, 0x6f, 0x87, 0x71, 0xc6,
0x25, 0x43, 0x17, 0x3a, 0xc3, 0x30, 0xce, 0x54, 0x18, 0x22, 0x87, 0x63, 0x61, 0x38, 0x3f, 0xd9,
0x74, 0x38, 0x44, 0x4b, 0x20, 0x6c, 0x9a, 0x2a, 0xba, 0x7f, 0xe5, 0xc0, 0x3a, 0x71, 0x53, 0x16,
0x46, 0xfb, 0xae, 0x2f, 0xdf, 0xcd, 0xce, 0xd0, 0x0c, 0xcd, 0x36, 0x60, 0xfe, 0x30, 0x4e, 0x87,
0x5c, 0xb6, 0x24, 0x0a, 0x3f, 0xb9, 0x37, 0xde, 0xac, 0x78, 0xe3, 0x3f, 0x72, 0x60, 0x8d, 0xba,
0xba, 0x9f, 0xfb, 0xf9, 0x34, 0x93, 0xc3, 0xff, 0x15, 0x58, 0xc6, 0xa1, 0x72, 0xa5, 0x4e, 0xb2,
0xa3, 0x1b, 0x5a, 0xf3, 0x09, 0x15, 0xc4, 0xbb, 0x67, 0x3c, 0x9b, 0x98, 0x7d, 0x19, 0x3a, 0x66,
0x46, 0x84, 0xfa, 0xdc, 0xde, 0x39, 0xaf, 0x46, 0x59, 0x91, 0x9c, 0xdd, 0x33, 0x9e, 0xf5, 0x01,
0xbb, 0x01, 0x40, 0xee, 0x06, 0xb1, 0x95, 0xa1, 0xec, 0x79, 0x7b, 0x92, 0x8c, 0xc5, 0xda, 0x3d,
0xe3, 0x19, 0xe4, 0xb7, 0x96, 0x60, 0x41, 0xec, 0x8f, 0xee, 0x7d, 0x58, 0xb6, 0x7a, 0x6a, 0x45,
0x19, 0x1d, 0x11, 0x65, 0x54, 0x82, 0xd2, 0x46, 0x35, 0x28, 0x75, 0xff, 0xb5, 0x01, 0x0c, 0xa5,
0xad, 0xb4, 0x9c, 0xb8, 0x41, 0xc7, 0x23, 0xcb, 0xdd, 0xea, 0x78, 0x26, 0xc4, 0xae, 0x01, 0x33,
0x8a, 0x2a, 0xf7, 0x20, 0xf6, 0x8d, 0x9a, 0x1a, 0x34, 0x70, 0xc2, 0x57, 0x52, 0x31, 0xb0, 0x74,
0x2c, 0xc5, 0xba, 0xd5, 0xd6, 0xe1, 0xd6, 0x90, 0x4c, 0xb3, 0x23, 0x74, 0x20, 0x94, 0x43, 0xa6,
0xca, 0x65, 0x01, 0x59, 0x78, 0xa1, 0x80, 0x2c, 0x96, 0x05, 0xc4, 0x74, 0x09, 0x96, 0x2c, 0x97,
0x00, 0xfd, 0xaf, 0x49, 0x10, 0x91, 0x5f, 0x31, 0x98, 0x60, 0xeb, 0xd2, 0xff, 0xb2, 0x40, 0xb6,
0x05, 0xab, 0xd2, 0xaf, 0x2b, 0xfc, 0x0e, 0xa0, 0x39, 0xae, 0xe0, 0xee, 0x67, 0x0e, 0xac, 0xe2,
0x3c, 0x5b, 0xb2, 0xf8, 0x2e, 0x90, 0x2a, 0xbc, 0xa4, 0x28, 0x5a, 0xb4, 0x3f, 0xbb, 0x24, 0xbe,
0x03, 0x2d, 0x62, 0x18, 0x27, 0x3c, 0x92, 0x82, 0xd8, 0xb3, 0x05, 0xb1, 0xb0, 0x42, 0xbb, 0x67,
0xbc, 0x82, 0xd8, 0x10, 0xc3, 0x7f, 0x70, 0xa0, 0x2d, 0xbb, 0xf9, 0x53, 0xc7, 0x12, 0x7d, 0x58,
0x42, 0x89, 0x34, 0x1c, 0x76, 0x5d, 0xc6, 0xdd, 0x64, 0x82, 0x01, 0x1b, 0x6e, 0x9f, 0x56, 0x1c,
0x51, 0x86, 0x71, 0x2f, 0x24, 0x83, 0x9b, 0x0d, 0xf2, 0x20, 0x1c, 0xa8, 0x5a, 0x99, 0x80, 0xac,
0xab, 0x42, 0xbb, 0x93, 0xe5, 0xfe, 0x98, 0xcb, 0x6d, 0x4e, 0x14, 0x30, 0x60, 0x92, 0x03, 0x2a,
0xb9, 0x83, 0xee, 0x0f, 0x01, 0xce, 0x55, 0xaa, 0x74, 0xba, 0x5b, 0x3a, 0xc8, 0x61, 0x30, 0x39,
0x88, 0xb5, 0xaf, 0xed, 0x98, 0xbe, 0xb3, 0x55, 0xc5, 0xc6, 0x70, 0x56, 0xed, 0xe7, 0x38, 0xa7,
0xc5, 0xee, 0xdd, 0x20, 0x47, 0xe4, 0x2d, 0x5b, 0x06, 0xca, 0x0d, 0x2a, 0xdc, 0xd4, 0xdc, 0x7a,
0x7e, 0xec, 0x08, 0x7a, 0xda, 0x71, 0x90, 0x26, 0xde, 0x70, 0x2e, 0xb0, 0xad, 0x37, 0x5f, 0xd0,
0x16, 0xd9, 0xa3, 0x91, 0x6a, 0xe6, 0x54, 0x6e, 0x6c, 0x06, 0x97, 0x54, 0x1d, 0xd9, 0xf0, 0x6a,
0x7b, 0xcd, 0x97, 0x1a, 0xdb, 0x3d, 0xfc, 0xd8, 0x6e, 0xf4, 0x05, 0x8c, 0xfb, 0x3f, 0x74, 0xa0,
0x6b, 0xb3, 0x43, 0xd1, 0x91, 0x4a, 0xa8, 0x8c, 0x91, 0x72, 0xc8, 0x4a, 0x70, 0x35, 0x6c, 0x6c,
0xd4, 0x85, 0x8d, 0x66, 0x70, 0x38, 0xf7, 0xa2, 0xe0, 0xb0, 0xf9, 0x72, 0xc1, 0xe1, 0x7c, 0x5d,
0x70, 0xd8, 0xff, 0x2f, 0x07, 0x58, 0x75, 0x7d, 0xd9, 0x7d, 0x11, 0xb7, 0x46, 0x3c, 0x94, 0x76,
0xe2, 0x97, 0x5e, 0x4e, 0x46, 0xd4, 0x1c, 0xaa, 0xaf, 0x51, 0x58, 0x4d, 0x43, 0x60, 0xba, 0x2d,
0xcb, 0x5e, 0x5d, 0x55, 0x29, 0x5c, 0x6d, 0xbe, 0x38, 0x5c, 0x9d, 0x7f, 0x71, 0xb8, 0xba, 0x50,
0x0e, 0x57, 0xfb, 0xbf, 0x03, 0xcb, 0xd6, 0xaa, 0xff, 0xdf, 0x8d, 0xb8, 0xec, 0xf2, 0x88, 0x05,
0xb6, 0xb0, 0xfe, 0x8f, 0x1b, 0xc0, 0xaa, 0x92, 0xf7, 0x73, 0xed, 0x03, 0xc9, 0x91, 0x65, 0x40,
0xe6, 0xa4, 0x1c, 0x59, 0xa6, 0xe3, 0xff, 0xd3, 0x28, 0xbe, 0x09, 0x6b, 0x29, 0x1f, 0xc6, 0xc7,
0x74, 0x08, 0x67, 0xa7, 0x3a, 0xaa, 0x15, 0xe8, 0xf4, 0xd9, 0x41, 0xfa, 0x92, 0x75, 0x66, 0x62,
0xec, 0x0c, 0xa5, 0x58, 0xdd, 0xdd, 0x84, 0x0d, 0x71, 0x94, 0x75, 0x4b, 0xb0, 0x52, 0x46, 0xf6,
0x2f, 0x1c, 0x38, 0x5b, 0xaa, 0x28, 0x0e, 0x16, 0x84, 0x1d, 0xb5, 0x8d, 0xab, 0x0d, 0x62, 0xff,
0xa5, 0x00, 0x1b, 0xfd, 0x17, 0xfb, 0x4d, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac,
0xd7, 0x55, 0xb9, 0xe7, 0xe0, 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x64,
0x4a, 0xed, 0x2e, 0xab, 0x22, 0xba, 0x44, 0x96, 0xcd, 0xb6, 0xfb, 0x5b, 0x5b, 0xe7, 0xfe, 0x36,
0xb0, 0xaf, 0x4f, 0x79, 0x3a, 0xa3, 0x63, 0x0f, 0x9d, 0xaa, 0x38, 0x57, 0x8e, 0xe9, 0x17, 0x92,
0xe9, 0xc1, 0x57, 0xf9, 0x4c, 0x9d, 0x2b, 0x35, 0x8a, 0x73, 0xa5, 0x57, 0x00, 0x30, 0x14, 0xa1,
0x73, 0x12, 0x75, 0xd2, 0x87, 0x31, 0xa0, 0x60, 0xe8, 0xde, 0x80, 0x75, 0x8b, 0xbf, 0x9e, 0xfd,
0x05, 0xf9, 0x85, 0x08, 0x94, 0xed, 0xd3, 0x17, 0x59, 0xe7, 0xfe, 0x87, 0x03, 0x73, 0xbb, 0x71,
0x62, 0xa6, 0xd8, 0x1c, 0x3b, 0xc5, 0x26, 0x6d, 0xed, 0x40, 0x9b, 0xd2, 0x86, 0xb4, 0x14, 0x26,
0xc8, 0xb6, 0xa0, 0xeb, 0x4f, 0x72, 0x0c, 0x15, 0x0f, 0xe3, 0xf4, 0xc4, 0x4f, 0x47, 0x62, 0x49,
0x6e, 0x35, 0x7a, 0x8e, 0x57, 0xaa, 0x61, 0x1b, 0x30, 0xa7, 0x8d, 0x12, 0x11, 0x60, 0x11, 0x9d,
0x0d, 0xca, 0x34, 0xce, 0x64, 0x94, 0x2b, 0x4b, 0xb8, 0xe2, 0xf6, 0xf7, 0xc2, 0xbd, 0x13, 0x12,
0x5e, 0x57, 0x85, 0x76, 0x1f, 0x6d, 0x14, 0x91, 0xc9, 0xf4, 0x84, 0x2a, 0xbb, 0xff, 0xe6, 0xc0,
0x3c, 0xcd, 0x00, 0xea, 0xa4, 0x10, 0x44, 0x3a, 0xc8, 0xa4, 0xb4, 0xa8, 0x23, 0x74, 0xb2, 0x04,
0x33, 0xd7, 0x3a, 0xde, 0x6c, 0xe8, 0x6e, 0x9b, 0x47, 0x9c, 0x97, 0xa1, 0x25, 0x4a, 0xfa, 0x4c,
0x90, 0x48, 0x0a, 0x90, 0x5d, 0x82, 0xe6, 0x51, 0x9c, 0xa8, 0x1d, 0x15, 0x54, 0x56, 0x2c, 0x4e,
0x3c, 0xc2, 0x8b, 0xfe, 0x20, 0x3f, 0xd1, 0x79, 0x61, 0x93, 0xcb, 0x30, 0xee, 0x4a, 0x9a, 0xad,
0x39, 0x19, 0x25, 0xd4, 0xdd, 0x82, 0x95, 0x87, 0xf1, 0x88, 0x1b, 0x79, 0x90, 0x53, 0xa5, 0xce,
0xfd, 0x5d, 0x07, 0x96, 0x14, 0x31, 0xbb, 0x0a, 0x4d, 0xdc, 0x6a, 0x4b, 0xce, 0xad, 0xce, 0x86,
0x23, 0x9d, 0x47, 0x14, 0x68, 0x22, 0x29, 0x4a, 0x2e, 0x5c, 0x21, 0x15, 0x23, 0x17, 0x4e, 0x86,
0xee, 0x6e, 0x69, 0x33, 0x2e, 0xa1, 0xee, 0x5f, 0x3b, 0xb0, 0x6c, 0xb5, 0x81, 0x21, 0x4d, 0xe8,
0x67, 0xb9, 0xcc, 0x30, 0xca, 0xe5, 0x31, 0x21, 0x33, 0x33, 0xd6, 0xb0, 0x33, 0x63, 0x3a, 0x67,
0x33, 0x67, 0xe6, 0x6c, 0xae, 0x43, 0xab, 0x38, 0x84, 0x6e, 0x5a, 0xa6, 0x0f, 0x5b, 0x54, 0x79,
0xfe, 0x82, 0x08, 0xf9, 0x0c, 0xe3, 0x30, 0x4e, 0xe5, 0x19, 0xad, 0x28, 0xb8, 0x37, 0xa0, 0x6d,
0xd0, 0x63, 0x37, 0x22, 0x9e, 0x9f, 0xc4, 0xe9, 0x13, 0x95, 0xa0, 0x93, 0x45, 0x7d, 0x9c, 0xd5,
0x28, 0x8e, 0xb3, 0xdc, 0xbf, 0x71, 0x60, 0x19, 0x65, 0x30, 0x88, 0xc6, 0x7b, 0x71, 0x18, 0x0c,
0x67, 0xb4, 0xf6, 0x4a, 0xdc, 0xe4, 0xe1, 0xad, 0x92, 0x45, 0x1b, 0x46, 0xd9, 0x56, 0x11, 0x8d,
0x54, 0x44, 0x5d, 0x46, 0x4d, 0x45, 0x39, 0x3f, 0xf0, 0x33, 0x29, 0xfc, 0x72, 0x2f, 0xb2, 0x40,
0xd4, 0x27, 0x04, 0x52, 0x3f, 0xe7, 0x83, 0x49, 0x10, 0x86, 0x81, 0xa0, 0x15, 0x2e, 0x42, 0x5d,
0x95, 0xfb, 0x83, 0x06, 0xb4, 0xa5, 0xa5, 0xbc, 0x3b, 0x1a, 0x8b, 0x54, 0xb8, 0x74, 0xb4, 0xb4,
0xb9, 0x30, 0x10, 0x55, 0x6f, 0xb9, 0x66, 0x06, 0x52, 0x5e, 0xd6, 0xb9, 0xea, 0xb2, 0x5e, 0x84,
0x16, 0x8a, 0xd7, 0x5b, 0xe4, 0x03, 0x8a, 0x3b, 0x0b, 0x05, 0xa0, 0x6a, 0x77, 0xa8, 0x76, 0xbe,
0xa8, 0x25, 0xc0, 0xf2, 0xfa, 0x16, 0x4a, 0x5e, 0xdf, 0x3b, 0xd0, 0x91, 0x6c, 0x68, 0xde, 0xc9,
0x3a, 0x14, 0x02, 0x6e, 0xad, 0x89, 0x67, 0x51, 0xaa, 0x2f, 0x77, 0xd4, 0x97, 0x4b, 0x2f, 0xfa,
0x52, 0x51, 0xd2, 0xc9, 0x90, 0x98, 0x9b, 0xfb, 0xa9, 0x9f, 0x1c, 0xa9, 0xdd, 0x67, 0xa4, 0x8f,
0xbb, 0x09, 0x66, 0x5b, 0x30, 0x8f, 0x9f, 0x29, 0x6b, 0x5d, 0xaf, 0x74, 0x82, 0x84, 0x5d, 0x85,
0x79, 0x3e, 0x1a, 0x73, 0x15, 0x79, 0x30, 0x3b, 0x06, 0xc4, 0x35, 0xf2, 0x04, 0x01, 0x9a, 0x00,
0x44, 0x4b, 0x26, 0xc0, 0xb6, 0xf4, 0x0b, 0x58, 0x7c, 0x7f, 0xe4, 0x6e, 0x00, 0x7b, 0x28, 0xa4,
0xd6, 0xcc, 0x9c, 0xfe, 0xc1, 0x1c, 0xb4, 0x0d, 0x18, 0xb5, 0x79, 0x8c, 0x1d, 0x1e, 0x8c, 0x02,
0x7f, 0xc2, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0x48, 0xe7, 0x1f, 0x8f, 0x07, 0xf1, 0x34, 0x1f,
0x8c, 0xf8, 0x38, 0xe5, 0x62, 0x8f, 0xc4, 0xcd, 0xc0, 0x42, 0x91, 0x6e, 0xe2, 0x3f, 0x35, 0xe9,
0x84, 0x3c, 0x94, 0x50, 0x95, 0x07, 0x15, 0x73, 0xd4, 0x2c, 0xf2, 0xa0, 0x62, 0x46, 0xca, 0x76,
0x68, 0xbe, 0xc6, 0x0e, 0xbd, 0x0d, 0x9b, 0xc2, 0xe2, 0x48, 0xdd, 0x1c, 0x94, 0xc4, 0xe4, 0x94,
0x5a, 0xb6, 0x05, 0xab, 0xd8, 0x67, 0x25, 0xe0, 0x59, 0xf0, 0x89, 0xc8, 0x4c, 0x38, 0x5e, 0x05,
0x47, 0x5a, 0x54, 0x47, 0x8b, 0x56, 0x9c, 0x15, 0x55, 0x70, 0xa2, 0xf5, 0x9f, 0xda, 0xb4, 0x2d,
0x49, 0x5b, 0xc2, 0xdd, 0x65, 0x68, 0xef, 0xe7, 0x71, 0xa2, 0x16, 0xa5, 0x0b, 0x1d, 0x51, 0x94,
0x27, 0x83, 0x17, 0xe0, 0x3c, 0x49, 0xd1, 0xa3, 0x38, 0x89, 0xc3, 0x78, 0x3c, 0xdb, 0x9f, 0x1e,
0x64, 0xc3, 0x34, 0x48, 0x30, 0x22, 0x70, 0xff, 0xde, 0x81, 0x75, 0xab, 0x56, 0xa6, 0x32, 0xbe,
0x20, 0x44, 0x5a, 0x1f, 0xe9, 0x08, 0xc1, 0x5b, 0x33, 0xcc, 0xa1, 0x20, 0x14, 0x49, 0xa4, 0xc7,
0xf2, 0x94, 0xe7, 0x26, 0xac, 0xa8, 0x9e, 0xa9, 0x0f, 0x85, 0x14, 0xf6, 0xaa, 0x52, 0x28, 0xbf,
0xef, 0xca, 0x0f, 0x14, 0x8b, 0x5f, 0x15, 0x7e, 0x35, 0x1f, 0xd1, 0x18, 0x55, 0x4c, 0xdb, 0x57,
0xdf, 0x9b, 0xce, 0xbc, 0xea, 0xc1, 0x50, 0x83, 0x99, 0xfb, 0x47, 0x0e, 0x40, 0xd1, 0x3b, 0x14,
0x8c, 0xc2, 0xa4, 0x8b, 0xcb, 0x6a, 0x86, 0xf9, 0x7e, 0x0d, 0x3a, 0x3a, 0x9b, 0x5f, 0xec, 0x12,
0x6d, 0x85, 0xa1, 0xc3, 0x75, 0x05, 0x56, 0xc6, 0x61, 0x7c, 0x40, 0x5b, 0x2c, 0x1d, 0x35, 0x67,
0xf2, 0x7c, 0xb4, 0x2b, 0xe0, 0x7b, 0x12, 0x2d, 0xb6, 0x94, 0xa6, 0xb1, 0xa5, 0xb8, 0xdf, 0x69,
0xe8, 0x1c, 0x70, 0x31, 0xe6, 0x53, 0xb5, 0x8c, 0xed, 0x54, 0x8c, 0xe3, 0x29, 0x29, 0x57, 0xca,
0xde, 0xec, 0xbd, 0x30, 0x90, 0xbd, 0x01, 0xdd, 0x54, 0x58, 0x1f, 0x65, 0x9a, 0x9a, 0xcf, 0x31,
0x4d, 0xcb, 0xa9, 0xb5, 0xef, 0xfc, 0x02, 0xac, 0xfa, 0xa3, 0x63, 0x9e, 0xe6, 0x01, 0x45, 0x34,
0xb4, 0xe9, 0x0b, 0x83, 0xba, 0x62, 0xe0, 0xb4, 0x17, 0x5f, 0x81, 0x15, 0x79, 0x26, 0xad, 0x29,
0xe5, 0x4d, 0xa4, 0x02, 0x46, 0x42, 0xf7, 0xfb, 0x2a, 0xdd, 0x6c, 0xaf, 0xe1, 0xe9, 0x33, 0x62,
0x8e, 0xae, 0x51, 0x1a, 0xdd, 0xe7, 0x64, 0xea, 0x77, 0xa4, 0xc2, 0x26, 0x99, 0x84, 0x17, 0xa0,
0x4c, 0xd5, 0xdb, 0x53, 0xda, 0x7c, 0x99, 0x29, 0x75, 0x3f, 0x73, 0x60, 0x71, 0x37, 0x4e, 0x76,
0xe5, 0xf1, 0x32, 0x29, 0x82, 0xbe, 0xf1, 0xa1, 0x8a, 0xa6, 0x57, 0xdc, 0xa8, 0x78, 0xc5, 0xd5,
0xbd, 0x76, 0xb9, 0xbc, 0xd7, 0xfe, 0x1a, 0x5c, 0xa0, 0x68, 0x39, 0x8d, 0x93, 0x38, 0x45, 0x65,
0xf4, 0x43, 0xb1, 0xb1, 0xc6, 0x51, 0x7e, 0xa4, 0xcc, 0xd8, 0xf3, 0x48, 0x28, 0x3a, 0x0a, 0xf3,
0xe3, 0x81, 0x70, 0x86, 0xa5, 0x6f, 0x20, 0xac, 0x5b, 0xb5, 0xc2, 0xfd, 0x12, 0xb4, 0xc8, 0xb9,
0xa5, 0x61, 0xbd, 0x09, 0xad, 0xa3, 0x38, 0x19, 0x1c, 0x05, 0x51, 0xae, 0x94, 0xbb, 0x5b, 0x78,
0x9d, 0xbb, 0x34, 0x21, 0x9a, 0xc0, 0xfd, 0xf1, 0x1c, 0x2c, 0xbe, 0x1f, 0x1d, 0xc7, 0xc1, 0x90,
0x32, 0xd3, 0x13, 0x3e, 0x89, 0xd5, 0xfd, 0x17, 0xfc, 0x8d, 0x53, 0x41, 0x67, 0xc1, 0x49, 0x2e,
0x53, 0xcb, 0xaa, 0x88, 0xdb, 0x7d, 0x5a, 0xdc, 0x09, 0x13, 0xaa, 0x63, 0x20, 0xe8, 0xd8, 0xa7,
0xe6, 0x85, 0x38, 0x59, 0x2a, 0x2e, 0x10, 0xcd, 0x1b, 0x17, 0x88, 0xe8, 0x1c, 0x43, 0x1c, 0x73,
0x93, 0x7c, 0x2d, 0x79, 0xaa, 0x48, 0x81, 0x48, 0xca, 0x45, 0x96, 0x83, 0x1c, 0x87, 0x45, 0x19,
0x88, 0x98, 0x20, 0x3a, 0x17, 0xe2, 0x03, 0x41, 0x23, 0x8c, 0xaf, 0x09, 0xa1, 0xb3, 0x55, 0xbe,
0x53, 0xd7, 0x12, 0x32, 0x5f, 0x82, 0xd1, 0x42, 0x8f, 0xb8, 0x36, 0xa4, 0x62, 0x0c, 0x20, 0xee,
0xbc, 0x95, 0x71, 0x23, 0x7c, 0x11, 0xc7, 0xf5, 0x2a, 0x7c, 0x41, 0x41, 0xf1, 0xc3, 0xf0, 0xc0,
0x1f, 0x3e, 0xa1, 0x9b, 0x8e, 0x74, 0x3a, 0xdf, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x9d,
0x84, 0x35, 0x3d, 0x13, 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5,
0x8c, 0xe9, 0x68, 0x45, 0x4d, 0x22, 0x33, 0x5b, 0xbe, 0x62, 0x1f, 0xa0, 0x7f, 0x03, 0xd8, 0xcd,
0xd1, 0x48, 0xae, 0xb7, 0x0e, 0x19, 0x8b, 0x95, 0x72, 0xac, 0x95, 0xaa, 0x99, 0xb1, 0x46, 0xed,
0x8c, 0xb9, 0x77, 0xa1, 0xbd, 0x67, 0x5c, 0x77, 0x24, 0xd1, 0x50, 0x17, 0x1d, 0xa5, 0x38, 0x19,
0x88, 0xd1, 0x60, 0xc3, 0x6c, 0xd0, 0xfd, 0x65, 0x60, 0x0f, 0x82, 0x2c, 0xd7, 0xfd, 0x13, 0xcb,
0xf1, 0x1a, 0x74, 0x74, 0x80, 0x5d, 0x1c, 0xef, 0xb7, 0x25, 0x46, 0xc7, 0xee, 0x37, 0xc5, 0xbd,
0x80, 0xf2, 0xc0, 0xb6, 0x60, 0x29, 0x10, 0x50, 0x59, 0x13, 0x14, 0xa5, 0xae, 0x47, 0x7f, 0x4d,
0x82, 0xd6, 0x2e, 0xfa, 0x03, 0x07, 0x16, 0xe5, 0xd0, 0xd0, 0xdb, 0xb0, 0x2e, 0x7a, 0x8a, 0x81,
0x59, 0x58, 0xfd, 0xf5, 0xb8, 0xaa, 0x0c, 0xcf, 0xd5, 0xc9, 0x30, 0x83, 0x66, 0xe2, 0xe7, 0x47,
0x14, 0xa0, 0xb4, 0x3c, 0xfa, 0xcd, 0x56, 0x45, 0xd0, 0x2c, 0x74, 0x85, 0x02, 0xe6, 0xba, 0x1b,
0x99, 0xc2, 0x24, 0x57, 0x70, 0x1c, 0x14, 0x9d, 0xa4, 0x0b, 0x5c, 0x27, 0xc8, 0xe5, 0x2d, 0x85,
0x02, 0x2e, 0xe6, 0x4b, 0xb2, 0x28, 0xcf, 0x97, 0x24, 0xf5, 0x74, 0xbd, 0xdb, 0x87, 0xde, 0x1d,
0x1e, 0xf2, 0x9c, 0xdf, 0x0c, 0xc3, 0x32, 0xff, 0x0b, 0x70, 0xbe, 0xa6, 0x4e, 0x3a, 0x2d, 0xf7,
0x60, 0xed, 0x0e, 0x3f, 0x98, 0x8e, 0x1f, 0xf0, 0xe3, 0xe2, 0x14, 0x8b, 0x41, 0x33, 0x3b, 0x8a,
0x4f, 0xe4, 0xda, 0xd2, 0x6f, 0xf6, 0x0a, 0x40, 0x88, 0x34, 0x83, 0x2c, 0xe1, 0x43, 0x75, 0x31,
0x8c, 0x90, 0xfd, 0x84, 0x0f, 0xdd, 0xb7, 0x81, 0x99, 0x7c, 0xe4, 0x10, 0xd0, 0x0e, 0x4c, 0x0f,
0x06, 0xd9, 0x2c, 0xcb, 0xf9, 0x44, 0xdd, 0x78, 0x33, 0x21, 0xf7, 0x0a, 0x74, 0xf6, 0xfc, 0x99,
0xc7, 0x3f, 0x96, 0x77, 0x6d, 0x31, 0x36, 0xf6, 0x67, 0x28, 0xca, 0x3a, 0x36, 0xa6, 0x6a, 0xf7,
0x3f, 0x1b, 0xb0, 0x20, 0x28, 0x91, 0xeb, 0x88, 0x67, 0x79, 0x10, 0x89, 0x13, 0x1c, 0xc9, 0xd5,
0x80, 0x2a, 0xb2, 0xd1, 0xa8, 0x91, 0x0d, 0xe9, 0xad, 0xaa, 0x4b, 0x36, 0x52, 0x08, 0x2c, 0x0c,
0xdd, 0x9a, 0xe2, 0x64, 0x5c, 0x04, 0x67, 0x05, 0x50, 0x4a, 0x96, 0x14, 0xd6, 0x46, 0xf4, 0x4f,
0x09, 0xad, 0x14, 0x07, 0x13, 0xaa, 0xb5, 0x69, 0x8b, 0x42, 0x6a, 0x2a, 0x36, 0xad, 0x62, 0xbb,
0x96, 0x5e, 0xc2, 0x76, 0x09, 0x17, 0xf6, 0x79, 0xb6, 0x0b, 0x5e, 0xc2, 0x76, 0xb9, 0x0c, 0x56,
0xef, 0x71, 0xee, 0x71, 0xdc, 0x15, 0x95, 0x38, 0x7d, 0xd7, 0x81, 0x55, 0xb9, 0xa1, 0xeb, 0x3a,
0xf6, 0x9a, 0xb5, 0xfb, 0x3b, 0x75, 0x07, 0x01, 0xaf, 0xc3, 0x32, 0xed, 0xc9, 0x3a, 0x2b, 0x24,
0x53, 0x58, 0x16, 0x88, 0xe3, 0x50, 0xa9, 0xed, 0x49, 0x10, 0xca, 0x45, 0x31, 0x21, 0x95, 0x58,
0xc2, 0xf8, 0x98, 0x96, 0xc4, 0xf1, 0x74, 0xd9, 0xfd, 0x3b, 0x07, 0xd6, 0x8c, 0x0e, 0x4b, 0x29,
0xbc, 0x01, 0xea, 0xe4, 0x5c, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b,
0x98, 0x16, 0xd3, 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2,
0xf9, 0x13, 0x4d, 0x32, 0x47, 0x24, 0x16, 0x46, 0x07, 0xa3, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0x17,
0xc8, 0x06, 0xdd, 0x7f, 0x74, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0xf7, 0x14, 0x17, 0x84,
0x17, 0x2c, 0x34, 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x8b, 0x2f, 0xe9, 0xc8, 0xea, 0x03, 0xf1,
0x53, 0xd6, 0x62, 0xae, 0x6e, 0x2d, 0x9e, 0x33, 0xd3, 0x75, 0xc9, 0x92, 0xf9, 0xda, 0x64, 0xc9,
0xad, 0x45, 0x98, 0xcf, 0x86, 0x71, 0xc2, 0xdd, 0x4d, 0xd8, 0xb0, 0x07, 0x27, 0x4d, 0xd0, 0xf7,
0x1c, 0xe8, 0xdd, 0x13, 0xa9, 0xc3, 0x20, 0x1a, 0xef, 0x06, 0x59, 0x1e, 0xa7, 0xfa, 0x62, 0xf6,
0x25, 0x80, 0x2c, 0xf7, 0xd3, 0x5c, 0x5c, 0x58, 0x92, 0x69, 0x8e, 0x02, 0xc1, 0x3e, 0xf2, 0x68,
0x24, 0x6a, 0xc5, 0xda, 0xe8, 0x32, 0x2e, 0x0c, 0x1d, 0xd6, 0x0f, 0xe2, 0xc3, 0xc3, 0x8c, 0x6b,
0xb7, 0xd5, 0xc4, 0x30, 0xf2, 0x45, 0x8d, 0xc7, 0x58, 0x8f, 0x1f, 0x93, 0xa9, 0x15, 0xfe, 0x60,
0x09, 0x75, 0xff, 0xd6, 0x81, 0x95, 0xa2, 0x93, 0x77, 0x11, 0xb4, 0xad, 0x83, 0xe8, 0x9a, 0x61,
0x1d, 0x54, 0x02, 0x26, 0x18, 0x0d, 0x82, 0x48, 0xf6, 0xcd, 0x40, 0x48, 0x63, 0x65, 0x29, 0x9e,
0xaa, 0xcb, 0x61, 0x26, 0x24, 0x4e, 0x7e, 0x73, 0xfc, 0x5a, 0xdc, 0x0c, 0x93, 0x25, 0xba, 0x6f,
0x36, 0xc9, 0xe9, 0xab, 0x05, 0xe1, 0x10, 0xcb, 0xa2, 0xda, 0x9f, 0x16, 0x09, 0xc5, 0x9f, 0xee,
0x1f, 0x3b, 0x70, 0xbe, 0x66, 0x72, 0xa5, 0x66, 0xdc, 0x81, 0xb5, 0x43, 0x5d, 0xa9, 0x26, 0x40,
0xa8, 0xc7, 0xa6, 0x94, 0xa2, 0xd2, 0xa0, 0xbd, 0xea, 0x07, 0xe8, 0x1e, 0x53, 0xde, 0x48, 0x4c,
0xa9, 0x75, 0x69, 0xa2, 0x5a, 0xb1, 0xf3, 0xfd, 0x06, 0x74, 0xc5, 0x51, 0x85, 0x78, 0x9a, 0xc3,
0x53, 0xf6, 0x01, 0x2c, 0xca, 0x87, 0x50, 0xec, 0xac, 0x6c, 0xd6, 0x7e, 0x7a, 0xd5, 0xdf, 0x2c,
0xc3, 0x52, 0x76, 0xd6, 0x7f, 0xff, 0xb3, 0x7f, 0xf9, 0x93, 0xc6, 0x32, 0x6b, 0x6f, 0x1f, 0xbf,
0xb5, 0x3d, 0xe6, 0x51, 0x86, 0x3c, 0x7e, 0x13, 0xa0, 0x78, 0x4b, 0xc4, 0x7a, 0xda, 0xc9, 0x28,
0xbd, 0x7d, 0xea, 0x9f, 0xaf, 0xa9, 0x91, 0x7c, 0xcf, 0x13, 0xdf, 0x75, 0xb7, 0x8b, 0x7c, 0x83,
0x28, 0xc8, 0xc5, 0xc3, 0xa2, 0x77, 0x9d, 0x2d, 0x36, 0x82, 0x8e, 0xf9, 0xa6, 0x88, 0xa9, 0x90,
0xb9, 0xe6, 0xa1, 0x52, 0xff, 0x42, 0x6d, 0x9d, 0xca, 0x17, 0x50, 0x1b, 0x67, 0xdd, 0x55, 0x6c,
0x63, 0x4a, 0x14, 0xba, 0x95, 0x9d, 0x7f, 0xba, 0x00, 0x2d, 0x9d, 0x76, 0x62, 0xdf, 0x86, 0x65,
0xeb, 0x74, 0x87, 0x29, 0xc6, 0x75, 0x87, 0x41, 0xfd, 0x8b, 0xf5, 0x95, 0xb2, 0xd9, 0x4b, 0xd4,
0x6c, 0x8f, 0x6d, 0x62, 0xb3, 0xf2, 0x78, 0x64, 0x9b, 0xce, 0xb4, 0xc4, 0xfd, 0xb2, 0x27, 0xd0,
0xb5, 0x4f, 0x64, 0xd8, 0x45, 0xdb, 0xa0, 0x94, 0x5a, 0x7b, 0xe5, 0x94, 0x5a, 0xd9, 0xdc, 0x45,
0x6a, 0x6e, 0x93, 0x6d, 0x98, 0xcd, 0xe9, 0x74, 0x10, 0xa7, 0x1b, 0x81, 0xe6, 0x63, 0x23, 0xf6,
0x8a, 0x5e, 0xea, 0xba, 0x47, 0x48, 0x7a, 0xd1, 0xaa, 0x2f, 0x91, 0xdc, 0x1e, 0x35, 0xc5, 0x18,
0x4d, 0xa8, 0xf9, 0xd6, 0x88, 0x7d, 0x04, 0x2d, 0xfd, 0xc0, 0x80, 0x9d, 0x33, 0x5e, 0x75, 0x98,
0xaf, 0x1e, 0xfa, 0xbd, 0x6a, 0x45, 0xdd, 0x52, 0x99, 0x9c, 0x51, 0x20, 0x1e, 0xc0, 0x59, 0xe9,
0xa4, 0x1e, 0xf0, 0x9f, 0x64, 0x24, 0x35, 0x4f, 0xa4, 0xae, 0x3b, 0xec, 0x06, 0x2c, 0xa9, 0x77,
0x1b, 0x6c, 0xb3, 0xfe, 0xfd, 0x49, 0xff, 0x5c, 0x05, 0x97, 0xfa, 0x7c, 0x13, 0xa0, 0x78, 0x73,
0xa0, 0x25, 0xbf, 0xf2, 0x12, 0x42, 0x4f, 0x62, 0xcd, 0x03, 0x85, 0x31, 0xbd, 0xb0, 0xb0, 0x9f,
0x34, 0xb0, 0x57, 0x0b, 0xfa, 0xda, 0xc7, 0x0e, 0xcf, 0x61, 0xe8, 0x6e, 0xd2, 0xdc, 0xad, 0x32,
0x52, 0xa5, 0x88, 0x9f, 0xa8, 0xbb, 0xb1, 0x77, 0xa0, 0x6d, 0xbc, 0x63, 0x60, 0x8a, 0x43, 0xf5,
0x0d, 0x44, 0xbf, 0x5f, 0x57, 0x25, 0xbb, 0xfb, 0x15, 0x58, 0xb6, 0x1e, 0x24, 0x68, 0xcd, 0xa8,
0x7b, 0xee, 0xa0, 0x35, 0xa3, 0xfe, 0x0d, 0xc3, 0x37, 0xa1, 0x6d, 0x3c, 0x1f, 0x60, 0xc6, 0x9d,
0xa0, 0xd2, 0xc3, 0x01, 0xdd, 0xa3, 0xba, 0xd7, 0x06, 0x1b, 0x34, 0xde, 0xae, 0xdb, 0xc2, 0xf1,
0xd2, 0x05, 0x51, 0x14, 0x92, 0x6f, 0x43, 0xd7, 0x7e, 0x50, 0xa0, 0xb5, 0xaa, 0xf6, 0x69, 0x82,
0xd6, 0xaa, 0x53, 0x5e, 0x21, 0x48, 0x81, 0xdc, 0x5a, 0xd7, 0x8d, 0x6c, 0x7f, 0x2a, 0x0f, 0x5d,
0x9e, 0xb1, 0xaf, 0xa3, 0xe9, 0x90, 0x37, 0x76, 0x59, 0xf1, 0x8c, 0xc2, 0xbe, 0xd7, 0xab, 0xa5,
0xbd, 0x72, 0xb9, 0xd7, 0x5d, 0x23, 0xe6, 0x6d, 0x56, 0x8c, 0x40, 0x58, 0x68, 0xba, 0xb9, 0x6b,
0x58, 0x68, 0xf3, 0x72, 0xaf, 0x61, 0xa1, 0xad, 0x0b, 0xbe, 0x65, 0x0b, 0x9d, 0x07, 0xc8, 0x23,
0x82, 0x95, 0xd2, 0x3d, 0x00, 0xad, 0x2c, 0xf5, 0xb7, 0x88, 0xfa, 0x97, 0x9e, 0x7f, 0x7d, 0xc0,
0x36, 0x33, 0xca, 0xbc, 0x6c, 0xab, 0x4b, 0x5f, 0xbf, 0x05, 0x1d, 0xf3, 0x22, 0xb8, 0xb6, 0xd9,
0x35, 0xd7, 0xd7, 0xb5, 0xcd, 0xae, 0xbb, 0x39, 0xae, 0x16, 0x97, 0x75, 0xcc, 0x66, 0xd8, 0x37,
0x61, 0xc5, 0xb8, 0x71, 0xb2, 0x3f, 0x8b, 0x86, 0x5a, 0x78, 0xaa, 0x77, 0x04, 0xfb, 0x75, 0xfe,
0x99, 0x7b, 0x8e, 0x18, 0xaf, 0xb9, 0x16, 0x63, 0x14, 0x9c, 0xdb, 0xd0, 0x36, 0x6f, 0xb3, 0x3c,
0x87, 0xef, 0x39, 0xa3, 0xca, 0xbc, 0x2e, 0x77, 0xdd, 0x61, 0x7f, 0xe6, 0x40, 0xc7, 0xbc, 0x7d,
0xca, 0xac, 0x3c, 0x6f, 0x89, 0x4f, 0xcf, 0xac, 0x33, 0x19, 0xb9, 0x1e, 0x75, 0xf2, 0xc1, 0xd6,
0x57, 0xac, 0x49, 0xfe, 0xd4, 0xf2, 0xf3, 0xaf, 0x95, 0xdf, 0xf8, 0x3d, 0x2b, 0x13, 0x98, 0xf7,
0x28, 0x9f, 0x5d, 0x77, 0xd8, 0xbb, 0xe2, 0x1d, 0xa8, 0x8a, 0xeb, 0x99, 0x61, 0xdc, 0xca, 0x53,
0x66, 0x3e, 0x99, 0xbc, 0xea, 0x5c, 0x77, 0xd8, 0xb7, 0xc4, 0x53, 0x3e, 0xf9, 0x2d, 0xcd, 0xfc,
0xcb, 0x7e, 0xef, 0xbe, 0x4e, 0xa3, 0xb9, 0xe4, 0x9e, 0xb7, 0x46, 0x53, 0xb6, 0xee, 0x7b, 0x00,
0x45, 0x92, 0x86, 0x95, 0x32, 0x16, 0xda, 0xee, 0x55, 0xf3, 0x38, 0xf6, 0x8a, 0xaa, 0xc4, 0x06,
0x72, 0xfc, 0x48, 0x08, 0xa3, 0xa4, 0xcf, 0xf4, 0x92, 0x56, 0x93, 0x2d, 0xfd, 0x7e, 0x5d, 0x55,
0x9d, 0x28, 0x2a, 0xfe, 0xec, 0x31, 0x2c, 0x3f, 0x88, 0xe3, 0x27, 0xd3, 0x44, 0xa7, 0x11, 0xed,
0x9c, 0xc1, 0xae, 0x9f, 0x1d, 0xf5, 0x4b, 0xa3, 0x70, 0x2f, 0x13, 0xab, 0x3e, 0xeb, 0x19, 0xac,
0xb6, 0x3f, 0x2d, 0x52, 0x44, 0xcf, 0x98, 0x0f, 0x6b, 0x7a, 0x8f, 0xd3, 0x1d, 0xef, 0xdb, 0x6c,
0xcc, 0x4c, 0x4d, 0xa5, 0x09, 0xcb, 0xeb, 0x50, 0xbd, 0xdd, 0xce, 0x14, 0xcf, 0xeb, 0x0e, 0xdb,
0x83, 0xce, 0x1d, 0x3e, 0x8c, 0x47, 0x5c, 0x46, 0xf9, 0xeb, 0x45, 0xc7, 0x75, 0x7a, 0xa0, 0xbf,
0x6c, 0x81, 0xb6, 0xd6, 0x27, 0xfe, 0x2c, 0xe5, 0x1f, 0x6f, 0x7f, 0x2a, 0xf3, 0x07, 0xcf, 0x94,
0xd6, 0xab, 0x9c, 0x87, 0xa5, 0xf5, 0xa5, 0x24, 0x89, 0xa5, 0xf5, 0x95, 0x24, 0x89, 0x35, 0xd5,
0x2a, 0xe7, 0xc2, 0x42, 0x58, 0xab, 0xe4, 0x55, 0xf4, 0x4e, 0x79, 0x5a, 0x36, 0xa6, 0x7f, 0xf9,
0x74, 0x02, 0xbb, 0xb5, 0x2d, 0xbb, 0xb5, 0x7d, 0x58, 0xbe, 0xc3, 0xc5, 0x64, 0x89, 0xb3, 0xca,
0xbe, 0x6d, 0x46, 0xcc, 0x73, 0xcd, 0xb2, 0x89, 0xa1, 0x3a, 0xdb, 0xac, 0xd3, 0x41, 0x21, 0xfb,
0x08, 0xda, 0xf7, 0x79, 0xae, 0x0e, 0x27, 0xb5, 0xbf, 0x51, 0x3a, 0xad, 0xec, 0xd7, 0x9c, 0x6d,
0xda, 0x32, 0x43, 0xdc, 0xb6, 0xf9, 0x68, 0xcc, 0x85, 0xb2, 0x0f, 0x82, 0xd1, 0x33, 0xf6, 0xeb,
0xc4, 0x5c, 0xdf, 0x67, 0xd8, 0x34, 0xce, 0xb4, 0x4c, 0xe6, 0x2b, 0x25, 0xbc, 0x8e, 0x73, 0x14,
0x8f, 0xb8, 0xb1, 0xc1, 0x45, 0xd0, 0x36, 0x2e, 0xdb, 0x68, 0x05, 0xaa, 0x5e, 0xf0, 0xd1, 0x0a,
0x54, 0x73, 0x37, 0xc7, 0xbd, 0x4a, 0xed, 0xb8, 0xec, 0x72, 0xd1, 0x8e, 0xb8, 0x8f, 0x53, 0xb4,
0xb4, 0xfd, 0xa9, 0x3f, 0xc9, 0x9f, 0xb1, 0x0f, 0xe9, 0x29, 0x8b, 0x79, 0x00, 0x5b, 0xf8, 0x3b,
0xe5, 0xb3, 0x5a, 0x3d, 0x59, 0x46, 0x95, 0xed, 0x03, 0x89, 0xa6, 0x68, 0x1f, 0xfc, 0x22, 0xc0,
0x7e, 0x1e, 0x27, 0x77, 0x7c, 0x3e, 0x89, 0xa3, 0xc2, 0x72, 0x15, 0x87, 0x8c, 0x85, 0xe5, 0x32,
0x4e, 0x1a, 0xd9, 0x87, 0x86, 0xc7, 0x69, 0x9d, 0x5f, 0x2b, 0xe1, 0x3a, 0xf5, 0x1c, 0x52, 0x4f,
0x48, 0xcd, 0x59, 0xe4, 0x75, 0x07, 0xfd, 0xc7, 0x22, 0x8b, 0xa7, 0xfd, 0xc7, 0x4a, 0x82, 0x50,
0x9b, 0xbd, 0x9a, 0x94, 0xdf, 0x1e, 0xb4, 0x8a, 0xb4, 0x90, 0xda, 0x92, 0xca, 0x49, 0x24, 0xbd,
0xc7, 0x54, 0x92, 0x35, 0xee, 0x2a, 0x4d, 0x15, 0xb0, 0x25, 0x9c, 0x2a, 0xca, 0xc0, 0x04, 0xb0,
0x2e, 0x3a, 0xa8, 0x37, 0x4c, 0x3a, 0x36, 0x53, 0x23, 0xa9, 0x49, 0x98, 0x68, 0x6d, 0xae, 0xcd,
0x37, 0x58, 0xb1, 0x1d, 0x4a, 0xab, 0x38, 0xb2, 0x43, 0xd3, 0x3c, 0x81, 0xb5, 0x4a, 0xb0, 0xac,
0x55, 0xfa, 0xb4, 0x1c, 0x85, 0x56, 0xe9, 0x53, 0xe3, 0x6c, 0xf7, 0x2c, 0x35, 0xb9, 0xe2, 0x02,
0x36, 0x99, 0x9d, 0x04, 0xf9, 0xf0, 0xe8, 0x5d, 0x67, 0xeb, 0x60, 0x81, 0xfe, 0x69, 0xe4, 0xf3,
0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xc7, 0xfe, 0x7f, 0x9b, 0x44, 0x00, 0x00,
}

@ -592,18 +592,10 @@ func RegisterWalletUnlockerHandlerFromEndpoint(ctx context.Context, mux *runtime
// RegisterWalletUnlockerHandler registers the http handlers for service WalletUnlocker to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterWalletUnlockerHandlerClient(ctx, mux, NewWalletUnlockerClient(conn))
}
// RegisterWalletUnlockerHandler registers the http handlers for service WalletUnlocker to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "WalletUnlockerClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WalletUnlockerClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "WalletUnlockerClient" to call the correct interceptors.
func RegisterWalletUnlockerHandlerClient(ctx context.Context, mux *runtime.ServeMux, client WalletUnlockerClient) error {
client := NewWalletUnlockerClient(conn)
mux.Handle("GET", pattern_WalletUnlocker_GenSeed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -632,7 +624,7 @@ func RegisterWalletUnlockerHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("POST", pattern_WalletUnlocker_InitWallet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -661,7 +653,7 @@ func RegisterWalletUnlockerHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("POST", pattern_WalletUnlocker_UnlockWallet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -736,18 +728,10 @@ func RegisterLightningHandlerFromEndpoint(ctx context.Context, mux *runtime.Serv
// RegisterLightningHandler registers the http handlers for service Lightning to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterLightningHandlerClient(ctx, mux, NewLightningClient(conn))
}
// RegisterLightningHandler registers the http handlers for service Lightning to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "LightningClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "LightningClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "LightningClient" to call the correct interceptors.
func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux, client LightningClient) error {
client := NewLightningClient(conn)
mux.Handle("GET", pattern_Lightning_WalletBalance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -776,7 +760,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ChannelBalance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -805,7 +789,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetTransactions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -834,7 +818,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_SendCoins_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -863,7 +847,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_NewWitnessAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -892,7 +876,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_ConnectPeer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -921,7 +905,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("DELETE", pattern_Lightning_DisconnectPeer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -950,7 +934,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListPeers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -979,7 +963,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1008,7 +992,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_PendingChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1037,7 +1021,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1066,7 +1050,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_OpenChannelSync_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1095,7 +1079,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("DELETE", pattern_Lightning_CloseChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1124,7 +1108,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_SendPaymentSync_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1153,7 +1137,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_AddInvoice_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1182,7 +1166,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListInvoices_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1211,7 +1195,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_LookupInvoice_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1240,7 +1224,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_SubscribeInvoices_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1269,7 +1253,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_DecodePayReq_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1298,7 +1282,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListPayments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1327,7 +1311,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("DELETE", pattern_Lightning_DeleteAllPayments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1356,7 +1340,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_DescribeGraph_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1385,7 +1369,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetChanInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1414,7 +1398,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetNodeInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1443,7 +1427,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_QueryRoutes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1472,7 +1456,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetNetworkInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1501,7 +1485,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_FeeReport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1530,7 +1514,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_UpdateChannelPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
@ -1559,7 +1543,7 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_Lightning_ForwardingHistory_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {

@ -1369,6 +1369,34 @@ message ClosedChannelUpdate {
ChannelPoint chan_point = 4;
}
message HopHint {
/// The public key of the node at the start of the channel.
string node_id = 1 [json_name = "node_id"];
/// The unique identifier of the channel.
uint64 chan_id = 2 [json_name = "chan_id"];
/// The base fee of the channel denominated in millisatoshis.
uint32 fee_base_msat = 3 [json_name = "fee_base_msat"];
/**
The fee rate of the channel for sending one satoshi across it denominated in
millionths of a satoshi.
*/
uint32 fee_proportional_millionths = 4 [json_name = "fee_proportional_millionths"];
/// The time-lock delta of the channel.
uint32 cltv_expiry_delta = 5 [json_name = "cltv_expiry_delta"];
}
message RouteHint {
/**
A list of hop hints that when chained together can assist in reaching a
specific destination.
*/
repeated HopHint hop_hints = 1 [json_name = "hop_hints"];
}
message Invoice {
/**
An optional memo to attach along with the invoice. Used for record keeping
@ -1424,6 +1452,15 @@ message Invoice {
/// Delta to use for the time-lock of the CLTV extended to the final hop.
uint64 cltv_expiry = 13 [json_name = "cltv_expiry"];
/**
Route hints that can each be individually used to assist in reaching the
invoice's destination.
*/
repeated RouteHint route_hints = 14 [json_name = "route_hints"];
/// Whether this invoice should include routing hints for private channels.
bool private = 15 [json_name = "private"];
}
message AddInvoiceResponse {
bytes r_hash = 1 [json_name = "r_hash"];
@ -1513,6 +1550,7 @@ message PayReq {
string description_hash = 7 [json_name = "description_hash"];
string fallback_addr = 8 [json_name = "fallback_addr"];
int64 cltv_expiry = 9 [json_name = "cltv_expiry"];
repeated RouteHint route_hints = 10 [json_name = "route_hints"];
}
message FeeReportRequest {}

@ -789,14 +789,6 @@
}
},
"definitions": {
"NewAddressRequestAddressType": {
"type": "string",
"enum": [
"WITNESS_PUBKEY_HASH",
"NESTED_PUBKEY_HASH"
],
"default": "WITNESS_PUBKEY_HASH"
},
"PendingChannelsResponseClosedChannel": {
"type": "object",
"properties": {
@ -1515,6 +1507,35 @@
}
}
},
"lnrpcHopHint": {
"type": "object",
"properties": {
"node_id": {
"type": "string",
"description": "/ The public key of the node at the start of the channel."
},
"chan_id": {
"type": "string",
"format": "uint64",
"description": "/ The unique identifier of the channel."
},
"fee_base_msat": {
"type": "integer",
"format": "int64",
"description": "/ The base fee of the channel denominated in millisatoshis."
},
"fee_proportional_millionths": {
"type": "integer",
"format": "int64",
"description": "*\nThe fee rate of the channel for sending one satoshi across it denominated in\nmillionths of a satoshi."
},
"cltv_expiry_delta": {
"type": "integer",
"format": "int64",
"description": "/ The time-lock delta of the channel."
}
}
},
"lnrpcInitWalletRequest": {
"type": "object",
"properties": {
@ -1604,6 +1625,18 @@
"type": "string",
"format": "uint64",
"description": "/ Delta to use for the time-lock of the CLTV extended to the final hop."
},
"route_hints": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcRouteHint"
},
"description": "*\nRoute hints that can each be individually used to assist in reaching the\ninvoice's destination."
},
"private": {
"type": "boolean",
"format": "boolean",
"description": "/ Whether this invoice should include routing hints for private channels."
}
}
},
@ -1887,6 +1920,12 @@
"cltv_expiry": {
"type": "string",
"format": "int64"
},
"route_hints": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcRouteHint"
}
}
}
},
@ -2127,6 +2166,18 @@
},
"description": "*\nA path through the channel graph which runs over one or more channels in\nsuccession. This struct carries all the information required to craft the\nSphinx onion packet, and send the payment along the first hop in the path. A\nroute is only selected as valid if all the channels have sufficient capacity to\ncarry the initial payment amount after fees are accounted for."
},
"lnrpcRouteHint": {
"type": "object",
"properties": {
"hop_hints": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcHopHint"
},
"description": "*\nA list of hop hints that when chained together can assist in reaching a\nspecific destination."
}
}
},
"lnrpcRoutingPolicy": {
"type": "object",
"properties": {

@ -5,6 +5,8 @@ import (
"time"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
)
const (
@ -148,20 +150,69 @@ func (m *missionControl) GraphPruneView() graphPruneView {
// and will now be pruned after a decay like the main view within mission
// control. We do this as we want to avoid the case where we continually try a
// bad edge or route multiple times in a session. This can lead to an infinite
// loop if payment attempts take long enough.
// loop if payment attempts take long enough. An additional set of edges can
// also be provided to assist in reaching the payment's destination.
type paymentSession struct {
pruneViewSnapshot graphPruneView
additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy
mc *missionControl
}
// NewPaymentSession creates a new payment session backed by the latest prune
// view from Mission Control.
func (m *missionControl) NewPaymentSession() *paymentSession {
// view from Mission Control. An optional set of routing hints can be provided
// in order to populate additional edges to explore when finding a path to the
// payment's destination.
func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
target *btcec.PublicKey) *paymentSession {
viewSnapshot := m.GraphPruneView()
edges := make(map[Vertex][]*channeldb.ChannelEdgePolicy)
// Traverse through all of the available hop hints and include them in
// our edges map, indexed by the public key of the channel's starting
// node.
for _, routeHint := range routeHints {
// If multiple hop hints are provided within a single route
// hint, we'll assume they must be chained together and sorted
// in forward order in order to reach the target successfully.
for i, hopHint := range routeHint {
// In order to determine the end node of this hint,
// we'll need to look at the next hint's start node. If
// we've reached the end of the hints list, we can
// assume we've reached the destination.
endNode := &channeldb.LightningNode{}
if i != len(routeHint)-1 {
endNode.AddPubKey(routeHint[i+1].NodeID)
} else {
endNode.AddPubKey(target)
}
// Finally, create the channel edge from the hop hint
// and add it to list of edges corresponding to the node
// at the start of the channel.
edge := &channeldb.ChannelEdgePolicy{
Node: endNode,
ChannelID: hopHint.ChannelID,
FeeBaseMSat: lnwire.MilliSatoshi(
hopHint.FeeBaseMSat,
),
FeeProportionalMillionths: lnwire.MilliSatoshi(
hopHint.FeeProportionalMillionths,
),
TimeLockDelta: hopHint.CLTVExpiryDelta,
}
v := NewVertex(hopHint.NodeID)
edges[v] = append(edges[v], edge)
}
}
return &paymentSession{
pruneViewSnapshot: viewSnapshot,
additionalEdges: edges,
mc: m,
}
}
@ -231,8 +282,11 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
// Taking into account this prune view, we'll attempt to locate a path
// to our destination, respecting the recommendations from
// missionControl.
path, err := findPath(nil, p.mc.graph, p.mc.selfNode, payment.Target,
pruneView.vertexes, pruneView.edges, payment.Amount)
path, err := findPath(
nil, p.mc.graph, p.additionalEdges, p.mc.selfNode,
payment.Target, pruneView.vertexes, pruneView.edges,
payment.Amount,
)
if err != nil {
return nil, err
}

@ -28,6 +28,28 @@ const (
infinity = math.MaxInt64
)
// HopHint is a routing hint that contains the minimum information of a channel
// required for an intermediate hop in a route to forward the payment to the
// next. This should be ideally used for private channels, since they are not
// publicly advertised to the network for routing.
type HopHint struct {
// NodeID is the public key of the node at the start of the channel.
NodeID *btcec.PublicKey
// ChannelID is the unique identifier of the channel.
ChannelID uint64
// FeeBaseMSat is the base fee of the channel in millisatoshis.
FeeBaseMSat uint32
// FeeProportionalMillionths is the fee rate, in millionths of a
// satoshi, for every satoshi sent through the channel.
FeeProportionalMillionths uint32
// CLTVExpiryDelta is the time-lock delta of the channel.
CLTVExpiryDelta uint16
}
// ChannelHop is an intermediate hop within the network with a greater
// multi-hop payment route. This struct contains the relevant routing policy of
// the particular edge, as well as the total capacity, and origin chain of the
@ -433,6 +455,7 @@ func edgeWeight(amt lnwire.MilliSatoshi, e *channeldb.ChannelEdgePolicy) int64 {
// function returns a slice of ChannelHop structs which encoded the chosen path
// from the target to the source.
func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy,
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
ignoredNodes map[Vertex]struct{}, ignoredEdges map[uint64]struct{},
amt lnwire.MilliSatoshi) ([]*ChannelHop, error) {
@ -451,9 +474,8 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
// traversal.
var nodeHeap distanceHeap
// For each node/Vertex the graph we create an entry in the distance
// map for the node set with a distance of "infinity". We also mark
// add the node to our set of unvisited nodes.
// For each node in the graph, we create an entry in the distance
// map for the node set with a distance of "infinity".
distance := make(map[Vertex]nodeWithDist)
if err := graph.ForEachNode(tx, func(_ *bolt.Tx, node *channeldb.LightningNode) error {
// TODO(roasbeef): with larger graph can just use disk seeks
@ -467,6 +489,86 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
return nil, err
}
// We'll also include all the nodes found within the additional edges
// that are not known to us yet in the distance map.
for vertex := range additionalEdges {
node := &channeldb.LightningNode{PubKeyBytes: vertex}
distance[vertex] = nodeWithDist{
dist: infinity,
node: node,
}
}
// We can't always assume that the end destination is publicly
// advertised to the network and included in the graph.ForEachNode call
// above, so we'll manually include the target node.
targetVertex := NewVertex(target)
targetNode := &channeldb.LightningNode{PubKeyBytes: targetVertex}
distance[targetVertex] = nodeWithDist{
dist: infinity,
node: targetNode,
}
// We'll use this map as a series of "previous" hop pointers. So to get
// to `Vertex` we'll take the edge that it's mapped to within `prev`.
prev := make(map[Vertex]edgeWithPrev)
// processEdge is a helper closure that will be used to make sure edges
// satisfy our specific requirements.
processEdge := func(edge *channeldb.ChannelEdgePolicy,
capacity btcutil.Amount, pivot Vertex) {
v := Vertex(edge.Node.PubKeyBytes)
// If the edge is currently disabled, then we'll stop here, as
// we shouldn't attempt to route through it.
edgeFlags := lnwire.ChanUpdateFlag(edge.Flags)
if edgeFlags&lnwire.ChanUpdateDisabled != 0 {
return
}
// If this vertex or edge has been black listed, then we'll skip
// exploring this edge.
if _, ok := ignoredNodes[v]; ok {
return
}
if _, ok := ignoredEdges[edge.ChannelID]; ok {
return
}
// Compute the tentative distance to this new channel/edge which
// is the distance to our pivot node plus the weight of this
// edge.
tempDist := distance[pivot].dist + edgeWeight(amt, edge)
// If this new tentative distance is better than the current
// best known distance to this node, then we record the new
// better distance, and also populate our "next hop" map with
// this edge. We'll also shave off irrelevant edges by adding
// the sufficient capacity of an edge and clearing their
// min-htlc amount to our relaxation condition.
if tempDist < distance[v].dist && capacity >= amt.ToSatoshis() &&
amt >= edge.MinHTLC && edge.TimeLockDelta != 0 {
distance[v] = nodeWithDist{
dist: tempDist,
node: edge.Node,
}
prev[v] = edgeWithPrev{
edge: &ChannelHop{
ChannelEdgePolicy: edge,
Capacity: capacity,
},
prevNode: pivot,
}
// Add this new node to our heap as we'd like to further
// explore down this edge.
heap.Push(&nodeHeap, distance[v])
}
}
// TODO(roasbeef): also add path caching
// * similar to route caching, but doesn't factor in the amount
@ -483,11 +585,6 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
// heap.
heap.Push(&nodeHeap, distance[sourceVertex])
targetBytes := target.SerializeCompressed()
// We'll use this map as a series of "previous" hop pointers. So to get
// to `Vertex` we'll take the edge that it's mapped to within `prev`.
prev := make(map[Vertex]edgeWithPrev)
for nodeHeap.Len() != 0 {
// Fetch the node within the smallest distance from our source
// from the heap.
@ -497,7 +594,7 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
// If we've reached our target (or we don't have any outgoing
// edges), then we're done here and can exit the graph
// traversal early.
if bytes.Equal(bestNode.PubKeyBytes[:], targetBytes) {
if bytes.Equal(bestNode.PubKeyBytes[:], targetVertex[:]) {
break
}
@ -507,65 +604,9 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
pivot := Vertex(bestNode.PubKeyBytes)
err := bestNode.ForEachChannel(tx, func(tx *bolt.Tx,
edgeInfo *channeldb.ChannelEdgeInfo,
outEdge, inEdge *channeldb.ChannelEdgePolicy) error {
outEdge, _ *channeldb.ChannelEdgePolicy) error {
v := Vertex(outEdge.Node.PubKeyBytes)
// If the outgoing edge is currently disabled, then
// we'll stop here, as we shouldn't attempt to route
// through it.
edgeFlags := lnwire.ChanUpdateFlag(outEdge.Flags)
if edgeFlags&lnwire.ChanUpdateDisabled == lnwire.ChanUpdateDisabled {
return nil
}
// If this Vertex or edge has been black listed, then
// we'll skip exploring this edge during this
// iteration.
if _, ok := ignoredNodes[v]; ok {
return nil
}
if _, ok := ignoredEdges[outEdge.ChannelID]; ok {
return nil
}
// Compute the tentative distance to this new
// channel/edge which is the distance to our current
// pivot node plus the weight of this edge.
tempDist := distance[pivot].dist + edgeWeight(amt, outEdge)
// If this new tentative distance is better than the
// current best known distance to this node, then we
// record the new better distance, and also populate
// our "next hop" map with this edge. We'll also shave
// off irrelevant edges by adding the sufficient
// capacity of an edge and clearing their min-htlc
// amount to our relaxation condition.
if tempDist < distance[v].dist &&
edgeInfo.Capacity >= amt.ToSatoshis() &&
amt >= outEdge.MinHTLC &&
outEdge.TimeLockDelta != 0 {
distance[v] = nodeWithDist{
dist: tempDist,
node: outEdge.Node,
}
prev[v] = edgeWithPrev{
// We'll use the *incoming* edge here
// as we need to use the routing policy
// specified by the node this channel
// connects to.
edge: &ChannelHop{
ChannelEdgePolicy: outEdge,
Capacity: edgeInfo.Capacity,
},
prevNode: bestNode.PubKeyBytes,
}
// Add this new node to our heap as we'd like
// to further explore down this edge.
heap.Push(&nodeHeap, distance[v])
}
processEdge(outEdge, edgeInfo.Capacity, pivot)
// TODO(roasbeef): return min HTLC as error in end?
@ -574,6 +615,15 @@ func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
if err != nil {
return nil, err
}
// Then, we'll examine all the additional edges from the node
// we're currently visiting. Since we don't know the capacity
// of the private channel, we'll assume it was selected as a
// routing hint due to having enough capacity for the payment
// and use the payment amount as its capacity.
for _, edge := range additionalEdges[bestNode.PubKeyBytes] {
processEdge(edge, amt.ToSatoshis(), pivot)
}
}
// If the target node isn't found in the prev hop map, then a path
@ -647,7 +697,8 @@ func findPaths(tx *bolt.Tx, graph *channeldb.ChannelGraph,
// selfNode) to the target destination that's capable of carrying amt
// satoshis along the path before fees are calculated.
startingPath, err := findPath(
tx, graph, source, target, ignoredVertexes, ignoredEdges, amt,
tx, graph, nil, source, target, ignoredVertexes, ignoredEdges,
amt,
)
if err != nil {
log.Errorf("Unable to find path: %v", err)
@ -720,8 +771,8 @@ func findPaths(tx *bolt.Tx, graph *channeldb.ChannelGraph,
// root path removed, we'll attempt to find another
// shortest path from the spur node to the destination.
spurPath, err := findPath(
tx, graph, spurNode, target, ignoredVertexes,
ignoredEdges, amt,
tx, graph, nil, spurNode, target,
ignoredVertexes, ignoredEdges, amt,
)
// If we weren't able to find a path, we'll continue to

@ -311,8 +311,10 @@ func TestBasicGraphPathFinding(t *testing.T) {
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := aliases["sophon"]
path, err := findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt)
path, err := findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
@ -451,8 +453,10 @@ func TestBasicGraphPathFinding(t *testing.T) {
// exist two possible paths in the graph, but the shorter (1 hop) path
// should be selected.
target = aliases["luoji"]
path, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt)
path, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt,
)
if err != nil {
t.Fatalf("unable to find route: %v", err)
}
@ -485,6 +489,69 @@ func TestBasicGraphPathFinding(t *testing.T) {
}
}
func TestPathFindingWithAdditionalEdges(t *testing.T) {
t.Parallel()
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
defer cleanUp()
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
sourceNode, err := graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
paymentAmt := lnwire.NewMSatFromSatoshis(100)
// In this test, we'll test that we're able to find paths through
// private channels when providing them as additional edges in our path
// finding algorithm. To do so, we'll create a new node, doge, and
// create a private channel between it and songoku. We'll then attempt
// to find a path from our source node, roasbeef, to doge.
dogePubKeyHex := "03dd46ff29a6941b4a2607525b043ec9b020b3f318a1bf281536fd7011ec59c882"
dogePubKeyBytes, err := hex.DecodeString(dogePubKeyHex)
if err != nil {
t.Fatalf("unable to decode public key: %v", err)
}
dogePubKey, err := btcec.ParsePubKey(dogePubKeyBytes, btcec.S256())
if err != nil {
t.Fatalf("unable to parse public key from bytes: %v", err)
}
doge := &channeldb.LightningNode{}
doge.AddPubKey(dogePubKey)
doge.Alias = "doge"
// Create the channel edge going from songoku to doge and include it in
// our map of additional edges.
songokuToDoge := &channeldb.ChannelEdgePolicy{
Node: doge,
ChannelID: 1337,
FeeBaseMSat: 1,
FeeProportionalMillionths: 1000,
TimeLockDelta: 9,
}
additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{
NewVertex(aliases["songoku"]): {songokuToDoge},
}
// We should now be able to find a path from roasbeef to doge.
path, err := findPath(
nil, graph, additionalEdges, sourceNode, dogePubKey, nil, nil,
paymentAmt,
)
if err != nil {
t.Fatalf("unable to find private path to doge: %v", err)
}
// The path should represent the following hops:
// roasbeef -> songoku -> doge
assertExpectedPath(t, path, "songoku", "doge")
}
func TestKShortestPathFinding(t *testing.T) {
t.Parallel()
@ -529,23 +596,11 @@ func TestKShortestPathFinding(t *testing.T) {
t.Fatalf("paths found not ordered properly")
}
// Finally, we'll assert the exact expected ordering of both paths
// found.
assertExpectedPath := func(path []*ChannelHop, nodeAliases ...string) {
for i, hop := range path {
if hop.Node.Alias != nodeAliases[i] {
t.Fatalf("expected %v to be pos #%v in hop, "+
"instead %v was", nodeAliases[i], i,
hop.Node.Alias)
}
}
}
// The first route should be a direct route to luo ji.
assertExpectedPath(paths[0], "roasbeef", "luoji")
assertExpectedPath(t, paths[0], "roasbeef", "luoji")
// The second route should be a route to luo ji via satoshi.
assertExpectedPath(paths[1], "roasbeef", "satoshi", "luoji")
assertExpectedPath(t, paths[1], "roasbeef", "satoshi", "luoji")
}
func TestNewRoutePathTooLong(t *testing.T) {
@ -572,8 +627,10 @@ func TestNewRoutePathTooLong(t *testing.T) {
// We start by confirming that routing a payment 20 hops away is possible.
// Alice should be able to find a valid route to ursula.
target := aliases["ursula"]
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt)
_, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt,
)
if err != nil {
t.Fatalf("path should have been found")
}
@ -581,8 +638,10 @@ func TestNewRoutePathTooLong(t *testing.T) {
// Vincent is 21 hops away from Alice, and thus no valid route should be
// presented to Alice.
target = aliases["vincent"]
path, err := findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt)
path, err := findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt,
)
if err == nil {
t.Fatalf("should not have been able to find path, supposed to be "+
"greater than 20 hops, found route with %v hops",
@ -621,8 +680,10 @@ func TestPathNotAvailable(t *testing.T) {
t.Fatalf("unable to parse pubkey: %v", err)
}
_, err = findPath(nil, graph, sourceNode, unknownNode, ignoredVertexes,
ignoredEdges, 100)
_, err = findPath(
nil, graph, nil, sourceNode, unknownNode, ignoredVertexes,
ignoredEdges, 100,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("path shouldn't have been found: %v", err)
}
@ -655,8 +716,10 @@ func TestPathInsufficientCapacity(t *testing.T) {
target := aliases["sophon"]
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt)
_, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
}
@ -683,8 +746,10 @@ func TestRouteFailMinHTLC(t *testing.T) {
// attempt should fail.
target := aliases["songoku"]
payAmt := lnwire.MilliSatoshi(10)
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt)
_, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
}
@ -711,8 +776,10 @@ func TestRouteFailDisabledEdge(t *testing.T) {
// succeed without issue, and return a single path.
target := aliases["songoku"]
payAmt := lnwire.NewMSatFromSatoshis(10000)
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt)
_, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
@ -730,8 +797,10 @@ func TestRouteFailDisabledEdge(t *testing.T) {
// Now, if we attempt to route through that edge, we should get a
// failure as it is no longer eligible.
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt)
_, err = findPath(
nil, graph, nil, sourceNode, target, ignoredVertexes,
ignoredEdges, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
}
@ -967,3 +1036,16 @@ func TestPathFindSpecExample(t *testing.T) {
startingHeight+DefaultFinalCLTVDelta)
}
}
func assertExpectedPath(t *testing.T, path []*ChannelHop, nodeAliases ...string) {
if len(path) != len(nodeAliases) {
t.Fatal("number of hops and number of aliases do not match")
}
for i, hop := range path {
if hop.Node.Alias != nodeAliases[i] {
t.Fatalf("expected %v to be pos #%v in hop, instead "+
"%v was", nodeAliases[i], i, hop.Node.Alias)
}
}
}

@ -1482,6 +1482,16 @@ type LightningPayment struct {
// indefinitely.
PayAttemptTimeout time.Duration
// RouteHints represents the different routing hints that can be used to
// assist a payment in reaching its destination successfully. These
// hints will act as intermediate hops along the route.
//
// NOTE: This is optional unless required by the payment. When providing
// multiple routes, ensure the hop hints within each route are chained
// together and sorted in forward order in order to reach the
// destination successfully.
RouteHints [][]HopHint
// TODO(roasbeef): add e2e message?
}
@ -1495,7 +1505,14 @@ type LightningPayment struct {
func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route, error) {
log.Tracef("Dispatching route for lightning payment: %v",
newLogClosure(func() string {
// Remove the public key curve parameters when logging
// the route to prevent spamming the logs.
payment.Target.Curve = nil
for _, routeHint := range payment.RouteHints {
for _, hopHint := range routeHint {
hopHint.NodeID.Curve = nil
}
}
return spew.Sdump(payment)
}),
)
@ -1537,7 +1554,9 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
// Before starting the HTLC routing attempt, we'll create a fresh
// payment session which will report our errors back to mission
// control.
paySession := r.missionControl.NewPaymentSession()
paySession := r.missionControl.NewPaymentSession(
payment.RouteHints, payment.Target,
)
// We'll continue until either our payment succeeds, or we encounter a
// critical error during path finding.

@ -1602,8 +1602,8 @@ func TestFindPathFeeWeighting(t *testing.T) {
// the edge weighting, we should select the direct path over the 2 hop
// path even though the direct path has a higher potential time lock.
path, err := findPath(
nil, ctx.graph, sourceNode, target, ignoreVertex, ignoreEdge,
amt,
nil, ctx.graph, nil, sourceNode, target, ignoreVertex,
ignoreEdge, amt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)

@ -1,6 +1,7 @@
package main
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
@ -1703,12 +1704,13 @@ func validatePayReqExpiry(payReq *zpay32.Invoice) error {
// Lightning Network with a single persistent connection.
func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) error {
// For each payment we need to know the msat amount, the destination
// public key, and the payment hash.
// public key, the payment hash, and the optional route hints.
type payment struct {
msat lnwire.MilliSatoshi
dest []byte
pHash []byte
cltvDelta uint16
routeHints [][]routing.HopHint
}
payChan := make(chan *payment)
errChan := make(chan error, 1)
@ -1820,6 +1822,7 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
p.pHash = payReq.PaymentHash[:]
p.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
p.routeHints = payReq.RouteHints
} else {
// If the payment request field was not
// specified, construct the payment from
@ -1903,6 +1906,7 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
Target: destNode,
Amount: p.msat,
PaymentHash: rHash,
RouteHints: p.routeHints,
}
if p.cltvDelta != 0 {
payment.FinalCLTVDelta = &p.cltvDelta
@ -1964,6 +1968,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
amtMSat lnwire.MilliSatoshi
rHash [32]byte
cltvDelta uint16
routeHints [][]routing.HopHint
)
// If the proto request has an encoded payment request, then we we'll
@ -1996,6 +2001,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
rHash = *payReq.PaymentHash
cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
routeHints = payReq.RouteHints
// Otherwise, the payment conditions have been manually
// specified in the proto.
@ -2046,6 +2052,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
Target: destPub,
Amount: amtMSat,
PaymentHash: rHash,
RouteHints: routeHints,
}
if cltvDelta != 0 {
payment.FinalCLTVDelta = &cltvDelta
@ -2202,13 +2209,107 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
}
// If we were requested to include routing hints in the invoice, then
// we'll fetch all of our available private channels and create routing
// hints for them.
if invoice.Private {
openChannels, err := r.server.chanDB.FetchAllChannels()
if err != nil {
return nil, fmt.Errorf("could not fetch all channels")
}
graph := r.server.chanDB.ChannelGraph()
numHints := 0
for _, channel := range openChannels {
// We'll restrict the number of individual route hints
// to 20 to avoid creating overly large invoices.
if numHints > 20 {
break
}
// Since we're only interested in our private channels,
// we'll skip public ones.
isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
if isPublic {
continue
}
// Make sure the counterparty has enough balance in the
// channel for our amount. We do this in order to reduce
// payment errors when attempting to use this channel
// as a hint.
chanPoint := lnwire.NewChanIDFromOutPoint(
&channel.FundingOutpoint,
)
if amtMSat >= channel.LocalCommitment.RemoteBalance {
rpcsLog.Debugf("Skipping channel %v due to "+
"not having enough remote balance",
chanPoint)
continue
}
// Make sure the channel is active.
link, err := r.server.htlcSwitch.GetLink(chanPoint)
if err != nil {
rpcsLog.Errorf("Unable to get link for "+
"channel %v: %v", chanPoint, err)
}
if !link.EligibleToForward() {
rpcsLog.Debugf("Skipping link %v due to not "+
"being eligible to forward payments",
chanPoint)
continue
}
// Fetch the policies for each end of the channel.
chanID := channel.ShortChanID.ToUint64()
_, p1, p2, err := graph.FetchChannelEdgesByID(chanID)
if err != nil {
rpcsLog.Errorf("Unable to fetch the routing "+
"policies for the edges of the channel "+
"%v: %v", chanPoint, err)
continue
}
// Now, we'll need to determine which is the correct
// policy for HTLCs being sent from the remote node.
var remotePolicy *channeldb.ChannelEdgePolicy
remotePub := channel.IdentityPub.SerializeCompressed()
if bytes.Equal(remotePub, p1.Node.PubKeyBytes[:]) {
remotePolicy = p1
} else {
remotePolicy = p2
}
// Finally, create the routing hint for this channel and
// add it to our list of route hints.
hint := routing.HopHint{
NodeID: channel.IdentityPub,
ChannelID: chanID,
FeeBaseMSat: uint32(remotePolicy.FeeBaseMSat),
FeeProportionalMillionths: uint32(
remotePolicy.FeeProportionalMillionths,
),
CLTVExpiryDelta: remotePolicy.TimeLockDelta,
}
// Include the route hint in our set of options that
// will be used when creating the invoice.
routeHint := []routing.HopHint{hint}
options = append(options, zpay32.RouteHint(routeHint))
numHints++
}
}
// Create and encode the payment request as a bech32 (zpay32) string.
creationDate := time.Now()
payReq, err := zpay32.NewInvoice(
activeNetParams.Params,
rHash,
creationDate,
options...,
activeNetParams.Params, rHash, creationDate, options...,
)
if err != nil {
return nil, err
@ -2282,6 +2383,9 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
// The expiry will default to 9 blocks if not specified explicitly.
cltvExpiry := decoded.MinFinalCLTVExpiry()
// Convert between the `lnrpc` and `routing` types.
routeHints := createRPCRouteHints(decoded.RouteHints)
preimage := invoice.Terms.PaymentPreimage
satAmt := invoice.Terms.Value.ToSatoshis()
@ -2299,9 +2403,40 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
Expiry: expiry,
CltvExpiry: cltvExpiry,
FallbackAddr: fallbackAddr,
RouteHints: routeHints,
}, nil
}
// createRPCRouteHints takes in the decoded form of an invoice's route hints
// and converts them into the lnrpc type.
func createRPCRouteHints(routeHints [][]routing.HopHint) []*lnrpc.RouteHint {
var res []*lnrpc.RouteHint
for _, route := range routeHints {
hopHints := make([]*lnrpc.HopHint, 0, len(route))
for _, hop := range route {
pubKey := hex.EncodeToString(
hop.NodeID.SerializeCompressed(),
)
hint := &lnrpc.HopHint{
NodeId: pubKey,
ChanId: hop.ChannelID,
FeeBaseMsat: hop.FeeBaseMSat,
FeeProportionalMillionths: hop.FeeProportionalMillionths,
CltvExpiryDelta: uint32(hop.CLTVExpiryDelta),
}
hopHints = append(hopHints, hint)
}
routeHint := &lnrpc.RouteHint{HopHints: hopHints}
res = append(res, routeHint)
}
return res
}
// LookupInvoice attempts to look up an invoice according to its payment hash.
// The passed payment hash *must* be exactly 32 bytes, if not an error is
// returned.
@ -3120,6 +3255,9 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
// explicitly.
expiry := int64(payReq.Expiry().Seconds())
// Convert between the `lnrpc` and `routing` types.
routeHints := createRPCRouteHints(payReq.RouteHints)
amt := int64(0)
if payReq.MilliSat != nil {
amt = int64(payReq.MilliSat.ToSatoshis())
@ -3136,6 +3274,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
FallbackAddr: fallbackAddr,
Expiry: expiry,
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
RouteHints: routeHints,
}, nil
}

@ -37,9 +37,9 @@ const (
// with zeroes.
pubKeyBase32Len = 53
// routingInfoLen is the number of bytes needed to encode the extra
// routing info of a single private route.
routingInfoLen = 51
// hopHintLen is the number of bytes needed to encode the hop hint of a
// single private route.
hopHintLen = 51
// The following byte values correspond to the supported field types.
// The field name is the character representing that 5-bit value in the
@ -141,31 +141,12 @@ type Invoice struct {
// Optional.
FallbackAddr btcutil.Address
// RoutingInfo is one or more entries containing extra routing
// information for a private route to the target node.
// Optional.
RoutingInfo []ExtraRoutingInfo
}
// ExtraRoutingInfo holds the information needed to route a payment along one
// private channel.
type ExtraRoutingInfo struct {
// PubKey is the public key of the node at the start of this channel.
PubKey *btcec.PublicKey
// ShortChanID is the channel ID of the channel.
ShortChanID uint64
// FeeBaseMsat is the base fee in millisatoshis required for routing
// along this channel.
FeeBaseMsat uint32
// FeeProportionalMillionths is the proportional fee in millionths of a
// satoshi required for routing along this channel.
FeeProportionalMillionths uint32
// CltvExpDelta is this channel's cltv expiry delta.
CltvExpDelta uint16
// RouteHints represents one or more different route hints. Each route
// hint can be individually used to reach the destination. These usually
// represent private routes.
//
// NOTE: This is optional.
RouteHints [][]routing.HopHint
}
// Amount is a functional option that allows callers of NewInvoice to set the
@ -231,12 +212,11 @@ func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
}
}
// RoutingInfo is a functional option that allows callers of NewInvoice to set
// one or more entries containing extra routing information for a private route
// to the target node.
func RoutingInfo(routingInfo []ExtraRoutingInfo) func(*Invoice) {
// RouteHint is a functional option that allows callers of NewInvoice to add
// one or more hop hints that represent a private route to the destination.
func RouteHint(routeHint []routing.HopHint) func(*Invoice) {
return func(i *Invoice) {
i.RoutingInfo = routingInfo
i.RouteHints = append(i.RouteHints, routeHint)
}
}
@ -525,10 +505,19 @@ func validateInvoice(invoice *Invoice) error {
return fmt.Errorf("neither description nor description hash set")
}
// Can have at most 20 extra hops for routing.
if len(invoice.RoutingInfo) > 20 {
return fmt.Errorf("too many extra hops: %d",
len(invoice.RoutingInfo))
// We'll restrict invoices to include up to 20 different private route
// hints. We do this to avoid overly large invoices.
if len(invoice.RouteHints) > 20 {
return fmt.Errorf("too many private routes: %d",
len(invoice.RouteHints))
}
// Each route hint can have at most 20 hops.
for i, routeHint := range invoice.RouteHints {
if len(routeHint) > 20 {
return fmt.Errorf("route hint %d has too many extra "+
"hops: %d", i, len(routeHint))
}
}
// Check that we support the field lengths.
@ -666,13 +655,15 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
case fieldTypeR:
if invoice.RoutingInfo != nil {
// We skip the field if we have already seen a
// supported one.
continue
// An `r` field can be included in an invoice multiple
// times, so we won't skip it if we have already seen
// one.
routeHint, err := parseRouteHint(base32Data)
if err != nil {
return err
}
invoice.RoutingInfo, err = parseRoutingInfo(base32Data)
invoice.RouteHints = append(invoice.RouteHints, routeHint)
default:
// Ignore unknown type.
}
@ -850,35 +841,38 @@ func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, erro
return addr, nil
}
// parseRoutingInfo converts the data (encoded in base32) into an array
// containing one or more entries of extra routing info.
func parseRoutingInfo(data []byte) ([]ExtraRoutingInfo, error) {
// parseRouteHint converts the data (encoded in base32) into an array containing
// one or more routing hop hints that represent a single route hint.
func parseRouteHint(data []byte) ([]routing.HopHint, error) {
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
if err != nil {
return nil, err
}
if len(base256Data)%routingInfoLen != 0 {
return nil, fmt.Errorf("expected length multiple of %d bytes, got %d",
routingInfoLen, len(base256Data))
if len(base256Data)%hopHintLen != 0 {
return nil, fmt.Errorf("expected length multiple of %d bytes, "+
"got %d", hopHintLen, len(base256Data))
}
var routingInfo []ExtraRoutingInfo
info := ExtraRoutingInfo{}
var routeHint []routing.HopHint
for len(base256Data) > 0 {
info.PubKey, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
hopHint := routing.HopHint{}
hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
if err != nil {
return nil, err
}
info.ShortChanID = binary.BigEndian.Uint64(base256Data[33:41])
info.FeeBaseMsat = binary.BigEndian.Uint32(base256Data[41:45])
info.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
info.CltvExpDelta = binary.BigEndian.Uint16(base256Data[49:51])
routingInfo = append(routingInfo, info)
hopHint.ChannelID = binary.BigEndian.Uint64(base256Data[33:41])
hopHint.FeeBaseMSat = binary.BigEndian.Uint32(base256Data[41:45])
hopHint.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
hopHint.CLTVExpiryDelta = binary.BigEndian.Uint16(base256Data[49:51])
routeHint = append(routeHint, hopHint)
base256Data = base256Data[51:]
}
return routingInfo, nil
return routeHint, nil
}
// writeTaggedFields writes the non-nil tagged fields of the Invoice to the
@ -977,25 +971,37 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
}
}
if len(invoice.RoutingInfo) > 0 {
// Each extra routing info is encoded using 51 bytes.
routingDataBase256 := make([]byte, 0, 51*len(invoice.RoutingInfo))
for _, r := range invoice.RoutingInfo {
base256 := make([]byte, 51)
copy(base256[:33], r.PubKey.SerializeCompressed())
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
routingDataBase256 = append(routingDataBase256, base256...)
for _, routeHint := range invoice.RouteHints {
// Each hop hint is encoded using 51 bytes, so we'll make to
// sure to allocate enough space for the whole route hint.
routeHintBase256 := make([]byte, 0, hopHintLen*len(routeHint))
for _, hopHint := range routeHint {
hopHintBase256 := make([]byte, hopHintLen)
copy(hopHintBase256[:33], hopHint.NodeID.SerializeCompressed())
binary.BigEndian.PutUint64(
hopHintBase256[33:41], hopHint.ChannelID,
)
binary.BigEndian.PutUint32(
hopHintBase256[41:45], hopHint.FeeBaseMSat,
)
binary.BigEndian.PutUint32(
hopHintBase256[45:49], hopHint.FeeProportionalMillionths,
)
binary.BigEndian.PutUint16(
hopHintBase256[49:51], hopHint.CLTVExpiryDelta,
)
routeHintBase256 = append(routeHintBase256, hopHintBase256...)
}
routingDataBase32, err := bech32.ConvertBits(routingDataBase256,
8, 5, true)
routeHintBase32, err := bech32.ConvertBits(
routeHintBase256, 8, 5, true,
)
if err != nil {
return err
}
err = writeTaggedField(bufferBase32, fieldTypeR, routingDataBase32)
err = writeTaggedField(bufferBase32, fieldTypeR, routeHintBase32)
if err != nil {
return err
}

@ -8,6 +8,7 @@ import (
"time"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcutil"
@ -706,18 +707,18 @@ func TestParseFallbackAddr(t *testing.T) {
}
}
// TestParseRoutingInfo checks that the routing info is properly parsed.
func TestParseRoutingInfo(t *testing.T) {
// TestParseRouteHint checks that the routing info is properly parsed.
func TestParseRouteHint(t *testing.T) {
t.Parallel()
var testSingleHopData []byte
for _, r := range testSingleHop {
base256 := make([]byte, 51)
copy(base256[:33], r.PubKey.SerializeCompressed())
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
copy(base256[:33], r.NodeID.SerializeCompressed())
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
testSingleHopData = append(testSingleHopData, base256...)
}
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
@ -725,11 +726,11 @@ func TestParseRoutingInfo(t *testing.T) {
var testDoubleHopData []byte
for _, r := range testDoubleHop {
base256 := make([]byte, 51)
copy(base256[:33], r.PubKey.SerializeCompressed())
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
copy(base256[:33], r.NodeID.SerializeCompressed())
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
testDoubleHopData = append(testDoubleHopData, base256...)
}
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
@ -737,7 +738,7 @@ func TestParseRoutingInfo(t *testing.T) {
tests := []struct {
data []byte
valid bool
result []ExtraRoutingInfo
result []routing.HopHint
}{
{
data: []byte{0x0, 0x0, 0x0, 0x0},
@ -746,7 +747,7 @@ func TestParseRoutingInfo(t *testing.T) {
{
data: []byte{},
valid: true,
result: []ExtraRoutingInfo{},
result: []routing.HopHint{},
},
{
data: testSingleHopData,
@ -765,13 +766,13 @@ func TestParseRoutingInfo(t *testing.T) {
}
for i, test := range tests {
routingInfo, err := parseRoutingInfo(test.data)
routeHint, err := parseRouteHint(test.data)
if (err == nil) != test.valid {
t.Errorf("routing info decoding test %d failed: %v", i, err)
return
}
if test.valid {
if err := compareRoutingInfos(test.result, routingInfo); err != nil {
if err := compareRouteHints(test.result, routeHint); err != nil {
t.Fatalf("test %d failed decoding routing info: %v", i, err)
}
}

@ -12,6 +12,7 @@ import (
"time"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash"
@ -47,34 +48,34 @@ var (
testAddrMainnetP2WPKH, _ = btcutil.DecodeAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", &chaincfg.MainNetParams)
testAddrMainnetP2WSH, _ = btcutil.DecodeAddress("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", &chaincfg.MainNetParams)
testRoutingInfoPubkeyBytes, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
testRoutingInfoPubkey, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes, btcec.S256())
testRoutingInfoPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
testRoutingInfoPubkey2, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes2, btcec.S256())
testHopHintPubkeyBytes1, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
testHopHintPubkey1, _ = btcec.ParsePubKey(testHopHintPubkeyBytes1, btcec.S256())
testHopHintPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
testHopHintPubkey2, _ = btcec.ParsePubKey(testHopHintPubkeyBytes2, btcec.S256())
testSingleHop = []ExtraRoutingInfo{
testSingleHop = []routing.HopHint{
{
PubKey: testRoutingInfoPubkey,
ShortChanID: 0x0102030405060708,
FeeBaseMsat: 0,
NodeID: testHopHintPubkey1,
ChannelID: 0x0102030405060708,
FeeBaseMSat: 0,
FeeProportionalMillionths: 20,
CltvExpDelta: 3,
CLTVExpiryDelta: 3,
},
}
testDoubleHop = []ExtraRoutingInfo{
testDoubleHop = []routing.HopHint{
{
PubKey: testRoutingInfoPubkey,
ShortChanID: 0x0102030405060708,
FeeBaseMsat: 1,
NodeID: testHopHintPubkey1,
ChannelID: 0x0102030405060708,
FeeBaseMSat: 1,
FeeProportionalMillionths: 20,
CltvExpDelta: 3,
CLTVExpiryDelta: 3,
},
{
PubKey: testRoutingInfoPubkey2,
ShortChanID: 0x030405060708090a,
FeeBaseMsat: 2,
NodeID: testHopHintPubkey2,
ChannelID: 0x030405060708090a,
FeeBaseMSat: 2,
FeeProportionalMillionths: 30,
CltvExpDelta: 4,
CLTVExpiryDelta: 4,
},
}
@ -413,7 +414,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash,
Destination: testPubKey,
FallbackAddr: testRustyAddr,
RoutingInfo: testSingleHop,
RouteHints: [][]routing.HopHint{testSingleHop},
}
},
beforeEncoding: func(i *Invoice) {
@ -436,7 +437,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash,
Destination: testPubKey,
FallbackAddr: testRustyAddr,
RoutingInfo: testDoubleHop,
RouteHints: [][]routing.HopHint{testDoubleHop},
}
},
beforeEncoding: func(i *Invoice) {
@ -680,7 +681,7 @@ func TestNewInvoice(t *testing.T) {
Amount(testMillisat20mBTC),
DescriptionHash(testDescriptionHash),
FallbackAddr(testRustyAddr),
RoutingInfo(testDoubleHop),
RouteHint(testDoubleHop),
)
},
valid: true,
@ -802,7 +803,19 @@ func compareInvoices(expected, actual *Invoice) error {
expected.FallbackAddr, actual.FallbackAddr)
}
return compareRoutingInfos(expected.RoutingInfo, actual.RoutingInfo)
if len(expected.RouteHints) != len(actual.RouteHints) {
return fmt.Errorf("expected %d RouteHints, got %d",
len(expected.RouteHints), len(actual.RouteHints))
}
for i, routeHint := range expected.RouteHints {
err := compareRouteHints(routeHint, actual.RouteHints[i])
if err != nil {
return err
}
}
return nil
}
func comparePubkeys(a, b *btcec.PublicKey) bool {
@ -831,36 +844,36 @@ func compareHashes(a, b *[32]byte) bool {
return bytes.Equal(a[:], b[:])
}
func compareRoutingInfos(a, b []ExtraRoutingInfo) error {
func compareRouteHints(a, b []routing.HopHint) error {
if len(a) != len(b) {
return fmt.Errorf("expected len routingInfo %d, got %d",
len(a), len(b))
}
for i := 0; i < len(a); i++ {
if !comparePubkeys(a[i].PubKey, b[i].PubKey) {
return fmt.Errorf("expected routingInfo pubkey %x, "+
"got %x", a[i].PubKey, b[i].PubKey)
if !comparePubkeys(a[i].NodeID, b[i].NodeID) {
return fmt.Errorf("expected routeHint nodeID %x, "+
"got %x", a[i].NodeID, b[i].NodeID)
}
if a[i].ShortChanID != b[i].ShortChanID {
return fmt.Errorf("expected routingInfo shortChanID "+
"%d, got %d", a[i].ShortChanID, b[i].ShortChanID)
if a[i].ChannelID != b[i].ChannelID {
return fmt.Errorf("expected routeHint channelID "+
"%d, got %d", a[i].ChannelID, b[i].ChannelID)
}
if a[i].FeeBaseMsat != b[i].FeeBaseMsat {
return fmt.Errorf("expected routingInfo feeBaseMsat %d, got %d",
a[i].FeeBaseMsat, b[i].FeeBaseMsat)
if a[i].FeeBaseMSat != b[i].FeeBaseMSat {
return fmt.Errorf("expected routeHint feeBaseMsat %d, got %d",
a[i].FeeBaseMSat, b[i].FeeBaseMSat)
}
if a[i].FeeProportionalMillionths != b[i].FeeProportionalMillionths {
return fmt.Errorf("expected routingInfo feeProportionalMillionths %d, got %d",
return fmt.Errorf("expected routeHint feeProportionalMillionths %d, got %d",
a[i].FeeProportionalMillionths, b[i].FeeProportionalMillionths)
}
if a[i].CltvExpDelta != b[i].CltvExpDelta {
return fmt.Errorf("expected routingInfo cltvExpDelta "+
"%d, got %d", a[i].CltvExpDelta, b[i].CltvExpDelta)
if a[i].CLTVExpiryDelta != b[i].CLTVExpiryDelta {
return fmt.Errorf("expected routeHint cltvExpiryDelta "+
"%d, got %d", a[i].CLTVExpiryDelta, b[i].CLTVExpiryDelta)
}
}