Merge pull request #1113 from wpaulino/fee-cutoff

rpc+routing: add support for fee limits when finding routes for payments
This commit is contained in:
Olaoluwa Osuntokun 2018-06-12 19:34:23 -07:00 committed by GitHub
commit 26636ce994
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1525 additions and 772 deletions

View File

@ -1617,6 +1617,16 @@ var sendPaymentCommand = cli.Command{
Name: "amt, a",
Usage: "number of satoshis to send",
},
cli.Int64Flag{
Name: "fee_limit",
Usage: "maximum fee allowed in satoshis when sending" +
"the payment",
},
cli.Int64Flag{
Name: "fee_limit_percent",
Usage: "percentage of the payment's amount used as the" +
"maximum fee allowed when sending the payment",
},
cli.StringFlag{
Name: "payment_hash, r",
Usage: "the hash to use within the payment's HTLC",
@ -1637,6 +1647,32 @@ var sendPaymentCommand = cli.Command{
Action: sendPayment,
}
// retrieveFeeLimit retrieves the fee limit based on the different fee limit
// flags passed.
func retrieveFeeLimit(ctx *cli.Context) (*lnrpc.FeeLimit, error) {
switch {
case ctx.IsSet("fee_limit") && ctx.IsSet("fee_limit_percent"):
return nil, fmt.Errorf("either fee_limit or fee_limit_percent " +
"can be set, but not both")
case ctx.IsSet("fee_limit"):
return &lnrpc.FeeLimit{
Limit: &lnrpc.FeeLimit_Fixed{
Fixed: ctx.Int64("fee_limit"),
},
}, nil
case ctx.IsSet("fee_limit_percent"):
return &lnrpc.FeeLimit{
Limit: &lnrpc.FeeLimit_Percent{
Percent: ctx.Int64("fee_limit_percent"),
},
}, nil
}
// Since the fee limit flags aren't required, we don't return an error
// if they're not set.
return nil, nil
}
func sendPayment(ctx *cli.Context) error {
// Show command help if no arguments provided
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
@ -1644,87 +1680,99 @@ func sendPayment(ctx *cli.Context) error {
return nil
}
var req *lnrpc.SendRequest
// First, we'll retrieve the fee limit value passed since it can apply
// to both ways of sending payments (with the payment request or
// providing the details manually).
feeLimit, err := retrieveFeeLimit(ctx)
if err != nil {
return err
}
// If a payment request was provided, we can exit early since all of the
// details of the payment are encoded within the request.
if ctx.IsSet("pay_req") {
req = &lnrpc.SendRequest{
req := &lnrpc.SendRequest{
PaymentRequest: ctx.String("pay_req"),
Amt: ctx.Int64("amt"),
FeeLimit: feeLimit,
}
} else {
args := ctx.Args()
var (
destNode []byte
err error
amount int64
)
return sendPaymentRequest(ctx, req)
}
var (
destNode []byte
amount int64
)
args := ctx.Args()
switch {
case ctx.IsSet("dest"):
destNode, err = hex.DecodeString(ctx.String("dest"))
case args.Present():
destNode, err = hex.DecodeString(args.First())
args = args.Tail()
default:
return fmt.Errorf("destination txid argument missing")
}
if err != nil {
return err
}
if len(destNode) != 33 {
return fmt.Errorf("dest node pubkey must be exactly 33 bytes, is "+
"instead: %v", len(destNode))
}
if ctx.IsSet("amt") {
amount = ctx.Int64("amt")
} else if args.Present() {
amount, err = strconv.ParseInt(args.First(), 10, 64)
args = args.Tail()
if err != nil {
return fmt.Errorf("unable to decode payment amount: %v", err)
}
}
req := &lnrpc.SendRequest{
Dest: destNode,
Amt: amount,
FeeLimit: feeLimit,
}
if ctx.Bool("debug_send") && (ctx.IsSet("payment_hash") || args.Present()) {
return fmt.Errorf("do not provide a payment hash with debug send")
} else if !ctx.Bool("debug_send") {
var rHash []byte
switch {
case ctx.IsSet("dest"):
destNode, err = hex.DecodeString(ctx.String("dest"))
case ctx.IsSet("payment_hash"):
rHash, err = hex.DecodeString(ctx.String("payment_hash"))
case args.Present():
destNode, err = hex.DecodeString(args.First())
args = args.Tail()
rHash, err = hex.DecodeString(args.First())
default:
return fmt.Errorf("destination txid argument missing")
return fmt.Errorf("payment hash argument missing")
}
if err != nil {
return err
}
if len(destNode) != 33 {
return fmt.Errorf("dest node pubkey must be exactly 33 bytes, is "+
"instead: %v", len(destNode))
if len(rHash) != 32 {
return fmt.Errorf("payment hash must be exactly 32 "+
"bytes, is instead %v", len(rHash))
}
req.PaymentHash = rHash
if ctx.IsSet("amt") {
amount = ctx.Int64("amt")
} else if args.Present() {
amount, err = strconv.ParseInt(args.First(), 10, 64)
args = args.Tail()
if err != nil {
return fmt.Errorf("unable to decode payment amount: %v", err)
}
}
req = &lnrpc.SendRequest{
Dest: destNode,
Amt: amount,
}
if ctx.Bool("debug_send") && (ctx.IsSet("payment_hash") || args.Present()) {
return fmt.Errorf("do not provide a payment hash with debug send")
} else if !ctx.Bool("debug_send") {
var rHash []byte
switch {
case ctx.IsSet("payment_hash"):
rHash, err = hex.DecodeString(ctx.String("payment_hash"))
case args.Present():
rHash, err = hex.DecodeString(args.First())
default:
return fmt.Errorf("payment hash argument missing")
}
switch {
case ctx.IsSet("final_cltv_delta"):
req.FinalCltvDelta = int32(ctx.Int64("final_cltv_delta"))
case args.Present():
delta, err := strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return err
}
if len(rHash) != 32 {
return fmt.Errorf("payment hash must be exactly 32 "+
"bytes, is instead %v", len(rHash))
}
req.PaymentHash = rHash
switch {
case ctx.IsSet("final_cltv_delta"):
req.FinalCltvDelta = int32(ctx.Int64("final_cltv_delta"))
case args.Present():
delta, err := strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return err
}
req.FinalCltvDelta = int32(delta)
}
req.FinalCltvDelta = int32(delta)
}
}
@ -1779,6 +1827,16 @@ var payInvoiceCommand = cli.Command{
Usage: "(optional) number of satoshis to fulfill the " +
"invoice",
},
cli.Int64Flag{
Name: "fee_limit",
Usage: "maximum fee allowed in satoshis when sending" +
"the payment",
},
cli.Int64Flag{
Name: "fee_limit_percent",
Usage: "percentage of the payment's amount used as the" +
"maximum fee allowed when sending the payment",
},
},
Action: actionDecorator(payInvoice),
}
@ -1787,7 +1845,6 @@ func payInvoice(ctx *cli.Context) error {
args := ctx.Args()
var payReq string
switch {
case ctx.IsSet("pay_req"):
payReq = ctx.String("pay_req")
@ -1797,9 +1854,15 @@ func payInvoice(ctx *cli.Context) error {
return fmt.Errorf("pay_req argument missing")
}
feeLimit, err := retrieveFeeLimit(ctx)
if err != nil {
return err
}
req := &lnrpc.SendRequest{
PaymentRequest: payReq,
Amt: ctx.Int64("amt"),
FeeLimit: feeLimit,
}
return sendPaymentRequest(ctx, req)
@ -2499,6 +2562,16 @@ var queryRoutesCommand = cli.Command{
Name: "amt",
Usage: "the amount to send expressed in satoshis",
},
cli.Int64Flag{
Name: "fee_limit",
Usage: "maximum fee allowed in satoshis when sending" +
"the payment",
},
cli.Int64Flag{
Name: "fee_limit_percent",
Usage: "percentage of the payment's amount used as the" +
"maximum fee allowed when sending the payment",
},
cli.Int64Flag{
Name: "num_max_routes",
Usage: "the max number of routes to be returned (default: 10)",
@ -2548,9 +2621,15 @@ func queryRoutes(ctx *cli.Context) error {
return fmt.Errorf("amt argument missing")
}
feeLimit, err := retrieveFeeLimit(ctx)
if err != nil {
return err
}
req := &lnrpc.QueryRoutesRequest{
PubKey: dest,
Amt: amt,
FeeLimit: feeLimit,
NumRoutes: int32(ctx.Int("num_max_routes")),
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
}

View File

@ -674,6 +674,135 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
}
// txStr returns the string representation of the channel's funding transaction.
func txStr(chanPoint *lnrpc.ChannelPoint) string {
txidHash, err := getChanPointFundingTxid(chanPoint)
if err != nil {
return ""
}
fundingTxID, err := chainhash.NewHash(txidHash)
if err != nil {
return ""
}
cp := wire.OutPoint{
Hash: *fundingTxID,
Index: chanPoint.OutputIndex,
}
return cp.String()
}
// waitForChannelUpdate waits for a node to receive updates from the advertising
// node for the specified channels.
func waitForChannelUpdate(t *harnessTest, graphUpdates chan *lnrpc.GraphTopologyUpdate,
advertisingNode string, expectedPolicy *lnrpc.RoutingPolicy,
chanPoints ...*lnrpc.ChannelPoint) {
// Create a set containing all the channel points we are awaiting
// updates for.
cps := make(map[string]struct{})
for _, chanPoint := range chanPoints {
cps[txStr(chanPoint)] = struct{}{}
}
out:
for {
select {
case graphUpdate := <-graphUpdates:
for _, update := range graphUpdate.ChannelUpdates {
fundingTxStr := txStr(update.ChanPoint)
if _, ok := cps[fundingTxStr]; !ok {
continue
}
if update.AdvertisingNode != advertisingNode {
continue
}
err := checkChannelPolicy(
update.RoutingPolicy, expectedPolicy,
)
if err != nil {
continue
}
// We got a policy update that matched the
// values and channel point of what we
// expected, delete it from the map.
delete(cps, fundingTxStr)
// If we have no more channel points we are
// waiting for, break out of the loop.
if len(cps) == 0 {
break out
}
}
case <-time.After(20 * time.Second):
t.Fatalf("did not receive channel update")
}
}
}
// assertChannelPolicy asserts that the passed node's known channel policy for
// the passed chanPoint is consistent with the expected policy values.
func assertChannelPolicy(t *harnessTest, node *lntest.HarnessNode,
advertisingNode string, expectedPolicy *lnrpc.RoutingPolicy,
chanPoints ...*lnrpc.ChannelPoint) {
descReq := &lnrpc.ChannelGraphRequest{}
chanGraph, err := node.DescribeGraph(context.Background(), descReq)
if err != nil {
t.Fatalf("unable to query for alice's graph: %v", err)
}
out:
for _, chanPoint := range chanPoints {
for _, e := range chanGraph.Edges {
if e.ChanPoint != txStr(chanPoint) {
continue
}
var err error
if e.Node1Pub == advertisingNode {
err = checkChannelPolicy(
e.Node1Policy, expectedPolicy,
)
} else {
err = checkChannelPolicy(
e.Node2Policy, expectedPolicy,
)
}
if err != nil {
t.Fatalf(err.Error())
}
continue out
}
// If we've iterated over all the known edges and we weren't
// able to find this specific one, then we'll fail.
t.Fatalf("did not find edge %v", txStr(chanPoint))
}
}
// checkChannelPolicy checks that the policy matches the expected one.
func checkChannelPolicy(policy, expectedPolicy *lnrpc.RoutingPolicy) error {
if policy.FeeBaseMsat != expectedPolicy.FeeBaseMsat {
return fmt.Errorf("expected base fee %v, got %v",
expectedPolicy.FeeBaseMsat, policy.FeeBaseMsat)
}
if policy.FeeRateMilliMsat != expectedPolicy.FeeRateMilliMsat {
return fmt.Errorf("expected fee rate %v, got %v",
expectedPolicy.FeeRateMilliMsat,
policy.FeeRateMilliMsat)
}
if policy.TimeLockDelta != expectedPolicy.TimeLockDelta {
return fmt.Errorf("expected time lock delta %v, got %v",
expectedPolicy.TimeLockDelta,
policy.TimeLockDelta)
}
return nil
}
// testUpdateChannelPolicy tests that policy updates made to a channel
// gets propagated to other nodes in the network.
func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
@ -746,162 +875,46 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
feeRate := int64(12)
timeLockDelta := uint32(66)
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: baseFee,
FeeRateMilliMsat: feeBase * feeRate,
TimeLockDelta: timeLockDelta,
}
req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
}
req.Scope = &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
},
}
_, err = net.Bob.UpdateChannelPolicy(ctxb, req)
if err != nil {
if _, err := net.Bob.UpdateChannelPolicy(ctxb, req); err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// txStr returns the string representation of the channel's
// funding tx.
txStr := func(chanPoint *lnrpc.ChannelPoint) string {
txidHash, err := getChanPointFundingTxid(chanPoint)
if err != nil {
return ""
}
fundingTxID, err := chainhash.NewHash(txidHash)
if err != nil {
return ""
}
cp := wire.OutPoint{
Hash: *fundingTxID,
Index: chanPoint.OutputIndex,
}
return cp.String()
}
// A closure that is used to wait for a channel updates that matches
// the channel policy update done by Alice.
waitForChannelUpdate := func(graphUpdates chan *lnrpc.GraphTopologyUpdate,
advertisingNode string, chanPoints ...*lnrpc.ChannelPoint) {
// Create a map containing all the channel points we are
// waiting for updates for.
cps := make(map[string]bool)
for _, chanPoint := range chanPoints {
cps[txStr(chanPoint)] = true
}
Loop:
for {
select {
case graphUpdate := <-graphUpdates:
for _, update := range graphUpdate.ChannelUpdates {
fundingTxStr := txStr(update.ChanPoint)
if _, ok := cps[fundingTxStr]; !ok {
continue
}
if update.AdvertisingNode != advertisingNode {
continue
}
policy := update.RoutingPolicy
if policy.FeeBaseMsat != baseFee {
continue
}
if policy.FeeRateMilliMsat != feeRate*feeBase {
continue
}
if policy.TimeLockDelta != timeLockDelta {
continue
}
// We got a policy update that matched the
// values and channel point of what we
// expected, delete it from the map.
delete(cps, fundingTxStr)
// If we have no more channel points we are
// waiting for, break out of the loop.
if len(cps) == 0 {
break Loop
}
}
case <-time.After(20 * time.Second):
t.Fatalf("did not receive channel update")
}
}
}
// Wait for all nodes to have seen the policy update done by Bob.
waitForChannelUpdate(aliceUpdates, net.Bob.PubKeyStr, chanPoint)
waitForChannelUpdate(bobUpdates, net.Bob.PubKeyStr, chanPoint)
waitForChannelUpdate(carolUpdates, net.Bob.PubKeyStr, chanPoint)
// assertChannelPolicy asserts that the passed node's known channel
// policy for the passed chanPoint is consistent with Bob's current
// expected policy values.
assertChannelPolicy := func(node *lntest.HarnessNode,
advertisingNode string, chanPoint *lnrpc.ChannelPoint) {
// Get a DescribeGraph from the node.
descReq := &lnrpc.ChannelGraphRequest{}
chanGraph, err := node.DescribeGraph(ctxb, descReq)
if err != nil {
t.Fatalf("unable to query for alice's routing table: %v",
err)
}
edgeFound := false
for _, e := range chanGraph.Edges {
if e.ChanPoint == txStr(chanPoint) {
edgeFound = true
if e.Node1Pub == advertisingNode {
if e.Node1Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node1Policy.FeeBaseMsat)
}
if e.Node1Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node1Policy.FeeRateMilliMsat)
}
if e.Node1Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node1Policy.TimeLockDelta)
}
} else {
if e.Node2Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node2Policy.FeeBaseMsat)
}
if e.Node2Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node2Policy.FeeRateMilliMsat)
}
if e.Node2Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node2Policy.TimeLockDelta)
}
}
}
}
if !edgeFound {
t.Fatalf("did not find edge")
}
}
waitForChannelUpdate(
t, aliceUpdates, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
waitForChannelUpdate(
t, bobUpdates, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
waitForChannelUpdate(
t, carolUpdates, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
// Check that all nodes now know about Bob's updated policy.
assertChannelPolicy(net.Alice, net.Bob.PubKeyStr, chanPoint)
assertChannelPolicy(net.Bob, net.Bob.PubKeyStr, chanPoint)
assertChannelPolicy(carol, net.Bob.PubKeyStr, chanPoint)
assertChannelPolicy(
t, net.Alice, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
assertChannelPolicy(
t, net.Bob, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
assertChannelPolicy(
t, carol, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
// Now that all nodes have received the new channel update, we'll try
// to send a payment from Alice to Carol to ensure that Alice has
@ -951,6 +964,10 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
feeRate = int64(123)
timeLockDelta = uint32(22)
expectedPolicy.FeeBaseMsat = baseFee
expectedPolicy.FeeRateMilliMsat = feeBase * feeRate
expectedPolicy.TimeLockDelta = timeLockDelta
req = &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
@ -966,24 +983,32 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
// Wait for all nodes to have seen the policy updates for both of
// Alice's channels.
waitForChannelUpdate(
aliceUpdates, net.Alice.PubKeyStr, chanPoint, chanPoint3,
t, aliceUpdates, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
waitForChannelUpdate(
bobUpdates, net.Alice.PubKeyStr, chanPoint, chanPoint3,
t, bobUpdates, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
waitForChannelUpdate(
carolUpdates, net.Alice.PubKeyStr, chanPoint, chanPoint3,
t, carolUpdates, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
// And finally check that all nodes remembers the policy update they
// received.
assertChannelPolicy(net.Alice, net.Alice.PubKeyStr, chanPoint)
assertChannelPolicy(net.Bob, net.Alice.PubKeyStr, chanPoint)
assertChannelPolicy(carol, net.Alice.PubKeyStr, chanPoint)
assertChannelPolicy(net.Alice, net.Alice.PubKeyStr, chanPoint3)
assertChannelPolicy(net.Bob, net.Alice.PubKeyStr, chanPoint3)
assertChannelPolicy(carol, net.Alice.PubKeyStr, chanPoint3)
assertChannelPolicy(
t, net.Alice, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
assertChannelPolicy(
t, net.Bob, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
assertChannelPolicy(
t, carol, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
chanPoint3,
)
// Close the channels.
ctxt, _ = context.WithTimeout(ctxb, timeout)
@ -9736,6 +9761,278 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
}
}
// testRouteFeeCutoff tests that we are able to prevent querying routes and
// sending payments that incur a fee higher than the fee limit.
func testRouteFeeCutoff(net *lntest.NetworkHarness, t *harnessTest) {
// For this test, we'll create the following topology:
//
// --- Bob ---
// / \
// Alice ---- ---- Dave
// \ /
// -- Carol --
//
// Alice will attempt to send payments to Dave that should not incur a
// fee greater than the fee limit expressed as a percentage of the
// amount and as a fixed amount of satoshis.
ctxb := context.Background()
timeout := time.Duration(time.Second * 15)
const chanAmt = btcutil.Amount(100000)
// Open a channel between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPointAliceBob := openChannelAndAssert(
ctxt, t, net, net.Alice, net.Bob, chanAmt, 0, false,
)
// Create Carol's node and open a channel between her and Alice with
// Alice being the funder.
carol, err := net.NewNode("Carol", nil)
if err != nil {
t.Fatalf("unable to create carol's node: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
if err := net.ConnectNodes(ctxt, carol, net.Alice); err != nil {
t.Fatalf("unable to connect carol to alice: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol)
if err != nil {
t.Fatalf("unable to send coins to carol: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointAliceCarol := openChannelAndAssert(
ctxt, t, net, net.Alice, carol, chanAmt, 0, false,
)
// Create Dave's node and open a channel between him and Bob with Bob
// being the funder.
dave, err := net.NewNode("Dave", nil)
if err != nil {
t.Fatalf("unable to create dave's node: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
if err := net.ConnectNodes(ctxt, dave, net.Bob); err != nil {
t.Fatalf("unable to connect dave to bob: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointBobDave := openChannelAndAssert(
ctxt, t, net, net.Bob, dave, chanAmt, 0, false,
)
// Open a channel between Carol and Dave.
ctxt, _ = context.WithTimeout(ctxb, timeout)
if err := net.ConnectNodes(ctxt, carol, dave); err != nil {
t.Fatalf("unable to connect carol to dave: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPointCarolDave := openChannelAndAssert(
ctxt, t, net, carol, dave, chanAmt, 0, false,
)
// Now that all the channels were set up, we'll wait for all the nodes
// to have seen all the channels.
nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol, dave}
nodeNames := []string{"alice", "bob", "carol", "dave"}
networkChans := []*lnrpc.ChannelPoint{
chanPointAliceBob, chanPointAliceCarol, chanPointBobDave,
chanPointCarolDave,
}
for _, chanPoint := range networkChans {
for i, node := range nodes {
txidHash, err := getChanPointFundingTxid(chanPoint)
if err != nil {
t.Fatalf("unable to get txid: %v", err)
}
txid, e := chainhash.NewHash(txidHash)
if e != nil {
t.Fatalf("unable to create sha hash: %v", e)
}
outpoint := wire.OutPoint{
Hash: *txid,
Index: chanPoint.OutputIndex,
}
ctxt, _ := context.WithTimeout(ctxb, timeout)
err = node.WaitForNetworkChannelOpen(ctxt, chanPoint)
if err != nil {
t.Fatalf("%s(%d) timed out waiting for "+
"channel(%s) open: %v", nodeNames[i],
node.NodeID, outpoint, err)
}
}
}
// The payments should only be succesful across the route:
// Alice -> Bob -> Dave
// Therefore, we'll update the fee policy on Carol's side for the
// channel between her and Dave to invalidate the route:
// Alice -> Carol -> Dave
const feeBase = 1e+6
baseFee := int64(10000)
feeRate := int64(5)
timeLockDelta := uint32(144)
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: baseFee,
FeeRateMilliMsat: feeBase * feeRate,
TimeLockDelta: timeLockDelta,
}
updateFeeReq := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPointCarolDave,
},
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
if _, err := carol.UpdateChannelPolicy(ctxt, updateFeeReq); err != nil {
t.Fatalf("unable to update chan policy: %v", err)
}
// Wait for Alice to receive the channel update from Carol.
ctxt, _ = context.WithTimeout(ctxb, timeout)
aliceUpdates, aQuit := subscribeGraphNotifications(t, ctxt, net.Alice)
defer close(aQuit)
waitForChannelUpdate(
t, aliceUpdates, carol.PubKeyStr, expectedPolicy,
chanPointCarolDave,
)
// We'll also need the channel IDs for Bob's channels in order to
// confirm the route of the payments.
listReq := &lnrpc.ListChannelsRequest{}
ctxt, _ = context.WithTimeout(ctxb, timeout)
listResp, err := net.Bob.ListChannels(ctxt, listReq)
if err != nil {
t.Fatalf("unable to retrieve bob's channels: %v", err)
}
var aliceBobChanID, bobDaveChanID uint64
for _, channel := range listResp.Channels {
switch channel.RemotePubkey {
case net.Alice.PubKeyStr:
aliceBobChanID = channel.ChanId
case dave.PubKeyStr:
bobDaveChanID = channel.ChanId
}
}
if aliceBobChanID == 0 {
t.Fatalf("channel between alice and bob not found")
}
if bobDaveChanID == 0 {
t.Fatalf("channel between bob and dave not found")
}
hopChanIDs := []uint64{aliceBobChanID, bobDaveChanID}
// checkRoute is a helper closure to ensure the route contains the
// correct intermediate hops.
checkRoute := func(route *lnrpc.Route) {
if len(route.Hops) != 2 {
t.Fatalf("expected two hops, got %d", len(route.Hops))
}
for i, hop := range route.Hops {
if hop.ChanId != hopChanIDs[i] {
t.Fatalf("expected chan id %d, got %d",
hop.ChanId)
}
}
}
// We'll be attempting to send two payments from Alice to Dave. One will
// have a fee cutoff expressed as a percentage of the amount and the
// other will have it expressed as a fixed amount of satoshis.
const paymentAmt = 100
carolFee := computeFee(lnwire.MilliSatoshi(baseFee), 1, paymentAmt)
// testFeeCutoff is a helper closure that will ensure the different
// types of fee limits work as intended when querying routes and sending
// payments.
testFeeCutoff := func(feeLimit *lnrpc.FeeLimit) {
queryRoutesReq := &lnrpc.QueryRoutesRequest{
PubKey: dave.PubKeyStr,
Amt: paymentAmt,
FeeLimit: feeLimit,
NumRoutes: 2,
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
routesResp, err := net.Alice.QueryRoutes(ctxt, queryRoutesReq)
if err != nil {
t.Fatalf("unable to get routes: %v", err)
}
if len(routesResp.Routes) != 1 {
t.Fatalf("expected one route, got %d",
len(routesResp.Routes))
}
checkRoute(routesResp.Routes[0])
invoice := &lnrpc.Invoice{Value: paymentAmt}
ctxt, _ = context.WithTimeout(ctxb, timeout)
invoiceResp, err := dave.AddInvoice(ctxt, invoice)
if err != nil {
t.Fatalf("unable to create invoice: %v", err)
}
sendReq := &lnrpc.SendRequest{
PaymentRequest: invoiceResp.PaymentRequest,
FeeLimit: feeLimit,
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
paymentResp, err := net.Alice.SendPaymentSync(ctxt, sendReq)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
if paymentResp.PaymentError != "" {
t.Fatalf("unable to send payment: %v",
paymentResp.PaymentError)
}
checkRoute(paymentResp.PaymentRoute)
}
// We'll start off using percentages first. Since the fee along the
// route using Carol as an intermediate hop is 10% of the payment's
// amount, we'll use a lower percentage in order to invalid that route.
feeLimitPercent := &lnrpc.FeeLimit{
&lnrpc.FeeLimit_Percent{baseFee/1000 - 1},
}
testFeeCutoff(feeLimitPercent)
// Now we'll test using fixed fee limit amounts. Since we computed the
// fee for the route using Carol as an intermediate hop earlier, we can
// use a smaller value in order to invalidate that route.
feeLimitFixed := &lnrpc.FeeLimit{
&lnrpc.FeeLimit_Fixed{int64(carolFee.ToSatoshis()) - 1},
}
testFeeCutoff(feeLimitFixed)
// Once we're done, close the channels and shut down the nodes created
// throughout this test.
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAliceBob, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAliceCarol, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Bob, chanPointBobDave, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarolDave, false)
if err := net.ShutdownNode(carol); err != nil {
t.Fatalf("unable to shut down carol: %v", err)
}
if err := net.ShutdownNode(dave); err != nil {
t.Fatalf("unable to shut down dave: %v", err)
}
}
type testCase struct {
name string
test func(net *lntest.NetworkHarness, t *harnessTest)
@ -9927,6 +10224,10 @@ var testsCases = []*testCase{
name: "query routes",
test: testQueryRoutes,
},
{
name: "route fee cutoff",
test: testRouteFeeCutoff,
},
}
// TestLightningNetworkDaemon performs a series of integration tests amongst a

View File

@ -19,6 +19,7 @@ It has these top-level messages:
Transaction
GetTransactionsRequest
TransactionDetails
FeeLimit
SendRequest
SendResponse
SendToRouteRequest
@ -154,7 +155,7 @@ func (x NewAddressRequest_AddressType) String() string {
return proto.EnumName(NewAddressRequest_AddressType_name, int32(x))
}
func (NewAddressRequest_AddressType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor0, []int{20, 0}
return fileDescriptor0, []int{21, 0}
}
type GenSeedRequest struct {
@ -470,6 +471,115 @@ func (m *TransactionDetails) GetTransactions() []*Transaction {
return nil
}
type FeeLimit struct {
// Types that are valid to be assigned to Limit:
// *FeeLimit_Fixed
// *FeeLimit_Percent
Limit isFeeLimit_Limit `protobuf_oneof:"limit"`
}
func (m *FeeLimit) Reset() { *m = FeeLimit{} }
func (m *FeeLimit) String() string { return proto.CompactTextString(m) }
func (*FeeLimit) ProtoMessage() {}
func (*FeeLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
type isFeeLimit_Limit interface{ isFeeLimit_Limit() }
type FeeLimit_Fixed struct {
Fixed int64 `protobuf:"varint,1,opt,name=fixed,oneof"`
}
type FeeLimit_Percent struct {
Percent int64 `protobuf:"varint,2,opt,name=percent,oneof"`
}
func (*FeeLimit_Fixed) isFeeLimit_Limit() {}
func (*FeeLimit_Percent) isFeeLimit_Limit() {}
func (m *FeeLimit) GetLimit() isFeeLimit_Limit {
if m != nil {
return m.Limit
}
return nil
}
func (m *FeeLimit) GetFixed() int64 {
if x, ok := m.GetLimit().(*FeeLimit_Fixed); ok {
return x.Fixed
}
return 0
}
func (m *FeeLimit) GetPercent() int64 {
if x, ok := m.GetLimit().(*FeeLimit_Percent); ok {
return x.Percent
}
return 0
}
// XXX_OneofFuncs is for the internal use of the proto package.
func (*FeeLimit) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _FeeLimit_OneofMarshaler, _FeeLimit_OneofUnmarshaler, _FeeLimit_OneofSizer, []interface{}{
(*FeeLimit_Fixed)(nil),
(*FeeLimit_Percent)(nil),
}
}
func _FeeLimit_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*FeeLimit)
// limit
switch x := m.Limit.(type) {
case *FeeLimit_Fixed:
b.EncodeVarint(1<<3 | proto.WireVarint)
b.EncodeVarint(uint64(x.Fixed))
case *FeeLimit_Percent:
b.EncodeVarint(2<<3 | proto.WireVarint)
b.EncodeVarint(uint64(x.Percent))
case nil:
default:
return fmt.Errorf("FeeLimit.Limit has unexpected type %T", x)
}
return nil
}
func _FeeLimit_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*FeeLimit)
switch tag {
case 1: // limit.fixed
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Limit = &FeeLimit_Fixed{int64(x)}
return true, err
case 2: // limit.percent
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Limit = &FeeLimit_Percent{int64(x)}
return true, err
default:
return false, nil
}
}
func _FeeLimit_OneofSizer(msg proto.Message) (n int) {
m := msg.(*FeeLimit)
// limit
switch x := m.Limit.(type) {
case *FeeLimit_Fixed:
n += proto.SizeVarint(1<<3 | proto.WireVarint)
n += proto.SizeVarint(uint64(x.Fixed))
case *FeeLimit_Percent:
n += proto.SizeVarint(2<<3 | proto.WireVarint)
n += proto.SizeVarint(uint64(x.Percent))
case nil:
default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
}
return n
}
type SendRequest struct {
// / The identity pubkey of the payment recipient
Dest []byte `protobuf:"bytes,1,opt,name=dest,proto3" json:"dest,omitempty"`
@ -486,14 +596,22 @@ type SendRequest struct {
// details of the invoice, the sender has all the data necessary to send a
// payment to the recipient.
PaymentRequest string `protobuf:"bytes,6,opt,name=payment_request,json=paymentRequest" json:"payment_request,omitempty"`
// / The CLTV delta from the current height that should be used to set the timelock for the final hop.
// *
// The CLTV delta from the current height that should be used to set the
// timelock for the final hop.
FinalCltvDelta int32 `protobuf:"varint,7,opt,name=final_cltv_delta,json=finalCltvDelta" json:"final_cltv_delta,omitempty"`
// *
// The maximum number of satoshis that will be paid as a fee of the payment.
// This value can be represented either as a percentage of the amount being
// sent, or as a fixed amount of the maximum fee the user is willing the pay to
// send the payment.
FeeLimit *FeeLimit `protobuf:"bytes,8,opt,name=fee_limit,json=feeLimit" json:"fee_limit,omitempty"`
}
func (m *SendRequest) Reset() { *m = SendRequest{} }
func (m *SendRequest) String() string { return proto.CompactTextString(m) }
func (*SendRequest) ProtoMessage() {}
func (*SendRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (*SendRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *SendRequest) GetDest() []byte {
if m != nil {
@ -544,6 +662,13 @@ func (m *SendRequest) GetFinalCltvDelta() int32 {
return 0
}
func (m *SendRequest) GetFeeLimit() *FeeLimit {
if m != nil {
return m.FeeLimit
}
return nil
}
type SendResponse struct {
PaymentError string `protobuf:"bytes,1,opt,name=payment_error" json:"payment_error,omitempty"`
PaymentPreimage []byte `protobuf:"bytes,2,opt,name=payment_preimage,proto3" json:"payment_preimage,omitempty"`
@ -553,7 +678,7 @@ type SendResponse struct {
func (m *SendResponse) Reset() { *m = SendResponse{} }
func (m *SendResponse) String() string { return proto.CompactTextString(m) }
func (*SendResponse) ProtoMessage() {}
func (*SendResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (*SendResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (m *SendResponse) GetPaymentError() string {
if m != nil {
@ -588,7 +713,7 @@ type SendToRouteRequest struct {
func (m *SendToRouteRequest) Reset() { *m = SendToRouteRequest{} }
func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) }
func (*SendToRouteRequest) ProtoMessage() {}
func (*SendToRouteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (*SendToRouteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (m *SendToRouteRequest) GetPaymentHash() []byte {
if m != nil {
@ -623,11 +748,9 @@ type ChannelPoint struct {
func (m *ChannelPoint) Reset() { *m = ChannelPoint{} }
func (m *ChannelPoint) String() string { return proto.CompactTextString(m) }
func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
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"`
@ -743,7 +866,7 @@ type LightningAddress struct {
func (m *LightningAddress) Reset() { *m = LightningAddress{} }
func (m *LightningAddress) String() string { return proto.CompactTextString(m) }
func (*LightningAddress) ProtoMessage() {}
func (*LightningAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
func (*LightningAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *LightningAddress) GetPubkey() string {
if m != nil {
@ -771,7 +894,7 @@ type SendManyRequest struct {
func (m *SendManyRequest) Reset() { *m = SendManyRequest{} }
func (m *SendManyRequest) String() string { return proto.CompactTextString(m) }
func (*SendManyRequest) ProtoMessage() {}
func (*SendManyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (*SendManyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
func (m *SendManyRequest) GetAddrToAmount() map[string]int64 {
if m != nil {
@ -802,7 +925,7 @@ type SendManyResponse struct {
func (m *SendManyResponse) Reset() { *m = SendManyResponse{} }
func (m *SendManyResponse) String() string { return proto.CompactTextString(m) }
func (*SendManyResponse) ProtoMessage() {}
func (*SendManyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
func (*SendManyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (m *SendManyResponse) GetTxid() string {
if m != nil {
@ -825,7 +948,7 @@ type SendCoinsRequest struct {
func (m *SendCoinsRequest) Reset() { *m = SendCoinsRequest{} }
func (m *SendCoinsRequest) String() string { return proto.CompactTextString(m) }
func (*SendCoinsRequest) ProtoMessage() {}
func (*SendCoinsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (*SendCoinsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} }
func (m *SendCoinsRequest) GetAddr() string {
if m != nil {
@ -863,7 +986,7 @@ type SendCoinsResponse struct {
func (m *SendCoinsResponse) Reset() { *m = SendCoinsResponse{} }
func (m *SendCoinsResponse) String() string { return proto.CompactTextString(m) }
func (*SendCoinsResponse) ProtoMessage() {}
func (*SendCoinsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} }
func (*SendCoinsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
func (m *SendCoinsResponse) GetTxid() string {
if m != nil {
@ -886,7 +1009,7 @@ type NewAddressRequest struct {
func (m *NewAddressRequest) Reset() { *m = NewAddressRequest{} }
func (m *NewAddressRequest) String() string { return proto.CompactTextString(m) }
func (*NewAddressRequest) ProtoMessage() {}
func (*NewAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
func (*NewAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
func (m *NewAddressRequest) GetType() NewAddressRequest_AddressType {
if m != nil {
@ -901,7 +1024,7 @@ type NewWitnessAddressRequest struct {
func (m *NewWitnessAddressRequest) Reset() { *m = NewWitnessAddressRequest{} }
func (m *NewWitnessAddressRequest) String() string { return proto.CompactTextString(m) }
func (*NewWitnessAddressRequest) ProtoMessage() {}
func (*NewWitnessAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
func (*NewWitnessAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} }
type NewAddressResponse struct {
// / The newly generated wallet address
@ -911,7 +1034,7 @@ type NewAddressResponse struct {
func (m *NewAddressResponse) Reset() { *m = NewAddressResponse{} }
func (m *NewAddressResponse) String() string { return proto.CompactTextString(m) }
func (*NewAddressResponse) ProtoMessage() {}
func (*NewAddressResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} }
func (*NewAddressResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} }
func (m *NewAddressResponse) GetAddress() string {
if m != nil {
@ -928,7 +1051,7 @@ type SignMessageRequest struct {
func (m *SignMessageRequest) Reset() { *m = SignMessageRequest{} }
func (m *SignMessageRequest) String() string { return proto.CompactTextString(m) }
func (*SignMessageRequest) ProtoMessage() {}
func (*SignMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} }
func (*SignMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} }
func (m *SignMessageRequest) GetMsg() []byte {
if m != nil {
@ -945,7 +1068,7 @@ type SignMessageResponse struct {
func (m *SignMessageResponse) Reset() { *m = SignMessageResponse{} }
func (m *SignMessageResponse) String() string { return proto.CompactTextString(m) }
func (*SignMessageResponse) ProtoMessage() {}
func (*SignMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} }
func (*SignMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} }
func (m *SignMessageResponse) GetSignature() string {
if m != nil {
@ -964,7 +1087,7 @@ type VerifyMessageRequest struct {
func (m *VerifyMessageRequest) Reset() { *m = VerifyMessageRequest{} }
func (m *VerifyMessageRequest) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageRequest) ProtoMessage() {}
func (*VerifyMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} }
func (*VerifyMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} }
func (m *VerifyMessageRequest) GetMsg() []byte {
if m != nil {
@ -990,7 +1113,7 @@ type VerifyMessageResponse struct {
func (m *VerifyMessageResponse) Reset() { *m = VerifyMessageResponse{} }
func (m *VerifyMessageResponse) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageResponse) ProtoMessage() {}
func (*VerifyMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} }
func (*VerifyMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} }
func (m *VerifyMessageResponse) GetValid() bool {
if m != nil {
@ -1017,7 +1140,7 @@ type ConnectPeerRequest struct {
func (m *ConnectPeerRequest) Reset() { *m = ConnectPeerRequest{} }
func (m *ConnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerRequest) ProtoMessage() {}
func (*ConnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} }
func (*ConnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} }
func (m *ConnectPeerRequest) GetAddr() *LightningAddress {
if m != nil {
@ -1039,7 +1162,7 @@ type ConnectPeerResponse struct {
func (m *ConnectPeerResponse) Reset() { *m = ConnectPeerResponse{} }
func (m *ConnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerResponse) ProtoMessage() {}
func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} }
func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
type DisconnectPeerRequest struct {
// / The pubkey of the node to disconnect from
@ -1049,7 +1172,7 @@ type DisconnectPeerRequest struct {
func (m *DisconnectPeerRequest) Reset() { *m = DisconnectPeerRequest{} }
func (m *DisconnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerRequest) ProtoMessage() {}
func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
func (m *DisconnectPeerRequest) GetPubKey() string {
if m != nil {
@ -1064,7 +1187,7 @@ type DisconnectPeerResponse struct {
func (m *DisconnectPeerResponse) Reset() { *m = DisconnectPeerResponse{} }
func (m *DisconnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerResponse) ProtoMessage() {}
func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
type HTLC struct {
Incoming bool `protobuf:"varint,1,opt,name=incoming" json:"incoming,omitempty"`
@ -1076,7 +1199,7 @@ type HTLC struct {
func (m *HTLC) Reset() { *m = HTLC{} }
func (m *HTLC) String() string { return proto.CompactTextString(m) }
func (*HTLC) ProtoMessage() {}
func (*HTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
func (*HTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
func (m *HTLC) GetIncoming() bool {
if m != nil {
@ -1166,7 +1289,7 @@ type Channel struct {
func (m *Channel) Reset() { *m = Channel{} }
func (m *Channel) String() string { return proto.CompactTextString(m) }
func (*Channel) ProtoMessage() {}
func (*Channel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
func (*Channel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
func (m *Channel) GetActive() bool {
if m != nil {
@ -1297,7 +1420,7 @@ type ListChannelsRequest struct {
func (m *ListChannelsRequest) Reset() { *m = ListChannelsRequest{} }
func (m *ListChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*ListChannelsRequest) ProtoMessage() {}
func (*ListChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
func (*ListChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} }
func (m *ListChannelsRequest) GetActiveOnly() bool {
if m != nil {
@ -1335,7 +1458,7 @@ type ListChannelsResponse struct {
func (m *ListChannelsResponse) Reset() { *m = ListChannelsResponse{} }
func (m *ListChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*ListChannelsResponse) ProtoMessage() {}
func (*ListChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} }
func (*ListChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} }
func (m *ListChannelsResponse) GetChannels() []*Channel {
if m != nil {
@ -1366,7 +1489,7 @@ type Peer struct {
func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} }
func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} }
func (m *Peer) GetPubKey() string {
if m != nil {
@ -1430,7 +1553,7 @@ type ListPeersRequest struct {
func (m *ListPeersRequest) Reset() { *m = ListPeersRequest{} }
func (m *ListPeersRequest) String() string { return proto.CompactTextString(m) }
func (*ListPeersRequest) ProtoMessage() {}
func (*ListPeersRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} }
func (*ListPeersRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} }
type ListPeersResponse struct {
// / The list of currently connected peers
@ -1440,7 +1563,7 @@ type ListPeersResponse struct {
func (m *ListPeersResponse) Reset() { *m = ListPeersResponse{} }
func (m *ListPeersResponse) String() string { return proto.CompactTextString(m) }
func (*ListPeersResponse) ProtoMessage() {}
func (*ListPeersResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} }
func (*ListPeersResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} }
func (m *ListPeersResponse) GetPeers() []*Peer {
if m != nil {
@ -1455,7 +1578,7 @@ type GetInfoRequest struct {
func (m *GetInfoRequest) Reset() { *m = GetInfoRequest{} }
func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) }
func (*GetInfoRequest) ProtoMessage() {}
func (*GetInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} }
func (*GetInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} }
type GetInfoResponse struct {
// / The identity pubkey of the current node.
@ -1489,7 +1612,7 @@ type GetInfoResponse struct {
func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} }
func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) }
func (*GetInfoResponse) ProtoMessage() {}
func (*GetInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} }
func (*GetInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} }
func (m *GetInfoResponse) GetIdentityPubkey() string {
if m != nil {
@ -1591,7 +1714,7 @@ type ConfirmationUpdate struct {
func (m *ConfirmationUpdate) Reset() { *m = ConfirmationUpdate{} }
func (m *ConfirmationUpdate) String() string { return proto.CompactTextString(m) }
func (*ConfirmationUpdate) ProtoMessage() {}
func (*ConfirmationUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} }
func (*ConfirmationUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
func (m *ConfirmationUpdate) GetBlockSha() []byte {
if m != nil {
@ -1621,7 +1744,7 @@ type ChannelOpenUpdate struct {
func (m *ChannelOpenUpdate) Reset() { *m = ChannelOpenUpdate{} }
func (m *ChannelOpenUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelOpenUpdate) ProtoMessage() {}
func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} }
func (m *ChannelOpenUpdate) GetChannelPoint() *ChannelPoint {
if m != nil {
@ -1638,7 +1761,7 @@ type ChannelCloseUpdate struct {
func (m *ChannelCloseUpdate) Reset() { *m = ChannelCloseUpdate{} }
func (m *ChannelCloseUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelCloseUpdate) ProtoMessage() {}
func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} }
func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} }
func (m *ChannelCloseUpdate) GetClosingTxid() []byte {
if m != nil {
@ -1671,7 +1794,7 @@ type CloseChannelRequest struct {
func (m *CloseChannelRequest) Reset() { *m = CloseChannelRequest{} }
func (m *CloseChannelRequest) String() string { return proto.CompactTextString(m) }
func (*CloseChannelRequest) ProtoMessage() {}
func (*CloseChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} }
func (*CloseChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
func (m *CloseChannelRequest) GetChannelPoint() *ChannelPoint {
if m != nil {
@ -1712,11 +1835,9 @@ type CloseStatusUpdate struct {
func (m *CloseStatusUpdate) Reset() { *m = CloseStatusUpdate{} }
func (m *CloseStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{45} }
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"`
@ -1861,7 +1982,7 @@ type PendingUpdate struct {
func (m *PendingUpdate) Reset() { *m = PendingUpdate{} }
func (m *PendingUpdate) String() string { return proto.CompactTextString(m) }
func (*PendingUpdate) ProtoMessage() {}
func (*PendingUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{45} }
func (*PendingUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{46} }
func (m *PendingUpdate) GetTxid() []byte {
if m != nil {
@ -1901,7 +2022,7 @@ type OpenChannelRequest struct {
func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} }
func (m *OpenChannelRequest) String() string { return proto.CompactTextString(m) }
func (*OpenChannelRequest) ProtoMessage() {}
func (*OpenChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{46} }
func (*OpenChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} }
func (m *OpenChannelRequest) GetNodePubkey() []byte {
if m != nil {
@ -1977,11 +2098,9 @@ type OpenStatusUpdate struct {
func (m *OpenStatusUpdate) Reset() { *m = OpenStatusUpdate{} }
func (m *OpenStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} }
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} }
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"`
@ -2139,7 +2258,7 @@ type PendingHTLC struct {
func (m *PendingHTLC) Reset() { *m = PendingHTLC{} }
func (m *PendingHTLC) String() string { return proto.CompactTextString(m) }
func (*PendingHTLC) ProtoMessage() {}
func (*PendingHTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} }
func (*PendingHTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{49} }
func (m *PendingHTLC) GetIncoming() bool {
if m != nil {
@ -2189,7 +2308,7 @@ type PendingChannelsRequest struct {
func (m *PendingChannelsRequest) Reset() { *m = PendingChannelsRequest{} }
func (m *PendingChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsRequest) ProtoMessage() {}
func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{49} }
func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} }
type PendingChannelsResponse struct {
// / The balance in satoshis encumbered in pending channels
@ -2207,7 +2326,7 @@ type PendingChannelsResponse struct {
func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} }
func (m *PendingChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse) ProtoMessage() {}
func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} }
func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} }
func (m *PendingChannelsResponse) GetTotalLimboBalance() int64 {
if m != nil {
@ -2258,7 +2377,7 @@ func (m *PendingChannelsResponse_PendingChannel) Reset() {
func (m *PendingChannelsResponse_PendingChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{50, 0}
return fileDescriptor0, []int{51, 0}
}
func (m *PendingChannelsResponse_PendingChannel) GetRemoteNodePub() string {
@ -2325,7 +2444,7 @@ func (m *PendingChannelsResponse_PendingOpenChannel) String() string {
}
func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{50, 1}
return fileDescriptor0, []int{51, 1}
}
func (m *PendingChannelsResponse_PendingOpenChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -2378,7 +2497,7 @@ func (m *PendingChannelsResponse_WaitingCloseChannel) String() string {
}
func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {}
func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{50, 2}
return fileDescriptor0, []int{51, 2}
}
func (m *PendingChannelsResponse_WaitingCloseChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -2406,7 +2525,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{50, 3}
return fileDescriptor0, []int{51, 3}
}
func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -2450,7 +2569,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string {
}
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{50, 4}
return fileDescriptor0, []int{51, 4}
}
func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -2508,7 +2627,7 @@ type WalletBalanceRequest struct {
func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} }
func (m *WalletBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceRequest) ProtoMessage() {}
func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} }
func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{52} }
type WalletBalanceResponse struct {
// / The balance of the wallet
@ -2522,7 +2641,7 @@ type WalletBalanceResponse struct {
func (m *WalletBalanceResponse) Reset() { *m = WalletBalanceResponse{} }
func (m *WalletBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceResponse) ProtoMessage() {}
func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{52} }
func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} }
func (m *WalletBalanceResponse) GetTotalBalance() int64 {
if m != nil {
@ -2551,7 +2670,7 @@ type ChannelBalanceRequest struct {
func (m *ChannelBalanceRequest) Reset() { *m = ChannelBalanceRequest{} }
func (m *ChannelBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceRequest) ProtoMessage() {}
func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} }
func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{54} }
type ChannelBalanceResponse struct {
// / Sum of channels balances denominated in satoshis
@ -2563,7 +2682,7 @@ type ChannelBalanceResponse struct {
func (m *ChannelBalanceResponse) Reset() { *m = ChannelBalanceResponse{} }
func (m *ChannelBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceResponse) ProtoMessage() {}
func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{54} }
func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{55} }
func (m *ChannelBalanceResponse) GetBalance() int64 {
if m != nil {
@ -2588,12 +2707,18 @@ type QueryRoutesRequest struct {
NumRoutes int32 `protobuf:"varint,3,opt,name=num_routes,json=numRoutes" json:"num_routes,omitempty"`
// / An optional CLTV delta from the current height that should be used for the timelock of the final hop
FinalCltvDelta int32 `protobuf:"varint,4,opt,name=final_cltv_delta,json=finalCltvDelta" json:"final_cltv_delta,omitempty"`
// *
// The maximum number of satoshis that will be paid as a fee of the payment.
// This value can be represented either as a percentage of the amount being
// sent, or as a fixed amount of the maximum fee the user is willing the pay to
// send the payment.
FeeLimit *FeeLimit `protobuf:"bytes,5,opt,name=fee_limit,json=feeLimit" json:"fee_limit,omitempty"`
}
func (m *QueryRoutesRequest) Reset() { *m = QueryRoutesRequest{} }
func (m *QueryRoutesRequest) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesRequest) ProtoMessage() {}
func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{55} }
func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} }
func (m *QueryRoutesRequest) GetPubKey() string {
if m != nil {
@ -2623,6 +2748,13 @@ func (m *QueryRoutesRequest) GetFinalCltvDelta() int32 {
return 0
}
func (m *QueryRoutesRequest) GetFeeLimit() *FeeLimit {
if m != nil {
return m.FeeLimit
}
return nil
}
type QueryRoutesResponse struct {
Routes []*Route `protobuf:"bytes,1,rep,name=routes" json:"routes,omitempty"`
}
@ -2630,7 +2762,7 @@ type QueryRoutesResponse struct {
func (m *QueryRoutesResponse) Reset() { *m = QueryRoutesResponse{} }
func (m *QueryRoutesResponse) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesResponse) ProtoMessage() {}
func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} }
func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} }
func (m *QueryRoutesResponse) GetRoutes() []*Route {
if m != nil {
@ -2656,7 +2788,7 @@ type Hop struct {
func (m *Hop) Reset() { *m = Hop{} }
func (m *Hop) String() string { return proto.CompactTextString(m) }
func (*Hop) ProtoMessage() {}
func (*Hop) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} }
func (*Hop) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{58} }
func (m *Hop) GetChanId() uint64 {
if m != nil {
@ -2746,7 +2878,7 @@ type Route struct {
func (m *Route) Reset() { *m = Route{} }
func (m *Route) String() string { return proto.CompactTextString(m) }
func (*Route) ProtoMessage() {}
func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{58} }
func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{59} }
func (m *Route) GetTotalTimeLock() uint32 {
if m != nil {
@ -2798,7 +2930,7 @@ type NodeInfoRequest struct {
func (m *NodeInfoRequest) Reset() { *m = NodeInfoRequest{} }
func (m *NodeInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NodeInfoRequest) ProtoMessage() {}
func (*NodeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{59} }
func (*NodeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{60} }
func (m *NodeInfoRequest) GetPubKey() string {
if m != nil {
@ -2821,7 +2953,7 @@ type NodeInfo struct {
func (m *NodeInfo) Reset() { *m = NodeInfo{} }
func (m *NodeInfo) String() string { return proto.CompactTextString(m) }
func (*NodeInfo) ProtoMessage() {}
func (*NodeInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{60} }
func (*NodeInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{61} }
func (m *NodeInfo) GetNode() *LightningNode {
if m != nil {
@ -2860,7 +2992,7 @@ type LightningNode struct {
func (m *LightningNode) Reset() { *m = LightningNode{} }
func (m *LightningNode) String() string { return proto.CompactTextString(m) }
func (*LightningNode) ProtoMessage() {}
func (*LightningNode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{61} }
func (*LightningNode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{62} }
func (m *LightningNode) GetLastUpdate() uint32 {
if m != nil {
@ -2905,7 +3037,7 @@ type NodeAddress struct {
func (m *NodeAddress) Reset() { *m = NodeAddress{} }
func (m *NodeAddress) String() string { return proto.CompactTextString(m) }
func (*NodeAddress) ProtoMessage() {}
func (*NodeAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{62} }
func (*NodeAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{63} }
func (m *NodeAddress) GetNetwork() string {
if m != nil {
@ -2931,7 +3063,7 @@ type RoutingPolicy struct {
func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} }
func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) }
func (*RoutingPolicy) ProtoMessage() {}
func (*RoutingPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{63} }
func (*RoutingPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} }
func (m *RoutingPolicy) GetTimeLockDelta() uint32 {
if m != nil {
@ -2985,7 +3117,7 @@ type ChannelEdge struct {
func (m *ChannelEdge) Reset() { *m = ChannelEdge{} }
func (m *ChannelEdge) String() string { return proto.CompactTextString(m) }
func (*ChannelEdge) ProtoMessage() {}
func (*ChannelEdge) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} }
func (*ChannelEdge) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{65} }
func (m *ChannelEdge) GetChannelId() uint64 {
if m != nil {
@ -3049,7 +3181,7 @@ type ChannelGraphRequest struct {
func (m *ChannelGraphRequest) Reset() { *m = ChannelGraphRequest{} }
func (m *ChannelGraphRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelGraphRequest) ProtoMessage() {}
func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{65} }
func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{66} }
// / Returns a new instance of the directed channel graph.
type ChannelGraph struct {
@ -3062,7 +3194,7 @@ type ChannelGraph struct {
func (m *ChannelGraph) Reset() { *m = ChannelGraph{} }
func (m *ChannelGraph) String() string { return proto.CompactTextString(m) }
func (*ChannelGraph) ProtoMessage() {}
func (*ChannelGraph) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{66} }
func (*ChannelGraph) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{67} }
func (m *ChannelGraph) GetNodes() []*LightningNode {
if m != nil {
@ -3089,7 +3221,7 @@ type ChanInfoRequest struct {
func (m *ChanInfoRequest) Reset() { *m = ChanInfoRequest{} }
func (m *ChanInfoRequest) String() string { return proto.CompactTextString(m) }
func (*ChanInfoRequest) ProtoMessage() {}
func (*ChanInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{67} }
func (*ChanInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} }
func (m *ChanInfoRequest) GetChanId() uint64 {
if m != nil {
@ -3104,7 +3236,7 @@ type NetworkInfoRequest struct {
func (m *NetworkInfoRequest) Reset() { *m = NetworkInfoRequest{} }
func (m *NetworkInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NetworkInfoRequest) ProtoMessage() {}
func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} }
func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} }
type NetworkInfo struct {
GraphDiameter uint32 `protobuf:"varint,1,opt,name=graph_diameter" json:"graph_diameter,omitempty"`
@ -3121,7 +3253,7 @@ type NetworkInfo struct {
func (m *NetworkInfo) Reset() { *m = NetworkInfo{} }
func (m *NetworkInfo) String() string { return proto.CompactTextString(m) }
func (*NetworkInfo) ProtoMessage() {}
func (*NetworkInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} }
func (*NetworkInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{70} }
func (m *NetworkInfo) GetGraphDiameter() uint32 {
if m != nil {
@ -3192,7 +3324,7 @@ type StopRequest struct {
func (m *StopRequest) Reset() { *m = StopRequest{} }
func (m *StopRequest) String() string { return proto.CompactTextString(m) }
func (*StopRequest) ProtoMessage() {}
func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{70} }
func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{71} }
type StopResponse struct {
}
@ -3200,7 +3332,7 @@ type StopResponse struct {
func (m *StopResponse) Reset() { *m = StopResponse{} }
func (m *StopResponse) String() string { return proto.CompactTextString(m) }
func (*StopResponse) ProtoMessage() {}
func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{71} }
func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{72} }
type GraphTopologySubscription struct {
}
@ -3208,7 +3340,7 @@ type GraphTopologySubscription struct {
func (m *GraphTopologySubscription) Reset() { *m = GraphTopologySubscription{} }
func (m *GraphTopologySubscription) String() string { return proto.CompactTextString(m) }
func (*GraphTopologySubscription) ProtoMessage() {}
func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{72} }
func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{73} }
type GraphTopologyUpdate struct {
NodeUpdates []*NodeUpdate `protobuf:"bytes,1,rep,name=node_updates,json=nodeUpdates" json:"node_updates,omitempty"`
@ -3219,7 +3351,7 @@ type GraphTopologyUpdate struct {
func (m *GraphTopologyUpdate) Reset() { *m = GraphTopologyUpdate{} }
func (m *GraphTopologyUpdate) String() string { return proto.CompactTextString(m) }
func (*GraphTopologyUpdate) ProtoMessage() {}
func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{73} }
func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} }
func (m *GraphTopologyUpdate) GetNodeUpdates() []*NodeUpdate {
if m != nil {
@ -3252,7 +3384,7 @@ type NodeUpdate struct {
func (m *NodeUpdate) Reset() { *m = NodeUpdate{} }
func (m *NodeUpdate) String() string { return proto.CompactTextString(m) }
func (*NodeUpdate) ProtoMessage() {}
func (*NodeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} }
func (*NodeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} }
func (m *NodeUpdate) GetAddresses() []string {
if m != nil {
@ -3298,7 +3430,7 @@ type ChannelEdgeUpdate struct {
func (m *ChannelEdgeUpdate) Reset() { *m = ChannelEdgeUpdate{} }
func (m *ChannelEdgeUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelEdgeUpdate) ProtoMessage() {}
func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} }
func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} }
func (m *ChannelEdgeUpdate) GetChanId() uint64 {
if m != nil {
@ -3356,7 +3488,7 @@ type ClosedChannelUpdate struct {
func (m *ClosedChannelUpdate) Reset() { *m = ClosedChannelUpdate{} }
func (m *ClosedChannelUpdate) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelUpdate) ProtoMessage() {}
func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} }
func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} }
func (m *ClosedChannelUpdate) GetChanId() uint64 {
if m != nil {
@ -3404,7 +3536,7 @@ type HopHint struct {
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{77} }
func (*HopHint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} }
func (m *HopHint) GetNodeId() string {
if m != nil {
@ -3451,7 +3583,7 @@ type RouteHint struct {
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{78} }
func (*RouteHint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} }
func (m *RouteHint) GetHopHints() []*HopHint {
if m != nil {
@ -3510,7 +3642,7 @@ type Invoice struct {
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{79} }
func (*Invoice) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} }
func (m *Invoice) GetMemo() string {
if m != nil {
@ -3629,7 +3761,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{80} }
func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} }
func (m *AddInvoiceResponse) GetRHash() []byte {
if m != nil {
@ -3657,7 +3789,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{81} }
func (*PaymentHash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} }
func (m *PaymentHash) GetRHashStr() string {
if m != nil {
@ -3681,7 +3813,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{82} }
func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} }
func (m *ListInvoiceRequest) GetPendingOnly() bool {
if m != nil {
@ -3697,7 +3829,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{83} }
func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} }
func (m *ListInvoiceResponse) GetInvoices() []*Invoice {
if m != nil {
@ -3712,7 +3844,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{84} }
func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} }
type Payment struct {
// / The payment hash
@ -3732,7 +3864,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{85} }
func (*Payment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} }
func (m *Payment) GetPaymentHash() string {
if m != nil {
@ -3782,7 +3914,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{86} }
func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} }
type ListPaymentsResponse struct {
// / The list of payments
@ -3792,7 +3924,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{87} }
func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} }
func (m *ListPaymentsResponse) GetPayments() []*Payment {
if m != nil {
@ -3807,7 +3939,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{88} }
func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} }
type DeleteAllPaymentsResponse struct {
}
@ -3815,7 +3947,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{89} }
func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} }
type DebugLevelRequest struct {
Show bool `protobuf:"varint,1,opt,name=show" json:"show,omitempty"`
@ -3825,7 +3957,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{90} }
func (*DebugLevelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} }
func (m *DebugLevelRequest) GetShow() bool {
if m != nil {
@ -3848,7 +3980,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{91} }
func (*DebugLevelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{92} }
func (m *DebugLevelResponse) GetSubSystems() string {
if m != nil {
@ -3865,7 +3997,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{92} }
func (*PayReqString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} }
func (m *PayReqString) GetPayReq() string {
if m != nil {
@ -3890,7 +4022,7 @@ type PayReq struct {
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{93} }
func (*PayReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
func (m *PayReq) GetDestination() string {
if m != nil {
@ -3968,7 +4100,7 @@ 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{94} }
func (*FeeReportRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{95} }
type ChannelFeeReport struct {
// / The channel that this fee report belongs to.
@ -3984,7 +4116,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{95} }
func (*ChannelFeeReport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{96} }
func (m *ChannelFeeReport) GetChanPoint() string {
if m != nil {
@ -4028,7 +4160,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{96} }
func (*FeeReportResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{97} }
func (m *FeeReportResponse) GetChannelFees() []*ChannelFeeReport {
if m != nil {
@ -4074,11 +4206,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{97} }
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{98} }
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"`
@ -4211,7 +4341,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{98} }
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{99} }
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.
@ -4227,7 +4357,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{99} }
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{100} }
func (m *ForwardingHistoryRequest) GetStartTime() uint64 {
if m != nil {
@ -4275,7 +4405,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{100} }
func (*ForwardingEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{101} }
func (m *ForwardingEvent) GetTimestamp() uint64 {
if m != nil {
@ -4329,7 +4459,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{101} }
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{102} }
func (m *ForwardingHistoryResponse) GetForwardingEvents() []*ForwardingEvent {
if m != nil {
@ -4357,6 +4487,7 @@ func init() {
proto.RegisterType((*Transaction)(nil), "lnrpc.Transaction")
proto.RegisterType((*GetTransactionsRequest)(nil), "lnrpc.GetTransactionsRequest")
proto.RegisterType((*TransactionDetails)(nil), "lnrpc.TransactionDetails")
proto.RegisterType((*FeeLimit)(nil), "lnrpc.FeeLimit")
proto.RegisterType((*SendRequest)(nil), "lnrpc.SendRequest")
proto.RegisterType((*SendResponse)(nil), "lnrpc.SendResponse")
proto.RegisterType((*SendToRouteRequest)(nil), "lnrpc.SendToRouteRequest")
@ -6620,367 +6751,372 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 5786 bytes of a gzipped FileDescriptorProto
// 5859 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x3c, 0x4b, 0x90, 0x1c, 0xc9,
0x55, 0xaa, 0x9e, 0x9e, 0x4f, 0xbf, 0xee, 0xe9, 0x99, 0xc9, 0x19, 0xcd, 0xb4, 0x7a, 0xb5, 0x5a,
0x6d, 0x59, 0x61, 0xc9, 0xc3, 0xa2, 0xd1, 0x8e, 0xed, 0x65, 0x2d, 0x81, 0x8d, 0xfe, 0xb3, 0xb6,
0x56, 0x1e, 0xd7, 0x48, 0x16, 0xd8, 0x10, 0xed, 0x9a, 0xee, 0x9c, 0x9e, 0xb2, 0xba, 0xab, 0xca,
0x55, 0xd5, 0x33, 0x6a, 0x2f, 0x8a, 0xe0, 0x17, 0x04, 0x07, 0x1c, 0x04, 0x81, 0x2f, 0x26, 0x82,
0x20, 0xc2, 0x5c, 0xe0, 0xc0, 0x91, 0x93, 0xe1, 0xc6, 0x89, 0x08, 0x82, 0xc3, 0x9e, 0x1c, 0x1c,
0x81, 0x03, 0x38, 0xb8, 0x10, 0xc1, 0x85, 0x03, 0x41, 0xbc, 0x97, 0x9f, 0xca, 0xac, 0xaa, 0x96,
0xc6, 0x1f, 0xb8, 0x75, 0xbe, 0x7c, 0x95, 0xf9, 0x32, 0xf3, 0xbd, 0x97, 0xef, 0x97, 0x0d, 0x8d,
0x24, 0xee, 0x5f, 0x8f, 0x93, 0x28, 0x8b, 0xd8, 0xfc, 0x28, 0x4c, 0xe2, 0x7e, 0xf7, 0xe2, 0x30,
0x8a, 0x86, 0x23, 0xbe, 0xe3, 0xc7, 0xc1, 0x8e, 0x1f, 0x86, 0x51, 0xe6, 0x67, 0x41, 0x14, 0xa6,
0x02, 0xc9, 0xfd, 0x06, 0xb4, 0x1f, 0xf2, 0xf0, 0x80, 0xf3, 0x81, 0xc7, 0xbf, 0x35, 0xe1, 0x69,
0xc6, 0x7e, 0x0e, 0xd6, 0x7c, 0xfe, 0x6d, 0xce, 0x07, 0xbd, 0xd8, 0x4f, 0xd3, 0xf8, 0x38, 0xf1,
0x53, 0xde, 0x71, 0x2e, 0x3b, 0xd7, 0x5a, 0xde, 0xaa, 0xe8, 0xd8, 0xd7, 0x70, 0xf6, 0x36, 0xb4,
0x52, 0x44, 0xe5, 0x61, 0x96, 0x44, 0xf1, 0xb4, 0x53, 0x23, 0xbc, 0x26, 0xc2, 0xee, 0x0b, 0x90,
0x3b, 0x82, 0x15, 0x3d, 0x43, 0x1a, 0x47, 0x61, 0xca, 0xd9, 0x0d, 0xd8, 0xe8, 0x07, 0xf1, 0x31,
0x4f, 0x7a, 0xf4, 0xf1, 0x38, 0xe4, 0xe3, 0x28, 0x0c, 0xfa, 0x1d, 0xe7, 0xf2, 0xdc, 0xb5, 0x86,
0xc7, 0x44, 0x1f, 0x7e, 0xf1, 0xa1, 0xec, 0x61, 0x57, 0x61, 0x85, 0x87, 0x02, 0xce, 0x07, 0xf4,
0x95, 0x9c, 0xaa, 0x9d, 0x83, 0xf1, 0x03, 0xf7, 0xef, 0x1c, 0x58, 0xfb, 0x20, 0x0c, 0xb2, 0x67,
0xfe, 0x68, 0xc4, 0x33, 0xb5, 0xa6, 0xab, 0xb0, 0x72, 0x4a, 0x00, 0x5a, 0xd3, 0x69, 0x94, 0x0c,
0xe4, 0x8a, 0xda, 0x02, 0xbc, 0x2f, 0xa1, 0x33, 0x29, 0xab, 0xcd, 0xa4, 0xac, 0x72, 0xbb, 0xe6,
0x66, 0x6c, 0xd7, 0x55, 0x58, 0x49, 0x78, 0x3f, 0x3a, 0xe1, 0xc9, 0xb4, 0x77, 0x1a, 0x84, 0x83,
0xe8, 0xb4, 0x53, 0xbf, 0xec, 0x5c, 0x9b, 0xf7, 0xda, 0x0a, 0xfc, 0x8c, 0xa0, 0xee, 0x06, 0x30,
0x73, 0x15, 0x62, 0xdf, 0xdc, 0x21, 0xac, 0x3f, 0x0d, 0x47, 0x51, 0xff, 0xf9, 0x4f, 0xb8, 0xba,
0x8a, 0xe9, 0x6b, 0x95, 0xd3, 0x6f, 0xc2, 0x86, 0x3d, 0x91, 0x24, 0x80, 0xc3, 0xf9, 0xbb, 0xc7,
0x7e, 0x38, 0xe4, 0x6a, 0x48, 0x45, 0xc2, 0xa7, 0x60, 0xb5, 0x3f, 0x49, 0x12, 0x1e, 0x96, 0x68,
0x58, 0x91, 0x70, 0x4d, 0xc4, 0xdb, 0xd0, 0x0a, 0xf9, 0x69, 0x8e, 0x26, 0x59, 0x26, 0xe4, 0xa7,
0x0a, 0xc5, 0xed, 0xc0, 0x66, 0x71, 0x1a, 0x49, 0xc0, 0xf7, 0x6a, 0xd0, 0x7c, 0x92, 0xf8, 0x61,
0xea, 0xf7, 0x91, 0x8b, 0x59, 0x07, 0x16, 0xb3, 0x17, 0xbd, 0x63, 0x3f, 0x3d, 0xa6, 0xe9, 0x1a,
0x9e, 0x6a, 0xb2, 0x4d, 0x58, 0xf0, 0xc7, 0xd1, 0x24, 0xcc, 0x68, 0x82, 0x39, 0x4f, 0xb6, 0xd8,
0x3b, 0xb0, 0x16, 0x4e, 0xc6, 0xbd, 0x7e, 0x14, 0x1e, 0x05, 0xc9, 0x58, 0xc8, 0x02, 0x9d, 0xd7,
0xbc, 0x57, 0xee, 0x60, 0x97, 0x00, 0x0e, 0x71, 0x1f, 0xc4, 0x14, 0x75, 0x9a, 0xc2, 0x80, 0x30,
0x17, 0x5a, 0xb2, 0xc5, 0x83, 0xe1, 0x71, 0xd6, 0x99, 0xa7, 0x81, 0x2c, 0x18, 0x8e, 0x91, 0x05,
0x63, 0xde, 0x4b, 0x33, 0x7f, 0x1c, 0x77, 0x16, 0x88, 0x1a, 0x03, 0x42, 0xfd, 0x51, 0xe6, 0x8f,
0x7a, 0x47, 0x9c, 0xa7, 0x9d, 0x45, 0xd9, 0xaf, 0x21, 0xec, 0x93, 0xd0, 0x1e, 0xf0, 0x34, 0xeb,
0xf9, 0x83, 0x41, 0xc2, 0xd3, 0x94, 0xa7, 0x9d, 0x25, 0xe2, 0xc6, 0x02, 0x14, 0x77, 0xed, 0x21,
0xcf, 0x8c, 0xdd, 0x49, 0xe5, 0xe9, 0xb8, 0x8f, 0x80, 0x19, 0xe0, 0x7b, 0x3c, 0xf3, 0x83, 0x51,
0xca, 0xde, 0x83, 0x56, 0x66, 0x20, 0x93, 0xf4, 0x35, 0x77, 0xd9, 0x75, 0x52, 0x1b, 0xd7, 0x8d,
0x0f, 0x3c, 0x0b, 0xcf, 0xfd, 0x6f, 0x07, 0x9a, 0x07, 0x3c, 0xd4, 0x67, 0xcf, 0xa0, 0x8e, 0x94,
0xc8, 0xf3, 0xa6, 0xdf, 0xec, 0x2d, 0x68, 0x12, 0x75, 0x69, 0x96, 0x04, 0xe1, 0x90, 0x8e, 0xa0,
0xe1, 0x01, 0x82, 0x0e, 0x08, 0xc2, 0x56, 0x61, 0xce, 0x1f, 0x67, 0xb4, 0xf1, 0x73, 0x1e, 0xfe,
0x44, 0xbe, 0x88, 0xfd, 0xe9, 0x18, 0x59, 0x48, 0x6f, 0x76, 0xcb, 0x6b, 0x4a, 0xd8, 0x1e, 0xee,
0xf6, 0x75, 0x58, 0x37, 0x51, 0xd4, 0xe8, 0xf3, 0x34, 0xfa, 0x9a, 0x81, 0x29, 0x27, 0xb9, 0x0a,
0x2b, 0x0a, 0x3f, 0x11, 0xc4, 0xd2, 0xf6, 0x37, 0xbc, 0xb6, 0x04, 0xab, 0x25, 0x5c, 0x83, 0xd5,
0xa3, 0x20, 0xf4, 0x47, 0xbd, 0xfe, 0x28, 0x3b, 0xe9, 0x0d, 0xf8, 0x28, 0xf3, 0xe9, 0x20, 0xe6,
0xbd, 0x36, 0xc1, 0xef, 0x8e, 0xb2, 0x93, 0x7b, 0x08, 0x75, 0xbf, 0xeb, 0x40, 0x4b, 0x2c, 0x5e,
0xea, 0xb2, 0x2b, 0xb0, 0xac, 0xe6, 0xe0, 0x49, 0x12, 0x25, 0x92, 0x0f, 0x6d, 0x20, 0xdb, 0x86,
0x55, 0x05, 0x88, 0x13, 0x1e, 0x8c, 0xfd, 0x21, 0x97, 0x8c, 0x5f, 0x82, 0xb3, 0xdd, 0x7c, 0xc4,
0x24, 0x9a, 0x64, 0x42, 0x9b, 0x34, 0x77, 0x5b, 0xf2, 0x60, 0x3c, 0x84, 0x79, 0x36, 0x8a, 0xfb,
0x1d, 0x07, 0x18, 0x92, 0xf5, 0x24, 0x12, 0xdd, 0x72, 0x5d, 0xc5, 0x3d, 0x75, 0xce, 0xbc, 0xa7,
0xb5, 0x59, 0x7b, 0x7a, 0x05, 0x16, 0x68, 0x4a, 0x14, 0x9a, 0xb9, 0x12, 0x59, 0xb2, 0xcf, 0xfd,
0xbe, 0x03, 0x2d, 0x14, 0xe1, 0x90, 0x8f, 0xf6, 0xa3, 0x20, 0xcc, 0xd8, 0x0d, 0x60, 0x47, 0x93,
0x70, 0x10, 0x84, 0xc3, 0x5e, 0xf6, 0x22, 0x18, 0xf4, 0x0e, 0xa7, 0x38, 0x04, 0xd1, 0xb3, 0x77,
0xce, 0xab, 0xe8, 0x63, 0xef, 0xc0, 0xaa, 0x05, 0x4d, 0xb3, 0x44, 0x50, 0xb5, 0x77, 0xce, 0x2b,
0xf5, 0xa0, 0x20, 0x46, 0x93, 0x2c, 0x9e, 0x64, 0xbd, 0x20, 0x1c, 0xf0, 0x17, 0xb4, 0x67, 0xcb,
0x9e, 0x05, 0xbb, 0xd3, 0x86, 0x96, 0xf9, 0x9d, 0xfb, 0x79, 0x58, 0x7d, 0x84, 0x12, 0x1a, 0x06,
0xe1, 0xf0, 0xb6, 0x10, 0x23, 0x54, 0x1b, 0xf1, 0xe4, 0xf0, 0x39, 0x9f, 0xca, 0x73, 0x94, 0x2d,
0x64, 0xf2, 0xe3, 0x28, 0xcd, 0xe4, 0xbe, 0xd0, 0x6f, 0xf7, 0x9f, 0x1d, 0x58, 0xc1, 0x4d, 0xff,
0xd0, 0x0f, 0xa7, 0x6a, 0xc7, 0x1f, 0x41, 0x0b, 0x87, 0x7a, 0x12, 0xdd, 0x16, 0xca, 0x47, 0x08,
0xd5, 0x35, 0xb9, 0x49, 0x05, 0xec, 0xeb, 0x26, 0x2a, 0xde, 0x97, 0x53, 0xcf, 0xfa, 0x1a, 0xc5,
0x28, 0xf3, 0x93, 0x21, 0xcf, 0x48, 0x2d, 0x49, 0x35, 0x05, 0x02, 0x74, 0x37, 0x0a, 0x8f, 0xd8,
0x65, 0x68, 0xa5, 0x7e, 0xd6, 0x8b, 0x79, 0x42, 0xbb, 0x46, 0xa2, 0x30, 0xe7, 0x41, 0xea, 0x67,
0xfb, 0x3c, 0xb9, 0x33, 0xcd, 0x78, 0xf7, 0x0b, 0xb0, 0x56, 0x9a, 0x05, 0xa5, 0x2f, 0x5f, 0x22,
0xfe, 0x64, 0x1b, 0x30, 0x7f, 0xe2, 0x8f, 0x26, 0x5c, 0x6a, 0x4b, 0xd1, 0xb8, 0x59, 0x7b, 0xdf,
0x71, 0x3f, 0x09, 0xab, 0x39, 0xd9, 0x92, 0xe9, 0x19, 0xd4, 0x71, 0x07, 0xe5, 0x00, 0xf4, 0xdb,
0xfd, 0x2d, 0x47, 0x20, 0xde, 0x8d, 0x02, 0xad, 0x79, 0x10, 0x11, 0x15, 0x94, 0x42, 0xc4, 0xdf,
0x33, 0x35, 0xf3, 0x4f, 0xbf, 0x58, 0xf7, 0x2a, 0xac, 0x19, 0x24, 0xbc, 0x82, 0xd8, 0xef, 0x38,
0xb0, 0xf6, 0x98, 0x9f, 0xca, 0x53, 0x57, 0xd4, 0xbe, 0x0f, 0xf5, 0x6c, 0x1a, 0x0b, 0x6b, 0xa7,
0xbd, 0x7b, 0x45, 0x1e, 0x5a, 0x09, 0xef, 0xba, 0x6c, 0x3e, 0x99, 0xc6, 0xdc, 0xa3, 0x2f, 0xdc,
0xcf, 0x43, 0xd3, 0x00, 0xb2, 0x2d, 0x58, 0x7f, 0xf6, 0xc1, 0x93, 0xc7, 0xf7, 0x0f, 0x0e, 0x7a,
0xfb, 0x4f, 0xef, 0x7c, 0xe9, 0xfe, 0xaf, 0xf6, 0xf6, 0x6e, 0x1f, 0xec, 0xad, 0x9e, 0x63, 0x9b,
0xc0, 0x1e, 0xdf, 0x3f, 0x78, 0x72, 0xff, 0x9e, 0x05, 0x77, 0xdc, 0x2e, 0x74, 0x1e, 0xf3, 0xd3,
0x67, 0x41, 0x16, 0xf2, 0x34, 0xb5, 0x67, 0x73, 0xaf, 0x03, 0x33, 0x49, 0x90, 0xab, 0xea, 0xc0,
0xa2, 0x54, 0xfd, 0xea, 0xe6, 0x93, 0x4d, 0xf7, 0x93, 0xc0, 0x0e, 0x82, 0x61, 0xf8, 0x21, 0x4f,
0x53, 0x7f, 0xa8, 0x55, 0xc1, 0x2a, 0xcc, 0x8d, 0xd3, 0xa1, 0xd4, 0x00, 0xf8, 0xd3, 0xfd, 0x34,
0xac, 0x5b, 0x78, 0x72, 0xe0, 0x8b, 0xd0, 0x48, 0x83, 0x61, 0xe8, 0x67, 0x93, 0x84, 0xcb, 0xa1,
0x73, 0x80, 0xfb, 0x00, 0x36, 0xbe, 0xca, 0x93, 0xe0, 0x68, 0xfa, 0xba, 0xe1, 0xed, 0x71, 0x6a,
0xc5, 0x71, 0xee, 0xc3, 0xf9, 0xc2, 0x38, 0x72, 0x7a, 0xc1, 0x88, 0xf2, 0xb8, 0x96, 0x3c, 0xd1,
0x30, 0xc4, 0xb2, 0x66, 0x8a, 0xa5, 0xfb, 0x14, 0xd8, 0xdd, 0x28, 0x0c, 0x79, 0x3f, 0xdb, 0xe7,
0x3c, 0xc9, 0x4d, 0xd8, 0x9c, 0xeb, 0x9a, 0xbb, 0x5b, 0xf2, 0x1c, 0x8b, 0xb2, 0x2e, 0xd9, 0x91,
0x41, 0x3d, 0xe6, 0xc9, 0x98, 0x06, 0x5e, 0xf2, 0xe8, 0xb7, 0x7b, 0x1e, 0xd6, 0xad, 0x61, 0xa5,
0xf5, 0xf1, 0x2e, 0x9c, 0xbf, 0x17, 0xa4, 0xfd, 0xf2, 0x84, 0x1d, 0x58, 0x8c, 0x27, 0x87, 0xbd,
0x5c, 0xa6, 0x54, 0x13, 0x2f, 0xe5, 0xe2, 0x27, 0x72, 0xb0, 0xdf, 0x73, 0xa0, 0xbe, 0xf7, 0xe4,
0xd1, 0x5d, 0xd6, 0x85, 0xa5, 0x20, 0xec, 0x47, 0x63, 0x54, 0xbb, 0x62, 0xd1, 0xba, 0x3d, 0x53,
0x56, 0x2e, 0x42, 0x83, 0xb4, 0x35, 0xda, 0x19, 0xd2, 0xda, 0xcc, 0x01, 0x68, 0xe3, 0xf0, 0x17,
0x71, 0x90, 0x90, 0x11, 0xa3, 0x4c, 0x93, 0x3a, 0x69, 0xc4, 0x72, 0x87, 0xfb, 0x3f, 0x75, 0x58,
0x94, 0xba, 0x9a, 0xe6, 0xeb, 0x67, 0xc1, 0x09, 0x97, 0x94, 0xc8, 0x16, 0xde, 0x72, 0x09, 0x1f,
0x47, 0x19, 0xef, 0x59, 0xc7, 0x60, 0x03, 0x11, 0xab, 0x2f, 0x06, 0xea, 0xc5, 0xa8, 0xf5, 0x89,
0xb2, 0x86, 0x67, 0x03, 0x71, 0xb3, 0x10, 0xd0, 0x0b, 0x06, 0x44, 0x53, 0xdd, 0x53, 0x4d, 0xdc,
0x89, 0xbe, 0x1f, 0xfb, 0xfd, 0x20, 0x9b, 0x4a, 0xe1, 0xd6, 0x6d, 0x1c, 0x7b, 0x14, 0xf5, 0xfd,
0x51, 0xef, 0xd0, 0x1f, 0xf9, 0x61, 0x9f, 0x4b, 0x43, 0xca, 0x06, 0xa2, 0xad, 0x24, 0x49, 0x52,
0x68, 0xc2, 0x9e, 0x2a, 0x40, 0xd1, 0xe6, 0xea, 0x47, 0xe3, 0x71, 0x90, 0xa1, 0x89, 0xd5, 0x59,
0x12, 0x8a, 0x24, 0x87, 0xd0, 0x4a, 0x44, 0xeb, 0x54, 0xec, 0x5e, 0x43, 0xcc, 0x66, 0x01, 0x71,
0x94, 0x23, 0xce, 0x49, 0x21, 0x3d, 0x3f, 0xed, 0x80, 0x18, 0x25, 0x87, 0xe0, 0x39, 0x4c, 0xc2,
0x94, 0x67, 0xd9, 0x88, 0x0f, 0x34, 0x41, 0x4d, 0x42, 0x2b, 0x77, 0xb0, 0x1b, 0xb0, 0x2e, 0xac,
0xbe, 0xd4, 0xcf, 0xa2, 0xf4, 0x38, 0x48, 0x7b, 0x29, 0x0f, 0xb3, 0x4e, 0x8b, 0xf0, 0xab, 0xba,
0xd8, 0xfb, 0xb0, 0x55, 0x00, 0x27, 0xbc, 0xcf, 0x83, 0x13, 0x3e, 0xe8, 0x2c, 0xd3, 0x57, 0xb3,
0xba, 0xd9, 0x65, 0x68, 0xa2, 0xb1, 0x3b, 0x89, 0x07, 0x3e, 0xde, 0xc3, 0x6d, 0x3a, 0x07, 0x13,
0xc4, 0xde, 0x85, 0xe5, 0x98, 0x8b, 0xcb, 0xf2, 0x38, 0x1b, 0xf5, 0xd3, 0xce, 0x0a, 0xdd, 0x64,
0x4d, 0x29, 0x4c, 0xc8, 0xb9, 0x9e, 0x8d, 0x81, 0x4c, 0xd9, 0x4f, 0xc9, 0x7c, 0xf2, 0xa7, 0x9d,
0x55, 0x62, 0xb7, 0x1c, 0x40, 0x32, 0x92, 0x04, 0x27, 0x7e, 0xc6, 0x3b, 0x6b, 0xc4, 0x5b, 0xaa,
0xe9, 0xfe, 0x99, 0x03, 0xeb, 0x8f, 0x82, 0x34, 0x93, 0x4c, 0xa8, 0xd5, 0xf1, 0x5b, 0xd0, 0x14,
0xec, 0xd7, 0x8b, 0xc2, 0xd1, 0x54, 0x72, 0x24, 0x08, 0xd0, 0x97, 0xc3, 0xd1, 0x94, 0x7d, 0x02,
0x96, 0x83, 0xd0, 0x44, 0x11, 0x32, 0xdc, 0x52, 0x40, 0x42, 0x7a, 0x0b, 0x9a, 0xf1, 0xe4, 0x70,
0x14, 0xf4, 0x05, 0xca, 0x9c, 0x18, 0x45, 0x80, 0x08, 0x01, 0x8d, 0x24, 0x41, 0x89, 0xc0, 0xa8,
0x13, 0x46, 0x53, 0xc2, 0x10, 0xc5, 0xbd, 0x03, 0x1b, 0x36, 0x81, 0x52, 0x59, 0x6d, 0xc3, 0x92,
0xe4, 0xed, 0xb4, 0xd3, 0xa4, 0xfd, 0x69, 0xcb, 0xfd, 0x91, 0xa8, 0x9e, 0xee, 0x77, 0xff, 0xdd,
0x81, 0x3a, 0x2a, 0x80, 0xd9, 0xca, 0xc2, 0xd4, 0xe9, 0x73, 0x96, 0x4e, 0x27, 0x3f, 0x04, 0xad,
0x22, 0xc1, 0x12, 0x42, 0x6c, 0x0c, 0x48, 0xde, 0x9f, 0xf0, 0xfe, 0x09, 0xc9, 0x8e, 0xee, 0x47,
0x08, 0x4a, 0x16, 0x5e, 0x9d, 0xf4, 0xb5, 0x10, 0x1c, 0xdd, 0x56, 0x7d, 0xf4, 0xe5, 0x62, 0xde,
0x47, 0xdf, 0x75, 0x60, 0x31, 0x08, 0x0f, 0xa3, 0x49, 0x38, 0x20, 0x21, 0x59, 0xf2, 0x54, 0x13,
0x0f, 0x3b, 0x26, 0x4b, 0x2a, 0x18, 0x73, 0x29, 0x1d, 0x39, 0xc0, 0x65, 0x68, 0x5a, 0xa5, 0xa4,
0xf0, 0xf4, 0x3d, 0xf6, 0x1e, 0xac, 0x19, 0x30, 0xb9, 0x83, 0x6f, 0xc3, 0x7c, 0x8c, 0x00, 0x69,
0x28, 0x29, 0xf6, 0x22, 0x4d, 0x29, 0x7a, 0xdc, 0x55, 0x68, 0x3f, 0xe4, 0xd9, 0x07, 0xe1, 0x51,
0xa4, 0x46, 0xfa, 0xe1, 0x1c, 0xac, 0x68, 0x90, 0x1c, 0xe8, 0x1a, 0xac, 0x04, 0x03, 0x1e, 0x66,
0x41, 0x36, 0xed, 0x59, 0x16, 0x5c, 0x11, 0x8c, 0x37, 0x8c, 0x3f, 0x0a, 0xfc, 0x54, 0xea, 0x30,
0xd1, 0x60, 0xbb, 0xb0, 0x81, 0xec, 0xaf, 0x38, 0x5a, 0x1f, 0xab, 0x30, 0x24, 0x2b, 0xfb, 0x50,
0x62, 0x11, 0x2e, 0x39, 0x50, 0x7f, 0x22, 0x34, 0x6d, 0x55, 0x17, 0xee, 0x9a, 0x18, 0x09, 0x97,
0x3c, 0x2f, 0x44, 0x44, 0x03, 0x4a, 0xde, 0xe4, 0x82, 0x30, 0x62, 0x8b, 0xde, 0xa4, 0xe1, 0x91,
0x2e, 0x95, 0x3c, 0xd2, 0x6b, 0xb0, 0x92, 0x4e, 0xc3, 0x3e, 0x1f, 0xf4, 0xb2, 0x08, 0xe7, 0x0d,
0x42, 0x3a, 0x9d, 0x25, 0xaf, 0x08, 0x26, 0xdf, 0x99, 0xa7, 0x59, 0xc8, 0x33, 0x52, 0x5d, 0x4b,
0x9e, 0x6a, 0xe2, 0x2d, 0x40, 0x28, 0x82, 0xa9, 0x1b, 0x9e, 0x6c, 0xe1, 0x55, 0x39, 0x49, 0x82,
0xb4, 0xd3, 0x22, 0x28, 0xfd, 0x66, 0x9f, 0x81, 0xf3, 0x87, 0xe8, 0xe9, 0x1d, 0x73, 0x7f, 0xc0,
0x13, 0x3a, 0x7d, 0xe1, 0xe8, 0x0a, 0x0d, 0x54, 0xdd, 0x89, 0x73, 0x9f, 0xf0, 0x24, 0x0d, 0xa2,
0x90, 0x74, 0x4f, 0xc3, 0x53, 0x4d, 0xf7, 0xdb, 0x74, 0xa3, 0x6b, 0x17, 0xfc, 0x29, 0xa9, 0x23,
0xf6, 0x06, 0x34, 0xc4, 0x1a, 0xd3, 0x63, 0x5f, 0x1a, 0x19, 0x4b, 0x04, 0x38, 0x38, 0xf6, 0x51,
0x80, 0xad, 0x6d, 0x13, 0x31, 0x8d, 0x26, 0xc1, 0xf6, 0xc4, 0xae, 0x5d, 0x81, 0xb6, 0x72, 0xee,
0xd3, 0xde, 0x88, 0x1f, 0x65, 0xca, 0x41, 0x08, 0x27, 0x63, 0x9c, 0x2e, 0x7d, 0xc4, 0x8f, 0x32,
0xf7, 0x31, 0xac, 0x49, 0xb9, 0xfd, 0x72, 0xcc, 0xd5, 0xd4, 0x9f, 0x2b, 0x5e, 0x6a, 0xc2, 0xaa,
0x58, 0xb7, 0x05, 0x9d, 0xbc, 0x9c, 0xc2, 0x4d, 0xe7, 0x7a, 0xc0, 0x64, 0xf7, 0xdd, 0x51, 0x94,
0x72, 0x39, 0xa0, 0x0b, 0xad, 0xfe, 0x28, 0x4a, 0x95, 0x1b, 0x22, 0x97, 0x63, 0xc1, 0x70, 0x7f,
0xd2, 0x49, 0xbf, 0x8f, 0x9a, 0x40, 0xe8, 0x34, 0xd5, 0x74, 0xff, 0xc2, 0x81, 0x75, 0x1a, 0x4d,
0x69, 0x18, 0x6d, 0xbb, 0x9e, 0x9d, 0xcc, 0x56, 0xdf, 0x74, 0xcd, 0x36, 0x60, 0xfe, 0x28, 0x4a,
0xfa, 0x5c, 0xce, 0x24, 0x1a, 0x3f, 0xbe, 0x35, 0x5e, 0x2f, 0x59, 0xe3, 0x3f, 0x74, 0x60, 0x8d,
0x48, 0x3d, 0xc8, 0xfc, 0x6c, 0x92, 0xca, 0xe5, 0xff, 0x22, 0x2c, 0xe3, 0x52, 0xb9, 0x12, 0x27,
0x49, 0xe8, 0x86, 0x96, 0x7c, 0x82, 0x0a, 0xe4, 0xbd, 0x73, 0x9e, 0x8d, 0xcc, 0xbe, 0x00, 0x2d,
0x33, 0x42, 0x43, 0x34, 0x37, 0x77, 0x2f, 0xa8, 0x55, 0x96, 0x38, 0x67, 0xef, 0x9c, 0x67, 0x7d,
0xc0, 0x6e, 0x01, 0x90, 0xb9, 0x41, 0xc3, 0x4a, 0xd7, 0xfa, 0x82, 0xbd, 0x49, 0xc6, 0x61, 0xed,
0x9d, 0xf3, 0x0c, 0xf4, 0x3b, 0x4b, 0xb0, 0x20, 0xee, 0x47, 0xf7, 0x21, 0x2c, 0x5b, 0x94, 0x5a,
0x5e, 0x46, 0x4b, 0x78, 0x19, 0x25, 0xa7, 0xb4, 0x56, 0x76, 0x4a, 0xdd, 0x7f, 0xad, 0x01, 0x43,
0x6e, 0x2b, 0x1c, 0x27, 0x5e, 0xd0, 0xd1, 0xc0, 0x32, 0xb7, 0x5a, 0x9e, 0x09, 0x62, 0xd7, 0x81,
0x19, 0x4d, 0xe5, 0xb7, 0x8b, 0x7b, 0xa3, 0xa2, 0x07, 0x15, 0x9c, 0xb0, 0x95, 0x94, 0x0f, 0x2c,
0x0d, 0x4b, 0x71, 0x6e, 0x95, 0x7d, 0x78, 0x35, 0xc4, 0x93, 0xf4, 0x18, 0x0d, 0x08, 0x65, 0x90,
0xa9, 0x76, 0x91, 0x41, 0x16, 0x5e, 0xcb, 0x20, 0x8b, 0x45, 0x06, 0x31, 0x4d, 0x82, 0x25, 0xcb,
0x24, 0x40, 0xfb, 0x6b, 0x1c, 0x84, 0x64, 0x57, 0xf4, 0xc6, 0x38, 0xbb, 0xb4, 0xbf, 0x2c, 0x20,
0xdb, 0x86, 0x55, 0x69, 0xd7, 0xe5, 0x76, 0x07, 0xd0, 0x1e, 0x97, 0xe0, 0xee, 0xc7, 0x0e, 0xac,
0xe2, 0x3e, 0x5b, 0xbc, 0x78, 0x13, 0x48, 0x14, 0xce, 0xc8, 0x8a, 0x16, 0xee, 0x4f, 0xcf, 0x89,
0xef, 0x43, 0x83, 0x06, 0x8c, 0x62, 0x1e, 0x4a, 0x46, 0xec, 0xd8, 0x8c, 0x98, 0x6b, 0xa1, 0xbd,
0x73, 0x5e, 0x8e, 0x6c, 0xb0, 0xe1, 0x3f, 0x3a, 0xd0, 0x94, 0x64, 0xfe, 0xc4, 0xbe, 0x44, 0x17,
0x96, 0x90, 0x23, 0x0d, 0x83, 0x5d, 0xb7, 0xf1, 0x36, 0x19, 0xa3, 0xc3, 0x86, 0xd7, 0xa7, 0xe5,
0x47, 0x14, 0xc1, 0x78, 0x17, 0x92, 0xc2, 0x4d, 0x7b, 0x59, 0x30, 0xea, 0xa9, 0x5e, 0x19, 0x10,
0xad, 0xea, 0x42, 0xbd, 0x93, 0x66, 0xfe, 0x90, 0xcb, 0x6b, 0x4e, 0x34, 0xd0, 0x61, 0x92, 0x0b,
0x2a, 0x98, 0x83, 0xee, 0xdf, 0xb6, 0x60, 0xab, 0xd4, 0xa5, 0x33, 0x0a, 0xd2, 0x40, 0x1e, 0x05,
0xe3, 0xc3, 0x48, 0xdb, 0xda, 0x8e, 0x69, 0x3b, 0x5b, 0x5d, 0x6c, 0x08, 0xe7, 0xd5, 0x7d, 0x8e,
0x7b, 0x9a, 0xdf, 0xde, 0x35, 0x32, 0x44, 0xde, 0xb5, 0x79, 0xa0, 0x38, 0xa1, 0x82, 0x9b, 0x92,
0x5b, 0x3d, 0x1e, 0x3b, 0x86, 0x8e, 0x36, 0x1c, 0xa4, 0x8a, 0x37, 0x8c, 0x0b, 0x9c, 0xeb, 0x9d,
0xd7, 0xcc, 0x45, 0xfa, 0x68, 0xa0, 0xa6, 0x99, 0x39, 0x1a, 0x9b, 0xc2, 0x25, 0xd5, 0x47, 0x3a,
0xbc, 0x3c, 0x5f, 0xfd, 0x4c, 0x6b, 0x7b, 0x80, 0x1f, 0xdb, 0x93, 0xbe, 0x66, 0x60, 0xf6, 0x4d,
0xd8, 0x3c, 0xf5, 0x83, 0x4c, 0x91, 0x65, 0x18, 0x43, 0xf3, 0x34, 0xe5, 0xee, 0x6b, 0xa6, 0x7c,
0x26, 0x3e, 0xb6, 0x2e, 0xb6, 0x19, 0x23, 0x76, 0xff, 0xde, 0x81, 0xb6, 0x3d, 0x0e, 0xb2, 0xa9,
0x14, 0x78, 0xa5, 0xf8, 0x94, 0xf1, 0x57, 0x00, 0x97, 0x5d, 0xd4, 0x5a, 0x95, 0x8b, 0x6a, 0x3a,
0xa2, 0x73, 0xaf, 0x73, 0x44, 0xeb, 0x67, 0x73, 0x44, 0xe7, 0xab, 0x1c, 0xd1, 0xee, 0x7f, 0x39,
0xc0, 0xca, 0xbc, 0xc4, 0x1e, 0x0a, 0x1f, 0x39, 0xe4, 0x23, 0xa9, 0x93, 0x7e, 0xfe, 0x6c, 0xfc,
0xa8, 0xf6, 0x4e, 0x7d, 0x8d, 0x82, 0x61, 0x2a, 0x1d, 0xd3, 0x44, 0x5a, 0xf6, 0xaa, 0xba, 0x0a,
0xae, 0x71, 0xfd, 0xf5, 0xae, 0xf1, 0xfc, 0xeb, 0x5d, 0xe3, 0x85, 0xa2, 0x6b, 0xdc, 0xfd, 0x5d,
0x07, 0xd6, 0x2b, 0x0e, 0xfd, 0x67, 0xb7, 0x70, 0x3c, 0x26, 0x4b, 0x17, 0xd4, 0xe4, 0x31, 0x99,
0xc0, 0xee, 0x6f, 0xc0, 0xb2, 0xc5, 0xe8, 0x3f, 0xbb, 0xf9, 0x8b, 0x56, 0x9e, 0xe0, 0x33, 0x0b,
0xd6, 0xfd, 0x51, 0x0d, 0x58, 0x59, 0xd8, 0xfe, 0x5f, 0x69, 0x28, 0xef, 0xd3, 0x5c, 0xc5, 0x3e,
0xfd, 0x9f, 0xde, 0x03, 0xef, 0xc0, 0x9a, 0x4c, 0x3f, 0x1a, 0x51, 0x12, 0xc1, 0x31, 0xe5, 0x0e,
0xb4, 0x73, 0xed, 0xb8, 0xc4, 0x92, 0x95, 0xb6, 0x32, 0x2e, 0xc3, 0x42, 0x78, 0xc2, 0xdd, 0x84,
0x0d, 0x91, 0xce, 0xbc, 0x23, 0x86, 0x52, 0xf7, 0xca, 0x9f, 0x3a, 0x70, 0xbe, 0xd0, 0x91, 0xe7,
0x76, 0xc4, 0xd5, 0x61, 0xdf, 0x27, 0x36, 0x10, 0xe9, 0x97, 0x72, 0x64, 0xd0, 0x2f, 0xb8, 0xad,
0xdc, 0x81, 0xfb, 0x33, 0x09, 0xcb, 0xf8, 0x62, 0xd7, 0xab, 0xba, 0xdc, 0x2d, 0x91, 0x74, 0x0d,
0xf9, 0xa8, 0x40, 0xf8, 0x91, 0x48, 0x93, 0x9a, 0x1d, 0x79, 0x70, 0xd8, 0x26, 0x59, 0x35, 0xd1,
0x0a, 0xb4, 0xae, 0x29, 0x9b, 0xde, 0xca, 0x3e, 0xf7, 0xf7, 0x1d, 0x60, 0x5f, 0x99, 0xf0, 0x64,
0x4a, 0x39, 0x1e, 0x1d, 0x9e, 0xd9, 0x2a, 0xc6, 0x31, 0x16, 0xe2, 0xc9, 0xe1, 0x97, 0xf8, 0x54,
0xe5, 0xf6, 0x6a, 0x79, 0x6e, 0xef, 0x4d, 0x00, 0x74, 0xbf, 0x74, 0xe2, 0x08, 0x79, 0x01, 0xfd,
0x5e, 0x31, 0x60, 0x65, 0xfa, 0xad, 0x5e, 0x99, 0x7e, 0xbb, 0x05, 0xeb, 0x16, 0x25, 0xfa, 0xa0,
0x54, 0x52, 0xca, 0x79, 0x45, 0x52, 0xea, 0x3f, 0x1c, 0x98, 0xdb, 0x8b, 0x62, 0x33, 0x00, 0xe9,
0xd8, 0x01, 0x48, 0x79, 0x3b, 0xf4, 0xb4, 0xf2, 0x97, 0x4a, 0xc3, 0x02, 0xb2, 0x6d, 0x68, 0xfb,
0xe3, 0x0c, 0x1d, 0xe9, 0xa3, 0x28, 0x39, 0xf5, 0x93, 0x81, 0x38, 0xbd, 0x3b, 0xb5, 0x8e, 0xe3,
0x15, 0x7a, 0xd8, 0x06, 0xcc, 0x69, 0x35, 0x4a, 0x08, 0xd8, 0x44, 0x53, 0x8c, 0xe2, 0xb0, 0x53,
0x19, 0x03, 0x90, 0x2d, 0x64, 0x0e, 0xfb, 0x7b, 0x61, 0xfc, 0x0a, 0x61, 0xa8, 0xea, 0xc2, 0x9b,
0x0a, 0xb5, 0x2a, 0xa1, 0xc9, 0xe0, 0x8d, 0x6a, 0xbb, 0xff, 0xe6, 0xc0, 0x3c, 0xed, 0x00, 0x8a,
0xaf, 0xe0, 0x59, 0x4a, 0x3b, 0x53, 0xd0, 0xd8, 0x11, 0xe2, 0x5b, 0x00, 0x33, 0xd7, 0x4a, 0x46,
0xd7, 0x34, 0xd9, 0x66, 0x42, 0xfa, 0x32, 0x34, 0x44, 0x4b, 0x67, 0x70, 0x09, 0x25, 0x07, 0xb2,
0x4b, 0x50, 0x3f, 0x8e, 0x62, 0x65, 0x6f, 0x80, 0x8a, 0x19, 0x46, 0xb1, 0x47, 0xf0, 0x9c, 0x1e,
0x1c, 0x4f, 0x10, 0x2f, 0x6e, 0x91, 0x22, 0x18, 0xef, 0x51, 0x3d, 0xac, 0xb9, 0x19, 0x05, 0xa8,
0xbb, 0x0d, 0x2b, 0x8f, 0xa3, 0x01, 0x37, 0xa2, 0x44, 0x33, 0xf9, 0xd3, 0xfd, 0x4d, 0x07, 0x96,
0x14, 0x32, 0xbb, 0x06, 0x75, 0x34, 0x0e, 0x0a, 0xa6, 0xbf, 0xce, 0x15, 0x20, 0x9e, 0x47, 0x18,
0xa8, 0x4d, 0x29, 0x86, 0x90, 0x1b, 0x8a, 0x2a, 0x82, 0x90, 0xdb, 0x41, 0x9a, 0xdc, 0x82, 0xf9,
0x50, 0x80, 0xba, 0x7f, 0xe9, 0xc0, 0xb2, 0x35, 0x07, 0x3a, 0x7c, 0x23, 0x3f, 0xcd, 0x64, 0xfc,
0x55, 0x1e, 0x8f, 0x09, 0x32, 0xe3, 0x86, 0x35, 0x3b, 0x6e, 0xa8, 0x23, 0x5a, 0x73, 0x66, 0x44,
0xeb, 0x06, 0x34, 0xf2, 0x92, 0x81, 0xba, 0xa5, 0x25, 0x71, 0x46, 0x95, 0x05, 0xc9, 0x91, 0x70,
0x9c, 0x7e, 0x34, 0x8a, 0x12, 0x99, 0x51, 0x17, 0x0d, 0xf7, 0x16, 0x34, 0x0d, 0x7c, 0x24, 0x23,
0xe4, 0xd9, 0x69, 0x94, 0x3c, 0x57, 0xe1, 0x4b, 0xd9, 0xd4, 0xc9, 0xbe, 0x5a, 0x9e, 0xec, 0x73,
0xff, 0xca, 0x81, 0x65, 0xe4, 0xc1, 0x20, 0x1c, 0xee, 0x47, 0xa3, 0xa0, 0x3f, 0xa5, 0xb3, 0x57,
0xec, 0x26, 0x65, 0x5d, 0xf1, 0xa2, 0x0d, 0x46, 0xde, 0x56, 0xfe, 0x9e, 0x14, 0x44, 0xdd, 0x46,
0x49, 0x45, 0x3e, 0x3f, 0xf4, 0x53, 0xc9, 0xfc, 0xf2, 0xda, 0xb2, 0x80, 0x28, 0x4f, 0x08, 0x48,
0xfc, 0x8c, 0xf7, 0xc6, 0xc1, 0x68, 0x14, 0x08, 0x5c, 0x61, 0xd4, 0x54, 0x75, 0xb9, 0x3f, 0xa8,
0x41, 0x53, 0x2a, 0xd5, 0xfb, 0x83, 0xa1, 0x48, 0x14, 0x48, 0xd3, 0x50, 0xab, 0x0b, 0x03, 0xa2,
0xfa, 0x2d, 0x63, 0xd2, 0x80, 0x14, 0x8f, 0x75, 0xae, 0x7c, 0xac, 0x17, 0xa1, 0x81, 0xec, 0xf5,
0x2e, 0x59, 0xad, 0xa2, 0xc2, 0x24, 0x07, 0xa8, 0xde, 0x5d, 0xea, 0x9d, 0xcf, 0x7b, 0x09, 0x60,
0xd9, 0xa9, 0x0b, 0x05, 0x3b, 0xf5, 0x7d, 0x68, 0xc9, 0x61, 0x68, 0xdf, 0x49, 0x3b, 0xe4, 0x0c,
0x6e, 0x9d, 0x89, 0x67, 0x61, 0xaa, 0x2f, 0x77, 0xd5, 0x97, 0x4b, 0xaf, 0xfb, 0x52, 0x61, 0x52,
0xde, 0x4c, 0xec, 0xcd, 0xc3, 0xc4, 0x8f, 0x8f, 0xd5, 0x45, 0x35, 0xd0, 0xc5, 0x00, 0x04, 0x66,
0xdb, 0x30, 0x8f, 0x9f, 0x29, 0x6d, 0x5d, 0x2d, 0x74, 0x02, 0x85, 0x5d, 0x83, 0x79, 0x3e, 0x18,
0x72, 0xe5, 0x97, 0x31, 0xdb, 0x43, 0xc6, 0x33, 0xf2, 0x04, 0x02, 0xaa, 0x00, 0x84, 0x16, 0x54,
0x80, 0xad, 0xe9, 0x17, 0xb0, 0xf9, 0xc1, 0xc0, 0xdd, 0x00, 0xf6, 0x58, 0x70, 0xad, 0x19, 0x57,
0xfe, 0x9d, 0x39, 0x68, 0x1a, 0x60, 0x94, 0xe6, 0x21, 0x12, 0xdc, 0x1b, 0x04, 0xfe, 0x98, 0x67,
0x3c, 0x91, 0x9c, 0x5a, 0x80, 0x22, 0x9e, 0x7f, 0x32, 0xec, 0x45, 0x93, 0xac, 0x37, 0xe0, 0xc3,
0x84, 0x8b, 0xeb, 0x14, 0x2f, 0x03, 0x0b, 0x8a, 0x78, 0x63, 0xff, 0x85, 0x89, 0x27, 0xf8, 0xa1,
0x00, 0x55, 0x51, 0x62, 0xb1, 0x47, 0xf5, 0x3c, 0x4a, 0x2c, 0x76, 0xa4, 0xa8, 0x87, 0xe6, 0x2b,
0xf4, 0xd0, 0x7b, 0xb0, 0x29, 0x34, 0x8e, 0x94, 0xcd, 0x5e, 0x81, 0x4d, 0x66, 0xf4, 0xb2, 0x6d,
0x58, 0x45, 0x9a, 0x15, 0x83, 0xa7, 0xc1, 0xb7, 0x45, 0xdc, 0xc6, 0xf1, 0x4a, 0x70, 0xc4, 0x45,
0x71, 0xb4, 0x70, 0x45, 0x26, 0xad, 0x04, 0x27, 0x5c, 0xff, 0x85, 0x8d, 0xdb, 0x90, 0xb8, 0x05,
0xb8, 0xbb, 0x0c, 0xcd, 0x83, 0x2c, 0x8a, 0xd5, 0xa1, 0xb4, 0xa1, 0x25, 0x9a, 0x32, 0x6f, 0xfa,
0x06, 0x5c, 0x20, 0x2e, 0x7a, 0x12, 0xc5, 0xd1, 0x28, 0x1a, 0x4e, 0x0f, 0x26, 0x87, 0x69, 0x3f,
0x09, 0x62, 0xf4, 0x61, 0xdc, 0x7f, 0x70, 0x60, 0xdd, 0xea, 0x95, 0x81, 0x9e, 0xcf, 0x08, 0x96,
0xd6, 0x09, 0x2f, 0xc1, 0x78, 0x6b, 0x86, 0x3a, 0x14, 0x88, 0x22, 0xc4, 0xf6, 0x54, 0xe6, 0xc0,
0x6e, 0xc3, 0x8a, 0xa2, 0x4c, 0x7d, 0x28, 0xb8, 0xb0, 0x53, 0xe6, 0x42, 0xf9, 0x7d, 0x5b, 0x7e,
0xa0, 0x86, 0xf8, 0x25, 0x61, 0x82, 0xf3, 0x01, 0xad, 0x51, 0x79, 0xfc, 0x5d, 0xf5, 0xbd, 0x69,
0xf7, 0x2b, 0x0a, 0xfa, 0x1a, 0x98, 0xba, 0x7f, 0xe0, 0x00, 0xe4, 0xd4, 0x21, 0x63, 0xe4, 0x2a,
0x5d, 0x54, 0x4b, 0x1a, 0xea, 0xfb, 0x6d, 0x68, 0xe9, 0x5c, 0x47, 0x7e, 0x4b, 0x34, 0x15, 0x0c,
0x4d, 0xb3, 0xab, 0xb0, 0x32, 0x1c, 0x45, 0x87, 0x74, 0xc5, 0x52, 0x22, 0x3e, 0x95, 0xd9, 0xe3,
0xb6, 0x00, 0x3f, 0x90, 0xd0, 0xfc, 0x4a, 0xa9, 0x1b, 0x57, 0x8a, 0xfb, 0x9d, 0x9a, 0x8e, 0x90,
0xe7, 0x6b, 0x9e, 0x29, 0x65, 0x6c, 0xb7, 0xa4, 0x1c, 0x67, 0x04, 0xa4, 0x29, 0xb6, 0xb5, 0xff,
0x5a, 0xd7, 0xfb, 0x16, 0xb4, 0x13, 0xa1, 0x7d, 0x94, 0x6a, 0xaa, 0xbf, 0x42, 0x35, 0x2d, 0x27,
0xd6, 0xbd, 0xf3, 0x29, 0x58, 0xf5, 0x07, 0x27, 0x3c, 0xc9, 0x02, 0x72, 0x7e, 0xe8, 0xd2, 0x17,
0x0a, 0x75, 0xc5, 0x80, 0xd3, 0x5d, 0x7c, 0x15, 0x56, 0x64, 0xc6, 0x5e, 0x63, 0xca, 0xba, 0xb1,
0x1c, 0x8c, 0x88, 0xee, 0x9f, 0xab, 0x60, 0xbc, 0x7d, 0x86, 0xb3, 0x77, 0xc4, 0x5c, 0x5d, 0xad,
0xb0, 0xba, 0x4f, 0xc8, 0xc0, 0xf8, 0x40, 0x79, 0x58, 0x32, 0x45, 0x21, 0x80, 0x32, 0x91, 0x61,
0x6f, 0x69, 0xfd, 0x2c, 0x5b, 0xea, 0x7e, 0xec, 0xc0, 0xe2, 0x5e, 0x14, 0xef, 0xc9, 0xe4, 0x3b,
0x09, 0x82, 0xae, 0x87, 0x51, 0x4d, 0xd3, 0x2a, 0xae, 0x95, 0xac, 0xe2, 0xf2, 0x5d, 0xbb, 0x5c,
0xbc, 0x6b, 0x7f, 0x19, 0xde, 0x20, 0xff, 0x3e, 0x89, 0xe2, 0x28, 0x41, 0x61, 0xf4, 0x47, 0xe2,
0x62, 0x8d, 0xc2, 0xec, 0x58, 0xa9, 0xb1, 0x57, 0xa1, 0x90, 0x23, 0x85, 0x0e, 0x80, 0x30, 0x86,
0xa5, 0x6d, 0x20, 0xb4, 0x5b, 0xb9, 0xc3, 0xfd, 0x1c, 0x34, 0xc8, 0xb8, 0xa5, 0x65, 0xbd, 0x03,
0x8d, 0xe3, 0x28, 0xee, 0x1d, 0x07, 0x61, 0xa6, 0x84, 0xbb, 0x9d, 0x5b, 0x9d, 0x7b, 0xb4, 0x21,
0x1a, 0xc1, 0xfd, 0xd1, 0x1c, 0x2c, 0x7e, 0x10, 0x9e, 0x44, 0x41, 0x9f, 0xe2, 0xf6, 0x63, 0x3e,
0x8e, 0x54, 0x75, 0x10, 0xfe, 0xc6, 0xad, 0xa0, 0x4c, 0x79, 0x9c, 0xc9, 0xc0, 0xbb, 0x6a, 0xe2,
0x75, 0x9f, 0xe4, 0x15, 0x7c, 0x42, 0x74, 0x0c, 0x08, 0x1a, 0xf6, 0x89, 0x59, 0xbe, 0x28, 0x5b,
0x79, 0x79, 0xd5, 0xbc, 0x51, 0x5e, 0x45, 0x59, 0x1e, 0x51, 0x04, 0x40, 0xfc, 0xb5, 0xe4, 0xa9,
0x26, 0x39, 0x22, 0x09, 0x17, 0x71, 0x19, 0x32, 0x1c, 0x16, 0xa5, 0x23, 0x62, 0x02, 0xd1, 0xb8,
0x10, 0x1f, 0x08, 0x1c, 0xa1, 0x7c, 0x4d, 0x10, 0x1a, 0x5b, 0xc5, 0x0a, 0xc8, 0x86, 0xe0, 0xf9,
0x02, 0x18, 0x35, 0xf4, 0x80, 0x6b, 0x45, 0x2a, 0xd6, 0x00, 0xa2, 0x42, 0xb1, 0x08, 0x37, 0xdc,
0x17, 0x51, 0xcc, 0xa0, 0xdc, 0x17, 0x64, 0x14, 0x7f, 0x34, 0x3a, 0xf4, 0xfb, 0xcf, 0xa9, 0x2e,
0x95, 0x6a, 0x17, 0x1a, 0x9e, 0x0d, 0x44, 0xaa, 0x8d, 0xd3, 0xa4, 0x3c, 0x61, 0xdd, 0x33, 0x41,
0x6c, 0x17, 0x9a, 0xe4, 0xb2, 0xc9, 0xf3, 0x6c, 0xd3, 0x79, 0xae, 0x9a, 0x3e, 0x1d, 0x9d, 0xa8,
0x89, 0x64, 0xe6, 0x12, 0x56, 0xec, 0xf2, 0x82, 0xaf, 0x02, 0xbb, 0x3d, 0x18, 0xc8, 0xf3, 0xd6,
0x2e, 0x63, 0x7e, 0x52, 0x8e, 0x75, 0x52, 0x15, 0x3b, 0x56, 0xab, 0xdc, 0x31, 0xf7, 0x3e, 0x34,
0xf7, 0x8d, 0x42, 0x4a, 0x62, 0x0d, 0x55, 0x42, 0x29, 0xd9, 0xc9, 0x80, 0x18, 0x13, 0xd6, 0xcc,
0x09, 0xdd, 0x5f, 0x00, 0xf6, 0x28, 0x48, 0x33, 0x4d, 0x5f, 0x5e, 0xb9, 0xa9, 0x7c, 0xf1, 0xbc,
0xf8, 0xa1, 0x29, 0x61, 0x54, 0x94, 0x70, 0x5b, 0x54, 0x4d, 0x14, 0x17, 0xb6, 0x0d, 0x4b, 0x81,
0x00, 0x15, 0x25, 0x41, 0x61, 0xea, 0x7e, 0xb4, 0xd7, 0x24, 0xd0, 0xba, 0x45, 0x7f, 0xe0, 0xc0,
0xa2, 0x5c, 0x1a, 0x5a, 0x1b, 0xa5, 0x12, 0xd2, 0x86, 0x67, 0xc1, 0xaa, 0x8b, 0x07, 0xcb, 0x3c,
0x3c, 0x57, 0xc5, 0xc3, 0x0c, 0xea, 0xb1, 0x9f, 0x1d, 0x93, 0x83, 0xd2, 0xf0, 0xe8, 0x37, 0x5b,
0x15, 0x4e, 0xb3, 0x90, 0x15, 0x72, 0x98, 0xab, 0xea, 0x67, 0x85, 0x4a, 0x2e, 0xc1, 0x71, 0x51,
0x54, 0x67, 0x20, 0xe0, 0x3a, 0x7d, 0x20, 0x6b, 0x38, 0x72, 0x70, 0xbe, 0x5f, 0x72, 0x88, 0xe2,
0x7e, 0x49, 0x54, 0x4f, 0xf7, 0xbb, 0x5d, 0xe8, 0xdc, 0xe3, 0x23, 0x9e, 0xf1, 0xdb, 0xa3, 0x51,
0x71, 0xfc, 0x37, 0xe0, 0x42, 0x45, 0x9f, 0x34, 0x5a, 0x1e, 0xc0, 0xda, 0x3d, 0x7e, 0x38, 0x19,
0x3e, 0xe2, 0x27, 0x79, 0x8e, 0x8f, 0x41, 0x3d, 0x3d, 0x8e, 0x4e, 0xe5, 0xd9, 0xd2, 0x6f, 0xf6,
0x26, 0xc0, 0x08, 0x71, 0x7a, 0x69, 0xcc, 0xfb, 0xaa, 0x6c, 0x8e, 0x20, 0x07, 0x31, 0xef, 0xbb,
0xef, 0x01, 0x33, 0xc7, 0x91, 0x4b, 0x40, 0x3d, 0x30, 0x39, 0xec, 0xa5, 0xd3, 0x34, 0xe3, 0x63,
0x55, 0x0f, 0x68, 0x82, 0xdc, 0xab, 0xd0, 0xda, 0xf7, 0xa7, 0x1e, 0xff, 0x96, 0xac, 0xe2, 0x45,
0xdf, 0xd8, 0x9f, 0x22, 0x2b, 0x6b, 0xdf, 0x98, 0xba, 0xdd, 0xff, 0xac, 0xc1, 0x82, 0xc0, 0xc4,
0x51, 0x07, 0x3c, 0xcd, 0x82, 0x50, 0xe4, 0xb7, 0xe4, 0xa8, 0x06, 0xa8, 0xc4, 0x1b, 0xb5, 0x0a,
0xde, 0x90, 0xd6, 0xaa, 0x2a, 0x41, 0x92, 0x4c, 0x60, 0xc1, 0xd0, 0xac, 0xc9, 0xeb, 0x06, 0x84,
0x73, 0x96, 0x03, 0x0a, 0xc1, 0x92, 0x5c, 0xdb, 0x08, 0xfa, 0x14, 0xd3, 0x4a, 0x76, 0x30, 0x41,
0x95, 0x3a, 0x6d, 0x51, 0x70, 0x4d, 0x49, 0xa7, 0x95, 0x74, 0xd7, 0xd2, 0x19, 0x74, 0x97, 0x30,
0x61, 0x5f, 0xa5, 0xbb, 0xe0, 0x0c, 0xba, 0xcb, 0x65, 0xb0, 0xfa, 0x80, 0x73, 0x8f, 0xe3, 0xad,
0xa8, 0xd8, 0xe9, 0x7b, 0x0e, 0xac, 0xca, 0x0b, 0x5d, 0xf7, 0xb1, 0xb7, 0xad, 0xdb, 0xdf, 0xa9,
0x4a, 0x5d, 0x5c, 0x81, 0x65, 0xba, 0x93, 0x75, 0x54, 0x48, 0x86, 0xb0, 0x2c, 0x20, 0xae, 0x43,
0x05, 0xe3, 0xc7, 0xc1, 0x48, 0x1e, 0x8a, 0x09, 0x52, 0x81, 0x25, 0xf4, 0x8f, 0xe9, 0x48, 0x1c,
0x4f, 0xb7, 0xdd, 0xbf, 0x71, 0x60, 0xcd, 0x20, 0x58, 0x72, 0xe1, 0x2d, 0x50, 0x75, 0x05, 0x22,
0x78, 0x24, 0x84, 0x69, 0xcb, 0x36, 0x4e, 0xf2, 0xcf, 0x2c, 0x64, 0x3a, 0x4c, 0x7f, 0x4a, 0x04,
0xa6, 0x93, 0xb1, 0xb4, 0x40, 0x4c, 0x10, 0x32, 0xd2, 0x29, 0xe7, 0xcf, 0x35, 0xca, 0x1c, 0xa1,
0x58, 0x30, 0x4a, 0x1b, 0xa3, 0x2d, 0xa1, 0x91, 0x44, 0xa5, 0x94, 0x0d, 0x74, 0xff, 0xc9, 0x81,
0x75, 0x61, 0x14, 0x4a, 0x93, 0x5b, 0x57, 0x71, 0x2e, 0x08, 0x2b, 0x58, 0x48, 0xe4, 0xde, 0x39,
0x4f, 0xb6, 0xd9, 0x67, 0xcf, 0x68, 0xc8, 0xea, 0x72, 0x81, 0x19, 0x67, 0x31, 0x57, 0x75, 0x16,
0xaf, 0xd8, 0xe9, 0xaa, 0x60, 0xc9, 0x7c, 0x65, 0xb0, 0xe4, 0xce, 0x22, 0xcc, 0xa7, 0xfd, 0x28,
0xe6, 0xee, 0x26, 0x6c, 0xd8, 0x8b, 0x93, 0x2a, 0xe8, 0xfb, 0x0e, 0x74, 0x1e, 0x88, 0xd0, 0x61,
0x10, 0x0e, 0xf7, 0x82, 0x34, 0x8b, 0x12, 0x5d, 0xb6, 0x7e, 0x09, 0x20, 0xcd, 0xfc, 0x24, 0x13,
0xe5, 0x5c, 0x32, 0xcc, 0x91, 0x43, 0x90, 0x46, 0x1e, 0x0e, 0x44, 0xaf, 0x38, 0x1b, 0xdd, 0xc6,
0x83, 0xa1, 0x52, 0x86, 0x5e, 0x74, 0x74, 0x94, 0x72, 0x6d, 0xb6, 0x9a, 0x30, 0xf4, 0x7c, 0x51,
0xe2, 0xd1, 0xd7, 0xe3, 0x27, 0xa4, 0x6a, 0x85, 0x3d, 0x58, 0x80, 0xba, 0x7f, 0xed, 0xc0, 0x4a,
0x4e, 0xe4, 0x7d, 0x04, 0xda, 0xda, 0x41, 0x90, 0x66, 0x68, 0x07, 0x15, 0x80, 0x09, 0x06, 0xbd,
0x20, 0x94, 0xb4, 0x19, 0x10, 0x92, 0x58, 0xd9, 0x8a, 0x26, 0xaa, 0x74, 0xce, 0x04, 0x89, 0xbc,
0x78, 0x86, 0x5f, 0x8b, 0xba, 0x39, 0xd9, 0xa2, 0x6a, 0xbc, 0x71, 0x46, 0x5f, 0x2d, 0x08, 0x83,
0x58, 0x36, 0xd5, 0xfd, 0xb4, 0x48, 0x50, 0xfc, 0xe9, 0xfe, 0xa1, 0x03, 0x17, 0x2a, 0x36, 0x57,
0x4a, 0xc6, 0x3d, 0x58, 0x3b, 0xd2, 0x9d, 0x6a, 0x03, 0x84, 0x78, 0x6c, 0x4a, 0x2e, 0x2a, 0x2c,
0xda, 0x2b, 0x7f, 0x80, 0xe6, 0x31, 0xc5, 0x8d, 0xc4, 0x96, 0x5a, 0x25, 0x25, 0xe5, 0x8e, 0xdd,
0x3f, 0x9a, 0x83, 0xb6, 0xc8, 0x6a, 0x88, 0x97, 0x5c, 0x3c, 0x61, 0x1f, 0xc2, 0xa2, 0x7c, 0x89,
0xc7, 0xce, 0xcb, 0x69, 0xed, 0xb7, 0x7f, 0xdd, 0xcd, 0x22, 0x58, 0xf2, 0xce, 0xfa, 0x6f, 0x7f,
0xfc, 0x2f, 0x7f, 0x5c, 0x5b, 0x66, 0xcd, 0x9d, 0x93, 0x77, 0x77, 0x86, 0x3c, 0x4c, 0x71, 0x8c,
0x5f, 0x03, 0xc8, 0xdf, 0xa8, 0xb1, 0x8e, 0x36, 0x32, 0x0a, 0x8f, 0xef, 0xba, 0x17, 0x2a, 0x7a,
0xe4, 0xb8, 0x17, 0x68, 0xdc, 0x75, 0xb7, 0x8d, 0xe3, 0x06, 0x61, 0x90, 0x89, 0x07, 0x6b, 0x37,
0x9d, 0x6d, 0x36, 0x80, 0x96, 0xf9, 0x04, 0x8d, 0x29, 0x97, 0xb9, 0xe2, 0x01, 0x5c, 0xf7, 0x8d,
0xca, 0x3e, 0x15, 0x2f, 0xa0, 0x39, 0xce, 0xbb, 0xab, 0x38, 0xc7, 0x84, 0x30, 0xf2, 0x59, 0x46,
0xd0, 0xb6, 0x5f, 0x9a, 0xb1, 0x8b, 0x86, 0x58, 0x97, 0xde, 0xb9, 0x75, 0xdf, 0x9c, 0xd1, 0x2b,
0xe7, 0x7a, 0x93, 0xe6, 0xda, 0x72, 0x19, 0xce, 0xd5, 0x27, 0x1c, 0xf5, 0xce, 0xed, 0xa6, 0xb3,
0xbd, 0xfb, 0xdd, 0x37, 0xa1, 0xa1, 0x83, 0x5c, 0xec, 0x9b, 0xb0, 0x6c, 0xa5, 0x9d, 0x98, 0x5a,
0x46, 0x55, 0x96, 0xaa, 0x7b, 0xb1, 0xba, 0x53, 0x4e, 0x7c, 0x89, 0x26, 0xee, 0xb0, 0x4d, 0x9c,
0x58, 0xe6, 0x6d, 0x76, 0x28, 0xd9, 0x26, 0x6a, 0xfd, 0x9e, 0x8b, 0x75, 0xe6, 0xa9, 0x22, 0x6b,
0x9d, 0xa5, 0xd4, 0x92, 0xb5, 0xce, 0x72, 0x7e, 0xc9, 0xbd, 0x48, 0xd3, 0x6d, 0xb2, 0x0d, 0x73,
0x3a, 0x1d, 0x7c, 0xe2, 0x54, 0x9d, 0x69, 0x3e, 0x44, 0x63, 0x6f, 0x6a, 0xc6, 0xaa, 0x7a, 0xa0,
0xa6, 0x59, 0xa4, 0xfc, 0x4a, 0xcd, 0xed, 0xd0, 0x54, 0x8c, 0xd1, 0xf1, 0x99, 0xef, 0xd0, 0xd8,
0xd7, 0xa1, 0xa1, 0x1f, 0x7b, 0xb0, 0x2d, 0xe3, 0x85, 0x8d, 0xf9, 0x02, 0xa5, 0xdb, 0x29, 0x77,
0x54, 0x31, 0x86, 0x39, 0x32, 0x32, 0xc6, 0x23, 0x38, 0x2f, 0x4d, 0xe2, 0x43, 0xfe, 0xe3, 0xac,
0xa4, 0xe2, 0xf9, 0xdc, 0x0d, 0x87, 0xdd, 0x82, 0x25, 0xf5, 0x86, 0x86, 0x6d, 0x56, 0xbf, 0x05,
0xea, 0x6e, 0x95, 0xe0, 0x52, 0x7b, 0xdc, 0x06, 0xc8, 0xdf, 0x7f, 0x68, 0x39, 0x2b, 0xbd, 0x4a,
0xd1, 0x9b, 0x58, 0xf1, 0x58, 0x64, 0x48, 0xaf, 0x5d, 0xec, 0xe7, 0x25, 0xec, 0xad, 0x1c, 0xbf,
0xf2, 0xe1, 0xc9, 0x2b, 0x06, 0x74, 0x37, 0x69, 0xef, 0x56, 0x19, 0x09, 0x6e, 0xc8, 0x4f, 0x55,
0x9d, 0xf2, 0x3d, 0x68, 0x1a, 0x6f, 0x4a, 0x98, 0x1a, 0xa1, 0xfc, 0x1e, 0xa5, 0xdb, 0xad, 0xea,
0x92, 0xe4, 0x7e, 0x11, 0x96, 0xad, 0xc7, 0x21, 0x5a, 0x32, 0xaa, 0x9e, 0x9e, 0x68, 0xc9, 0xa8,
0x7e, 0x4f, 0xf2, 0x35, 0x68, 0x1a, 0x4f, 0x39, 0x98, 0x51, 0x9f, 0x55, 0x78, 0xc4, 0xa1, 0x29,
0xaa, 0x7a, 0xf9, 0xb1, 0x41, 0xeb, 0x6d, 0xbb, 0x0d, 0x5c, 0x2f, 0x15, 0xeb, 0x22, 0x93, 0x7c,
0x13, 0xda, 0xf6, 0xe3, 0x0e, 0x2d, 0x55, 0x95, 0xcf, 0x44, 0xb4, 0x54, 0xcd, 0x78, 0x11, 0x22,
0x19, 0x72, 0x7b, 0x5d, 0x4f, 0xb2, 0xf3, 0x91, 0x4c, 0xf1, 0xbc, 0x64, 0x5f, 0x41, 0xd5, 0x21,
0xab, 0xa7, 0x59, 0xfe, 0xa4, 0xc5, 0xae, 0xb1, 0xd6, 0xdc, 0x5e, 0x2a, 0xb4, 0x76, 0xd7, 0x68,
0xf0, 0x26, 0xcb, 0x57, 0x20, 0xee, 0x03, 0xaa, 0xa2, 0x36, 0xee, 0x03, 0xb3, 0xd0, 0xda, 0xb8,
0x0f, 0xac, 0x62, 0xeb, 0xe2, 0x7d, 0x90, 0x05, 0x38, 0x46, 0x08, 0x2b, 0x85, 0x02, 0x05, 0x2d,
0x2c, 0xd5, 0x15, 0x5d, 0xdd, 0x4b, 0xaf, 0xae, 0x6b, 0xb0, 0xd5, 0x8c, 0x52, 0x2f, 0x3b, 0xaa,
0x00, 0xef, 0xd7, 0xa1, 0x65, 0x16, 0xe5, 0xeb, 0x1b, 0xa2, 0xe2, 0x29, 0x81, 0xbe, 0x21, 0xaa,
0xaa, 0xf8, 0xd5, 0xe1, 0xb2, 0x96, 0x39, 0x0d, 0xfb, 0x1a, 0xac, 0x18, 0x15, 0x39, 0x07, 0xd3,
0xb0, 0xaf, 0x99, 0xa7, 0x5c, 0xaf, 0xd9, 0xad, 0xb2, 0x06, 0xdd, 0x2d, 0x1a, 0x78, 0xcd, 0xb5,
0x06, 0x46, 0xc6, 0xb9, 0x0b, 0x4d, 0xb3, 0xda, 0xe7, 0x15, 0xe3, 0x6e, 0x19, 0x5d, 0x66, 0xe9,
0xe2, 0x0d, 0x87, 0xfd, 0x89, 0x03, 0x2d, 0xab, 0x76, 0xc6, 0x8a, 0x2a, 0x17, 0xc6, 0xe9, 0x98,
0x7d, 0xe6, 0x40, 0xae, 0x47, 0x44, 0x3e, 0xda, 0xfe, 0xa2, 0xb5, 0xc9, 0x1f, 0x59, 0x5e, 0xc5,
0xf5, 0xe2, 0x7b, 0xcb, 0x97, 0x45, 0x04, 0xb3, 0xa6, 0xf5, 0xe5, 0x0d, 0x87, 0xdd, 0x14, 0x6f,
0x84, 0x55, 0x14, 0x81, 0x19, 0xca, 0xad, 0xb8, 0x65, 0xe6, 0x73, 0xda, 0x6b, 0xce, 0x0d, 0x87,
0x7d, 0x43, 0x3c, 0xab, 0x94, 0xdf, 0xd2, 0xce, 0x9f, 0xf5, 0x7b, 0xf7, 0x0a, 0xad, 0xe6, 0x92,
0x7b, 0xc1, 0x5a, 0x4d, 0x51, 0xbb, 0xdf, 0x16, 0xd4, 0xc9, 0xd7, 0xb2, 0xb9, 0x9a, 0x2a, 0xbd,
0xa0, 0x9d, 0x4d, 0xe4, 0x58, 0x10, 0x29, 0xd1, 0x2d, 0xf6, 0x38, 0xe3, 0x30, 0xee, 0x36, 0xd1,
0x7a, 0xc5, 0x7d, 0x6b, 0x26, 0xad, 0x3b, 0xe4, 0x25, 0x22, 0xc5, 0xfb, 0x00, 0x79, 0x10, 0x8b,
0x15, 0x22, 0x3a, 0x5a, 0x53, 0x97, 0xe3, 0x5c, 0x36, 0x0f, 0xaa, 0xc0, 0x0f, 0x8e, 0xf8, 0x75,
0x21, 0x3e, 0x12, 0x3f, 0xd5, 0xd4, 0x97, 0x83, 0x51, 0xdd, 0x6e, 0x55, 0x57, 0x95, 0xf0, 0xa8,
0xf1, 0xd9, 0x53, 0x58, 0x7e, 0x14, 0x45, 0xcf, 0x27, 0xb1, 0x0e, 0xb3, 0xda, 0x31, 0x95, 0x3d,
0x3f, 0x3d, 0xee, 0x16, 0x56, 0xe1, 0x5e, 0xa6, 0xa1, 0xba, 0xac, 0x63, 0x0c, 0xb5, 0xf3, 0x51,
0x1e, 0x42, 0x7b, 0xc9, 0x7c, 0x58, 0xd3, 0xb7, 0xb2, 0x26, 0xbc, 0x6b, 0x0f, 0x63, 0x46, 0xb2,
0x4a, 0x53, 0x58, 0x76, 0x92, 0xa2, 0x76, 0x27, 0x55, 0x63, 0xde, 0x70, 0xd8, 0x3e, 0xb4, 0xee,
0xf1, 0x7e, 0x34, 0xe0, 0x32, 0x0a, 0xb2, 0x9e, 0x13, 0xae, 0xc3, 0x27, 0xdd, 0x65, 0x0b, 0x68,
0xeb, 0xa9, 0xd8, 0x9f, 0x26, 0xfc, 0x5b, 0x3b, 0x1f, 0xc9, 0xf8, 0xca, 0x4b, 0xa5, 0xa7, 0x54,
0x4c, 0xc8, 0xd2, 0x53, 0x85, 0x20, 0x92, 0xa5, 0xa7, 0x4a, 0x41, 0x24, 0x6b, 0xab, 0x55, 0x4c,
0x8a, 0x8d, 0x60, 0xad, 0x14, 0x77, 0xd2, 0x77, 0xfb, 0xac, 0x68, 0x55, 0xf7, 0xf2, 0x6c, 0x04,
0x7b, 0xb6, 0x6d, 0x7b, 0xb6, 0x03, 0x58, 0xbe, 0xc7, 0xc5, 0x66, 0x89, 0x5c, 0x6e, 0xd7, 0x56,
0x7c, 0x66, 0xde, 0xb7, 0xa8, 0x14, 0xa9, 0xcf, 0xbe, 0x88, 0x28, 0x91, 0xca, 0xbe, 0x0e, 0xcd,
0x87, 0x3c, 0x53, 0xc9, 0x5b, 0x6d, 0x21, 0x15, 0xb2, 0xb9, 0xdd, 0x8a, 0xdc, 0xaf, 0xcd, 0x33,
0x34, 0xda, 0x0e, 0x1f, 0x0c, 0xb9, 0x50, 0x4f, 0xbd, 0x60, 0xf0, 0x92, 0xfd, 0x0a, 0x0d, 0xae,
0xeb, 0x3d, 0x36, 0x8d, 0x9c, 0x9f, 0x39, 0xf8, 0x4a, 0x01, 0x5e, 0x35, 0x72, 0x18, 0x0d, 0xb8,
0x71, 0x25, 0x87, 0xd0, 0x34, 0x8a, 0x91, 0xb4, 0x00, 0x95, 0x4b, 0xa5, 0xb4, 0x00, 0x55, 0xd4,
0x2e, 0xb9, 0xd7, 0x68, 0x1e, 0x97, 0x5d, 0xce, 0xe7, 0x11, 0xf5, 0x4a, 0xf9, 0x4c, 0x3b, 0x1f,
0xf9, 0xe3, 0xec, 0x25, 0x7b, 0x46, 0x0f, 0xa1, 0xcc, 0x04, 0x75, 0x6e, 0xa1, 0x15, 0x73, 0xd9,
0x7a, 0xb3, 0x8c, 0x2e, 0xdb, 0x6a, 0x13, 0x53, 0xd1, 0xcd, 0xfd, 0x59, 0x80, 0x83, 0x2c, 0x8a,
0xef, 0xf9, 0x7c, 0x1c, 0x85, 0xb9, 0xae, 0xcd, 0x93, 0xb0, 0xb9, 0xfe, 0x32, 0x32, 0xb1, 0xec,
0x99, 0x61, 0x23, 0x5b, 0xf9, 0x7d, 0xc5, 0x5c, 0x33, 0xf3, 0xb4, 0x7a, 0x43, 0x2a, 0x72, 0xb5,
0x37, 0x1c, 0xb4, 0x78, 0xf3, 0x28, 0xa7, 0xb6, 0x78, 0x4b, 0x01, 0x54, 0xad, 0xf6, 0x2a, 0x42,
0xa2, 0xfb, 0xd0, 0xc8, 0xc3, 0x66, 0xea, 0x12, 0x2d, 0x06, 0xd9, 0xf4, 0xad, 0x58, 0x0a, 0x66,
0xb9, 0xab, 0xb4, 0x55, 0xc0, 0x96, 0x70, 0xab, 0x28, 0x42, 0x15, 0xc0, 0xba, 0x20, 0x50, 0x5f,
0xf1, 0x94, 0x56, 0x54, 0x2b, 0xa9, 0x08, 0x28, 0x69, 0x69, 0xae, 0x8c, 0xc7, 0x58, 0xbe, 0x2f,
0x72, 0xab, 0x48, 0x69, 0xa2, 0x6a, 0x1e, 0xc3, 0x5a, 0x29, 0x98, 0xa0, 0x45, 0x7a, 0x56, 0x0c,
0x47, 0x8b, 0xf4, 0xcc, 0x38, 0x84, 0x7b, 0x9e, 0xa6, 0x5c, 0x71, 0x01, 0xa7, 0x4c, 0x4f, 0x83,
0xac, 0x7f, 0x7c, 0xd3, 0xd9, 0x3e, 0x5c, 0xa0, 0xbf, 0x02, 0xfa, 0xf4, 0xff, 0x06, 0x00, 0x00,
0xff, 0xff, 0x15, 0xdc, 0x6d, 0x7b, 0x3c, 0x48, 0x00, 0x00,
0x55, 0xaa, 0xee, 0x9e, 0x4f, 0xbf, 0xee, 0xe9, 0x99, 0xc9, 0x19, 0xcd, 0xb4, 0x7a, 0xb5, 0x5a,
0x6d, 0x59, 0x61, 0xc9, 0xc3, 0xa2, 0xd1, 0x8e, 0xed, 0x65, 0xbd, 0x0b, 0x36, 0xfa, 0xcf, 0xda,
0x5a, 0x79, 0x5c, 0x23, 0x59, 0x60, 0x43, 0xb4, 0x6b, 0xba, 0x73, 0x7a, 0xca, 0xea, 0xae, 0x2a,
0x57, 0x55, 0xcf, 0xa8, 0xbd, 0x28, 0x82, 0x5f, 0x70, 0xc2, 0x41, 0x10, 0xf8, 0x62, 0x22, 0x08,
0x22, 0xcc, 0xc5, 0x1c, 0x38, 0xc2, 0xc5, 0x70, 0xe3, 0x44, 0x04, 0xc1, 0x61, 0x4f, 0x0e, 0x8e,
0xc0, 0x01, 0x1c, 0x5c, 0x88, 0xe0, 0x4a, 0x10, 0xef, 0xe5, 0xa7, 0x32, 0xab, 0xaa, 0x25, 0xf9,
0x03, 0xb7, 0xce, 0x97, 0xaf, 0x32, 0x5f, 0x66, 0xbe, 0x5f, 0xbe, 0xf7, 0xb2, 0xa1, 0x99, 0xc4,
0x83, 0xeb, 0x71, 0x12, 0x65, 0x11, 0x5b, 0x18, 0x87, 0x49, 0x3c, 0xe8, 0x5d, 0x1c, 0x45, 0xd1,
0x68, 0xcc, 0x77, 0xfd, 0x38, 0xd8, 0xf5, 0xc3, 0x30, 0xca, 0xfc, 0x2c, 0x88, 0xc2, 0x54, 0x20,
0xb9, 0xdf, 0x80, 0xce, 0x7d, 0x1e, 0x1e, 0x72, 0x3e, 0xf4, 0xf8, 0xb7, 0xa6, 0x3c, 0xcd, 0xd8,
0x2f, 0xc0, 0xba, 0xcf, 0xbf, 0xcd, 0xf9, 0xb0, 0x1f, 0xfb, 0x69, 0x1a, 0x9f, 0x24, 0x7e, 0xca,
0xbb, 0xce, 0x65, 0xe7, 0x5a, 0xdb, 0x5b, 0x13, 0x1d, 0x07, 0x1a, 0xce, 0xde, 0x84, 0x76, 0x8a,
0xa8, 0x3c, 0xcc, 0x92, 0x28, 0x9e, 0x75, 0x6b, 0x84, 0xd7, 0x42, 0xd8, 0x5d, 0x01, 0x72, 0xc7,
0xb0, 0xaa, 0x67, 0x48, 0xe3, 0x28, 0x4c, 0x39, 0xbb, 0x01, 0x9b, 0x83, 0x20, 0x3e, 0xe1, 0x49,
0x9f, 0x3e, 0x9e, 0x84, 0x7c, 0x12, 0x85, 0xc1, 0xa0, 0xeb, 0x5c, 0xae, 0x5f, 0x6b, 0x7a, 0x4c,
0xf4, 0xe1, 0x17, 0x1f, 0xca, 0x1e, 0x76, 0x15, 0x56, 0x79, 0x28, 0xe0, 0x7c, 0x48, 0x5f, 0xc9,
0xa9, 0x3a, 0x39, 0x18, 0x3f, 0x70, 0xff, 0xde, 0x81, 0xf5, 0x0f, 0xc2, 0x20, 0x7b, 0xe2, 0x8f,
0xc7, 0x3c, 0x53, 0x6b, 0xba, 0x0a, 0xab, 0x67, 0x04, 0xa0, 0x35, 0x9d, 0x45, 0xc9, 0x50, 0xae,
0xa8, 0x23, 0xc0, 0x07, 0x12, 0x3a, 0x97, 0xb2, 0xda, 0x5c, 0xca, 0x2a, 0xb7, 0xab, 0x3e, 0x67,
0xbb, 0xae, 0xc2, 0x6a, 0xc2, 0x07, 0xd1, 0x29, 0x4f, 0x66, 0xfd, 0xb3, 0x20, 0x1c, 0x46, 0x67,
0xdd, 0xc6, 0x65, 0xe7, 0xda, 0x82, 0xd7, 0x51, 0xe0, 0x27, 0x04, 0x75, 0x37, 0x81, 0x99, 0xab,
0x10, 0xfb, 0xe6, 0x8e, 0x60, 0xe3, 0x71, 0x38, 0x8e, 0x06, 0x4f, 0x7f, 0xca, 0xd5, 0x55, 0x4c,
0x5f, 0xab, 0x9c, 0x7e, 0x0b, 0x36, 0xed, 0x89, 0x24, 0x01, 0x1c, 0xce, 0xdf, 0x3e, 0xf1, 0xc3,
0x11, 0x57, 0x43, 0x2a, 0x12, 0x3e, 0x05, 0x6b, 0x83, 0x69, 0x92, 0xf0, 0xb0, 0x44, 0xc3, 0xaa,
0x84, 0x6b, 0x22, 0xde, 0x84, 0x76, 0xc8, 0xcf, 0x72, 0x34, 0xc9, 0x32, 0x21, 0x3f, 0x53, 0x28,
0x6e, 0x17, 0xb6, 0x8a, 0xd3, 0x48, 0x02, 0xbe, 0x57, 0x83, 0xd6, 0xa3, 0xc4, 0x0f, 0x53, 0x7f,
0x80, 0x5c, 0xcc, 0xba, 0xb0, 0x94, 0x3d, 0xeb, 0x9f, 0xf8, 0xe9, 0x09, 0x4d, 0xd7, 0xf4, 0x54,
0x93, 0x6d, 0xc1, 0xa2, 0x3f, 0x89, 0xa6, 0x61, 0x46, 0x13, 0xd4, 0x3d, 0xd9, 0x62, 0x6f, 0xc1,
0x7a, 0x38, 0x9d, 0xf4, 0x07, 0x51, 0x78, 0x1c, 0x24, 0x13, 0x21, 0x0b, 0x74, 0x5e, 0x0b, 0x5e,
0xb9, 0x83, 0x5d, 0x02, 0x38, 0xc2, 0x7d, 0x10, 0x53, 0x34, 0x68, 0x0a, 0x03, 0xc2, 0x5c, 0x68,
0xcb, 0x16, 0x0f, 0x46, 0x27, 0x59, 0x77, 0x81, 0x06, 0xb2, 0x60, 0x38, 0x46, 0x16, 0x4c, 0x78,
0x3f, 0xcd, 0xfc, 0x49, 0xdc, 0x5d, 0x24, 0x6a, 0x0c, 0x08, 0xf5, 0x47, 0x99, 0x3f, 0xee, 0x1f,
0x73, 0x9e, 0x76, 0x97, 0x64, 0xbf, 0x86, 0xb0, 0x4f, 0x42, 0x67, 0xc8, 0xd3, 0xac, 0xef, 0x0f,
0x87, 0x09, 0x4f, 0x53, 0x9e, 0x76, 0x97, 0x89, 0x1b, 0x0b, 0x50, 0xdc, 0xb5, 0xfb, 0x3c, 0x33,
0x76, 0x27, 0x95, 0xa7, 0xe3, 0x3e, 0x00, 0x66, 0x80, 0xef, 0xf0, 0xcc, 0x0f, 0xc6, 0x29, 0x7b,
0x07, 0xda, 0x99, 0x81, 0x4c, 0xd2, 0xd7, 0xda, 0x63, 0xd7, 0x49, 0x6d, 0x5c, 0x37, 0x3e, 0xf0,
0x2c, 0x3c, 0xf7, 0x3e, 0x2c, 0xdf, 0xe3, 0xfc, 0x41, 0x30, 0x09, 0x32, 0xb6, 0x05, 0x0b, 0xc7,
0xc1, 0x33, 0x2e, 0x0e, 0xbb, 0xbe, 0x7f, 0xce, 0x13, 0x4d, 0xd6, 0x83, 0xa5, 0x98, 0x27, 0x03,
0xae, 0xb6, 0x7f, 0xff, 0x9c, 0xa7, 0x00, 0xb7, 0x96, 0x60, 0x61, 0x8c, 0x1f, 0xbb, 0x3f, 0xa8,
0x41, 0xeb, 0x90, 0x87, 0x9a, 0x89, 0x18, 0x34, 0x70, 0x49, 0x92, 0x71, 0xe8, 0x37, 0x7b, 0x03,
0x5a, 0xb4, 0xcc, 0x34, 0x4b, 0x82, 0x70, 0x44, 0x83, 0x35, 0x3d, 0x40, 0xd0, 0x21, 0x41, 0xd8,
0x1a, 0xd4, 0xfd, 0x49, 0x46, 0x27, 0x58, 0xf7, 0xf0, 0x27, 0x32, 0x58, 0xec, 0xcf, 0x26, 0xc8,
0x8b, 0xfa, 0xd4, 0xda, 0x5e, 0x4b, 0xc2, 0xf6, 0xf1, 0xd8, 0xae, 0xc3, 0x86, 0x89, 0xa2, 0x46,
0x5f, 0xa0, 0xd1, 0xd7, 0x0d, 0x4c, 0x39, 0xc9, 0x55, 0x58, 0x55, 0xf8, 0x89, 0x20, 0x96, 0xce,
0xb1, 0xe9, 0x75, 0x24, 0x58, 0x2d, 0xe1, 0x1a, 0xac, 0x1d, 0x07, 0xa1, 0x3f, 0xee, 0x0f, 0xc6,
0xd9, 0x69, 0x7f, 0xc8, 0xc7, 0x99, 0x4f, 0x27, 0xba, 0xe0, 0x75, 0x08, 0x7e, 0x7b, 0x9c, 0x9d,
0xde, 0x41, 0x28, 0x7b, 0x0b, 0x9a, 0xc7, 0x9c, 0xf7, 0x69, 0x27, 0xba, 0xcb, 0x97, 0x9d, 0x6b,
0xad, 0xbd, 0x55, 0xb9, 0xf5, 0x6a, 0x77, 0xbd, 0xe5, 0x63, 0xf9, 0xcb, 0xfd, 0xae, 0x03, 0x6d,
0xb1, 0x55, 0x52, 0x85, 0x5e, 0x81, 0x15, 0x45, 0x11, 0x4f, 0x92, 0x28, 0x91, 0xec, 0x6f, 0x03,
0xd9, 0x0e, 0xac, 0x29, 0x40, 0x9c, 0xf0, 0x60, 0xe2, 0x8f, 0xb8, 0x94, 0xb7, 0x12, 0x9c, 0xed,
0xe5, 0x23, 0x26, 0xd1, 0x34, 0x13, 0x4a, 0xac, 0xb5, 0xd7, 0x96, 0x44, 0x79, 0x08, 0xf3, 0x6c,
0x14, 0xf7, 0x3b, 0x0e, 0x30, 0x24, 0xeb, 0x51, 0x24, 0xba, 0xe5, 0x2e, 0x14, 0x4f, 0xc0, 0x79,
0xe5, 0x13, 0xa8, 0xcd, 0x3b, 0x81, 0x2b, 0xb0, 0x48, 0x53, 0xa2, 0xac, 0xd6, 0x4b, 0x64, 0xc9,
0x3e, 0xf7, 0xfb, 0x0e, 0xb4, 0x51, 0x73, 0x84, 0x7c, 0x7c, 0x10, 0x05, 0x61, 0xc6, 0x6e, 0x00,
0x3b, 0x9e, 0x86, 0xc3, 0x20, 0x1c, 0xf5, 0xb3, 0x67, 0xc1, 0xb0, 0x7f, 0x34, 0xc3, 0x21, 0x88,
0x9e, 0xfd, 0x73, 0x5e, 0x45, 0x1f, 0x7b, 0x0b, 0xd6, 0x2c, 0x68, 0x9a, 0x25, 0x82, 0xaa, 0xfd,
0x73, 0x5e, 0xa9, 0x07, 0xe5, 0x3f, 0x9a, 0x66, 0xf1, 0x34, 0xeb, 0x07, 0xe1, 0x90, 0x3f, 0xa3,
0x3d, 0x5b, 0xf1, 0x2c, 0xd8, 0xad, 0x0e, 0xb4, 0xcd, 0xef, 0xdc, 0xcf, 0xc3, 0xda, 0x03, 0x54,
0x0c, 0x61, 0x10, 0x8e, 0x6e, 0x0a, 0xe9, 0x45, 0x6d, 0x15, 0x4f, 0x8f, 0x9e, 0xf2, 0x99, 0x3c,
0x47, 0xd9, 0x42, 0x91, 0x38, 0x89, 0xd2, 0x4c, 0xee, 0x0b, 0xfd, 0x76, 0xff, 0xc5, 0x81, 0x55,
0xdc, 0xf4, 0x0f, 0xfd, 0x70, 0xa6, 0x76, 0xfc, 0x01, 0xb4, 0x71, 0xa8, 0x47, 0xd1, 0x4d, 0xa1,
0xf3, 0x84, 0x2c, 0x5f, 0x93, 0x9b, 0x54, 0xc0, 0xbe, 0x6e, 0xa2, 0xa2, 0x99, 0x9e, 0x79, 0xd6,
0xd7, 0x28, 0x74, 0x99, 0x9f, 0x8c, 0x78, 0x46, 0xda, 0x50, 0x6a, 0x47, 0x10, 0xa0, 0xdb, 0x51,
0x78, 0xcc, 0x2e, 0x43, 0x3b, 0xf5, 0xb3, 0x7e, 0xcc, 0x13, 0xda, 0x35, 0x12, 0x9c, 0xba, 0x07,
0xa9, 0x9f, 0x1d, 0xf0, 0xe4, 0xd6, 0x2c, 0xe3, 0xbd, 0x2f, 0xc0, 0x7a, 0x69, 0x16, 0x94, 0xd5,
0x7c, 0x89, 0xf8, 0x93, 0x6d, 0xc2, 0xc2, 0xa9, 0x3f, 0x9e, 0x72, 0xa9, 0xa4, 0x45, 0xe3, 0xbd,
0xda, 0xbb, 0x8e, 0xfb, 0x49, 0x58, 0xcb, 0xc9, 0x96, 0x4c, 0xcf, 0xa0, 0x81, 0x3b, 0x28, 0x07,
0xa0, 0xdf, 0xee, 0xef, 0x38, 0x02, 0xf1, 0x76, 0x14, 0x68, 0x85, 0x87, 0x88, 0xa8, 0x17, 0x15,
0x22, 0xfe, 0x9e, 0x6b, 0x10, 0x7e, 0xf6, 0xc5, 0xba, 0x57, 0x61, 0xdd, 0x20, 0xe1, 0x05, 0xc4,
0x7e, 0xc7, 0x81, 0xf5, 0x87, 0xfc, 0x4c, 0x9e, 0xba, 0xa2, 0xf6, 0x5d, 0x68, 0x64, 0xb3, 0x58,
0x38, 0x59, 0x9d, 0xbd, 0x2b, 0xf2, 0xd0, 0x4a, 0x78, 0xd7, 0x65, 0xf3, 0xd1, 0x2c, 0xe6, 0x1e,
0x7d, 0xe1, 0x7e, 0x1e, 0x5a, 0x06, 0x90, 0x6d, 0xc3, 0xc6, 0x93, 0x0f, 0x1e, 0x3d, 0xbc, 0x7b,
0x78, 0xd8, 0x3f, 0x78, 0x7c, 0xeb, 0x4b, 0x77, 0x7f, 0xbd, 0xbf, 0x7f, 0xf3, 0x70, 0x7f, 0xed,
0x1c, 0xdb, 0x02, 0xf6, 0xf0, 0xee, 0xe1, 0xa3, 0xbb, 0x77, 0x2c, 0xb8, 0xe3, 0xf6, 0xa0, 0xfb,
0x90, 0x9f, 0x3d, 0x09, 0xb2, 0x90, 0xa7, 0xa9, 0x3d, 0x9b, 0x7b, 0x1d, 0x98, 0x49, 0x82, 0x5c,
0x55, 0x17, 0x96, 0xa4, 0xc5, 0x51, 0x06, 0x57, 0x36, 0xdd, 0x4f, 0x02, 0x3b, 0x0c, 0x46, 0xe1,
0x87, 0x3c, 0x4d, 0xfd, 0x91, 0x56, 0x05, 0x6b, 0x50, 0x9f, 0xa4, 0x23, 0xa9, 0x01, 0xf0, 0xa7,
0xfb, 0x69, 0xd8, 0xb0, 0xf0, 0xe4, 0xc0, 0x17, 0xa1, 0x99, 0x06, 0xa3, 0xd0, 0xcf, 0xa6, 0x09,
0x97, 0x43, 0xe7, 0x00, 0xf7, 0x1e, 0x6c, 0x7e, 0x95, 0x27, 0xc1, 0xf1, 0xec, 0x65, 0xc3, 0xdb,
0xe3, 0xd4, 0x8a, 0xe3, 0xdc, 0x85, 0xf3, 0x85, 0x71, 0xe4, 0xf4, 0x82, 0x11, 0xe5, 0x71, 0x2d,
0x7b, 0xa2, 0x61, 0x88, 0x65, 0xcd, 0x14, 0x4b, 0xf7, 0x31, 0xb0, 0xdb, 0x51, 0x18, 0xf2, 0x41,
0x76, 0xc0, 0x79, 0x92, 0x7b, 0xce, 0x39, 0xd7, 0xb5, 0xf6, 0xb6, 0xe5, 0x39, 0x16, 0x65, 0x5d,
0xb2, 0x23, 0x83, 0x46, 0xcc, 0x93, 0x09, 0x0d, 0xbc, 0xec, 0xd1, 0x6f, 0xf7, 0x3c, 0x6c, 0x58,
0xc3, 0x4a, 0xa7, 0xe7, 0x6d, 0x38, 0x7f, 0x27, 0x48, 0x07, 0xe5, 0x09, 0xbb, 0xb0, 0x14, 0x4f,
0x8f, 0xfa, 0xb9, 0x4c, 0xa9, 0x26, 0xfa, 0x02, 0xc5, 0x4f, 0xe4, 0x60, 0x7f, 0xe0, 0x40, 0x63,
0xff, 0xd1, 0x83, 0xdb, 0xac, 0x07, 0xcb, 0x41, 0x38, 0x88, 0x26, 0xa8, 0x76, 0xc5, 0xa2, 0x75,
0x7b, 0xae, 0xac, 0x5c, 0x84, 0x26, 0x69, 0x6b, 0x74, 0x6f, 0xa4, 0x93, 0x9b, 0x03, 0xd0, 0xb5,
0xe2, 0xcf, 0xe2, 0x20, 0x21, 0xdf, 0x49, 0x79, 0x44, 0x0d, 0xd2, 0x88, 0xe5, 0x0e, 0xf7, 0x7f,
0x1a, 0xb0, 0x24, 0x75, 0x35, 0xcd, 0x37, 0xc8, 0x82, 0x53, 0x2e, 0x29, 0x91, 0x2d, 0xb4, 0x72,
0x09, 0x9f, 0x44, 0x19, 0xef, 0x5b, 0xc7, 0x60, 0x03, 0x11, 0x6b, 0x20, 0x06, 0xea, 0xc7, 0xa8,
0xf5, 0x89, 0xb2, 0xa6, 0x67, 0x03, 0x71, 0xb3, 0x10, 0xd0, 0x0f, 0x86, 0x44, 0x53, 0xc3, 0x53,
0x4d, 0xdc, 0x89, 0x81, 0x1f, 0xfb, 0x83, 0x20, 0x9b, 0x49, 0xe1, 0xd6, 0x6d, 0x1c, 0x7b, 0x1c,
0x0d, 0xfc, 0x71, 0xff, 0xc8, 0x1f, 0xfb, 0xe1, 0x80, 0x4b, 0xff, 0xcd, 0x06, 0xa2, 0x8b, 0x26,
0x49, 0x52, 0x68, 0xc2, 0x8d, 0x2b, 0x40, 0xd1, 0xd5, 0x1b, 0x44, 0x93, 0x49, 0x90, 0xa1, 0x67,
0x47, 0x56, 0xbf, 0xee, 0x19, 0x10, 0x5a, 0x89, 0x68, 0x9d, 0x89, 0xdd, 0x6b, 0x8a, 0xd9, 0x2c,
0x20, 0x8e, 0x82, 0xae, 0x03, 0x2a, 0xa4, 0xa7, 0x67, 0x5d, 0x10, 0xa3, 0xe4, 0x10, 0x3c, 0x87,
0x69, 0x98, 0xf2, 0x2c, 0x1b, 0xf3, 0xa1, 0x26, 0xa8, 0x45, 0x68, 0xe5, 0x0e, 0x76, 0x03, 0x36,
0x84, 0xb3, 0x99, 0xfa, 0x59, 0x94, 0x9e, 0x04, 0x69, 0x3f, 0x45, 0xb7, 0xad, 0x4d, 0xf8, 0x55,
0x5d, 0xec, 0x5d, 0xd8, 0x2e, 0x80, 0x13, 0x3e, 0xe0, 0xc1, 0x29, 0x1f, 0x76, 0x57, 0xe8, 0xab,
0x79, 0xdd, 0xec, 0x32, 0xb4, 0xd0, 0xc7, 0x9e, 0xc6, 0x43, 0x1f, 0xed, 0x70, 0x87, 0xce, 0xc1,
0x04, 0xb1, 0xb7, 0x61, 0x25, 0xe6, 0xc2, 0x58, 0x9e, 0x64, 0xe3, 0x41, 0xda, 0x5d, 0x25, 0x4b,
0xd6, 0x92, 0xc2, 0x84, 0x9c, 0xeb, 0xd9, 0x18, 0xc8, 0x94, 0x83, 0x94, 0x9c, 0x2d, 0x7f, 0xd6,
0x5d, 0x23, 0x76, 0xcb, 0x01, 0x24, 0x23, 0x49, 0x70, 0xea, 0x67, 0xbc, 0xbb, 0x4e, 0xbc, 0xa5,
0x9a, 0xee, 0x9f, 0x3b, 0xb0, 0xf1, 0x20, 0x48, 0x33, 0xc9, 0x84, 0x5a, 0x1d, 0xbf, 0x01, 0x2d,
0xc1, 0x7e, 0xfd, 0x28, 0x1c, 0xcf, 0x24, 0x47, 0x82, 0x00, 0x7d, 0x39, 0x1c, 0xcf, 0xd8, 0x27,
0x60, 0x25, 0x08, 0x4d, 0x14, 0x21, 0xc3, 0x6d, 0x05, 0x24, 0xa4, 0x37, 0xa0, 0x15, 0x4f, 0x8f,
0xc6, 0xc1, 0x40, 0xa0, 0xd4, 0xc5, 0x28, 0x02, 0x44, 0x08, 0xe8, 0x24, 0x09, 0x4a, 0x04, 0x46,
0x83, 0x30, 0x5a, 0x12, 0x86, 0x28, 0xee, 0x2d, 0xd8, 0xb4, 0x09, 0x94, 0xca, 0x6a, 0x07, 0x96,
0x25, 0x6f, 0xa7, 0xdd, 0x16, 0xed, 0x4f, 0x47, 0xee, 0x8f, 0x44, 0xf5, 0x74, 0xbf, 0xfb, 0x1f,
0x0e, 0x34, 0x50, 0x01, 0xcc, 0x57, 0x16, 0xa6, 0x4e, 0xaf, 0x5b, 0x3a, 0x9d, 0xae, 0x3f, 0xe8,
0x15, 0x09, 0x96, 0x10, 0x62, 0x63, 0x40, 0xf2, 0xfe, 0x84, 0x0f, 0x4e, 0x49, 0x76, 0x74, 0x3f,
0x42, 0x50, 0xb2, 0xd0, 0x74, 0xd2, 0xd7, 0x42, 0x70, 0x74, 0x5b, 0xf5, 0xd1, 0x97, 0x4b, 0x79,
0x1f, 0x7d, 0xd7, 0x85, 0xa5, 0x20, 0x3c, 0x8a, 0xa6, 0xe1, 0x90, 0x84, 0x64, 0xd9, 0x53, 0x4d,
0x3c, 0xec, 0x98, 0x3c, 0xa9, 0x60, 0xc2, 0xa5, 0x74, 0xe4, 0x00, 0x97, 0xa1, 0x6b, 0x95, 0x92,
0xc2, 0xd3, 0x76, 0xec, 0x1d, 0x58, 0x37, 0x60, 0x72, 0x07, 0xdf, 0x84, 0x85, 0x18, 0x01, 0xd2,
0x51, 0x52, 0xec, 0x45, 0x9a, 0x52, 0xf4, 0xb8, 0x6b, 0xd0, 0xb9, 0xcf, 0xb3, 0x0f, 0xc2, 0xe3,
0x48, 0x8d, 0xf4, 0xa3, 0x3a, 0xac, 0x6a, 0x90, 0x1c, 0xe8, 0x1a, 0xac, 0x06, 0x43, 0x1e, 0x66,
0x41, 0x36, 0xeb, 0x5b, 0x1e, 0x5c, 0x11, 0x8c, 0x16, 0xc6, 0x1f, 0x07, 0x7e, 0x2a, 0x75, 0x98,
0x68, 0xb0, 0x3d, 0xd8, 0x44, 0xf6, 0x57, 0x1c, 0xad, 0x8f, 0x55, 0x38, 0x92, 0x95, 0x7d, 0x28,
0xb1, 0x08, 0x97, 0x1c, 0xa8, 0x3f, 0x11, 0x9a, 0xb6, 0xaa, 0x0b, 0x77, 0x4d, 0x8c, 0x84, 0x4b,
0x5e, 0x10, 0x22, 0xa2, 0x01, 0xa5, 0x4b, 0xec, 0xa2, 0x70, 0x62, 0x8b, 0x97, 0x58, 0xe3, 0x22,
0xbc, 0x5c, 0xba, 0x08, 0x5f, 0x83, 0xd5, 0x74, 0x16, 0x0e, 0xf8, 0xb0, 0x9f, 0x45, 0x38, 0x6f,
0x10, 0xd2, 0xe9, 0x2c, 0x7b, 0x45, 0x30, 0x5d, 0xd9, 0x79, 0x9a, 0x85, 0x3c, 0x23, 0xd5, 0xb5,
0xec, 0xa9, 0x26, 0x5a, 0x01, 0x42, 0x11, 0x4c, 0xdd, 0xf4, 0x64, 0x0b, 0x4d, 0xe5, 0x34, 0x09,
0xd2, 0x6e, 0x9b, 0xa0, 0xf4, 0x9b, 0x7d, 0x06, 0xce, 0x1f, 0xe1, 0xbd, 0xf0, 0x84, 0xfb, 0x43,
0x9e, 0xd0, 0xe9, 0x8b, 0xfb, 0xb5, 0xd0, 0x40, 0xd5, 0x9d, 0x38, 0xf7, 0x29, 0x4f, 0xd2, 0x20,
0x0a, 0x49, 0xf7, 0x34, 0x3d, 0xd5, 0x74, 0xbf, 0x4d, 0x16, 0x5d, 0xdf, 0xfc, 0x1f, 0x93, 0x3a,
0x62, 0xaf, 0x41, 0x53, 0xac, 0x31, 0x3d, 0xf1, 0xa5, 0x93, 0xb1, 0x4c, 0x80, 0xc3, 0x13, 0x1f,
0x05, 0xd8, 0xda, 0x36, 0x11, 0x4a, 0x69, 0x11, 0x6c, 0x5f, 0xec, 0xda, 0x15, 0xe8, 0xa8, 0x98,
0x42, 0xda, 0x1f, 0xf3, 0xe3, 0x4c, 0x5d, 0x10, 0xc2, 0xe9, 0x04, 0xa7, 0x4b, 0x1f, 0xf0, 0xe3,
0xcc, 0x7d, 0x08, 0xeb, 0x52, 0x6e, 0xbf, 0x1c, 0x73, 0x35, 0xf5, 0xe7, 0x8a, 0x46, 0x4d, 0x78,
0x15, 0x1b, 0xb6, 0xa0, 0xd3, 0x2d, 0xa7, 0x60, 0xe9, 0x5c, 0x0f, 0x98, 0xec, 0xbe, 0x3d, 0x8e,
0x52, 0x2e, 0x07, 0x74, 0xa1, 0x3d, 0x18, 0x47, 0xa9, 0xba, 0x86, 0xc8, 0xe5, 0x58, 0x30, 0xdc,
0x9f, 0x74, 0x3a, 0x18, 0xa0, 0x26, 0x10, 0x3a, 0x4d, 0x35, 0xdd, 0x1f, 0x38, 0xb0, 0x41, 0xa3,
0x29, 0x0d, 0xa3, 0x7d, 0xd7, 0x57, 0x27, 0xb3, 0x3d, 0x30, 0xaf, 0x66, 0x9b, 0xb0, 0x70, 0x1c,
0x25, 0x03, 0x2e, 0x67, 0x12, 0x8d, 0x9f, 0xdc, 0x1b, 0x6f, 0x94, 0xbc, 0xf1, 0x1f, 0x39, 0xb0,
0x4e, 0xa4, 0x1e, 0x66, 0x7e, 0x36, 0x4d, 0xe5, 0xf2, 0x7f, 0x19, 0x56, 0x70, 0xa9, 0x5c, 0x89,
0x93, 0x24, 0x74, 0x53, 0x4b, 0x3e, 0x41, 0x05, 0xf2, 0xfe, 0x39, 0xcf, 0x46, 0x66, 0x5f, 0x80,
0xb6, 0x19, 0x18, 0x22, 0x9a, 0x5b, 0x7b, 0x17, 0xd4, 0x2a, 0x4b, 0x9c, 0xb3, 0x7f, 0xce, 0xb3,
0x3e, 0x60, 0xef, 0x03, 0x90, 0xbb, 0x41, 0xc3, 0xca, 0xab, 0xf5, 0x05, 0x7b, 0x93, 0x8c, 0xc3,
0xda, 0x3f, 0xe7, 0x19, 0xe8, 0xb7, 0x96, 0x61, 0x51, 0xd8, 0x47, 0xf7, 0x3e, 0xac, 0x58, 0x94,
0x5a, 0xb7, 0x8c, 0xb6, 0xb8, 0x65, 0x94, 0x2e, 0xa5, 0xb5, 0xf2, 0xa5, 0xd4, 0xfd, 0xb7, 0x1a,
0x30, 0xe4, 0xb6, 0xc2, 0x71, 0xa2, 0x81, 0x8e, 0x86, 0x96, 0xbb, 0xd5, 0xf6, 0x4c, 0x10, 0xbb,
0x0e, 0xcc, 0x68, 0xaa, 0x7b, 0xbb, 0xb0, 0x1b, 0x15, 0x3d, 0xa8, 0xe0, 0x84, 0xaf, 0xa4, 0xee,
0xc0, 0xd2, 0xb1, 0x14, 0xe7, 0x56, 0xd9, 0x87, 0xa6, 0x21, 0x9e, 0xa6, 0x27, 0xe8, 0x40, 0x28,
0x87, 0x4c, 0xb5, 0x8b, 0x0c, 0xb2, 0xf8, 0x52, 0x06, 0x59, 0x2a, 0x32, 0x88, 0xe9, 0x12, 0x2c,
0x5b, 0x2e, 0x01, 0xfa, 0x5f, 0x93, 0x20, 0x24, 0xbf, 0xa2, 0x3f, 0xc1, 0xd9, 0xa5, 0xff, 0x65,
0x01, 0xd9, 0x0e, 0xac, 0x49, 0xbf, 0x2e, 0xf7, 0x3b, 0x80, 0xf6, 0xb8, 0x04, 0x77, 0x3f, 0x76,
0x60, 0x0d, 0xf7, 0xd9, 0xe2, 0xc5, 0xf7, 0x80, 0x44, 0xe1, 0x15, 0x59, 0xd1, 0xc2, 0xfd, 0xd9,
0x39, 0xf1, 0x5d, 0x68, 0xd2, 0x80, 0x51, 0xcc, 0x43, 0xc9, 0x88, 0x5d, 0x9b, 0x11, 0x73, 0x2d,
0xb4, 0x7f, 0xce, 0xcb, 0x91, 0x0d, 0x36, 0xfc, 0x27, 0x07, 0x5a, 0x92, 0xcc, 0x9f, 0xfa, 0x2e,
0xd1, 0x83, 0x65, 0xe4, 0x48, 0xc3, 0x61, 0xd7, 0x6d, 0xb4, 0x26, 0x13, 0xbc, 0xb0, 0xa1, 0xf9,
0xb4, 0xee, 0x11, 0x45, 0x30, 0xda, 0x42, 0x52, 0xb8, 0x69, 0x3f, 0x0b, 0xc6, 0x7d, 0xd5, 0x2b,
0xe3, 0xb0, 0x55, 0x5d, 0xa8, 0x77, 0xd2, 0xcc, 0x1f, 0x71, 0x69, 0xe6, 0x44, 0x03, 0x2f, 0x4c,
0x72, 0x41, 0x05, 0x77, 0xd0, 0xfd, 0xbb, 0x36, 0x6c, 0x97, 0xba, 0x74, 0x22, 0x43, 0x3a, 0xc8,
0xe3, 0x60, 0x72, 0x14, 0x69, 0x5f, 0xdb, 0x31, 0x7d, 0x67, 0xab, 0x8b, 0x8d, 0xe0, 0xbc, 0xb2,
0xe7, 0xb8, 0xa7, 0xb9, 0xf5, 0xae, 0x91, 0x23, 0xf2, 0xb6, 0xcd, 0x03, 0xc5, 0x09, 0x15, 0xdc,
0x94, 0xdc, 0xea, 0xf1, 0xd8, 0x09, 0x74, 0xb5, 0xe3, 0x20, 0x55, 0xbc, 0xe1, 0x5c, 0xe0, 0x5c,
0x6f, 0xbd, 0x64, 0x2e, 0xd2, 0x47, 0x43, 0x35, 0xcd, 0xdc, 0xd1, 0xd8, 0x0c, 0x2e, 0xa9, 0x3e,
0xd2, 0xe1, 0xe5, 0xf9, 0x1a, 0xaf, 0xb4, 0xb6, 0x7b, 0xf8, 0xb1, 0x3d, 0xe9, 0x4b, 0x06, 0x66,
0xdf, 0x84, 0xad, 0x33, 0x3f, 0xc8, 0x14, 0x59, 0x86, 0x33, 0xb4, 0x40, 0x53, 0xee, 0xbd, 0x64,
0xca, 0x27, 0xe2, 0x63, 0xcb, 0xb0, 0xcd, 0x19, 0xb1, 0xf7, 0x0f, 0x0e, 0x74, 0xec, 0x71, 0x90,
0x4d, 0xa5, 0xc0, 0x2b, 0xc5, 0xa7, 0x9c, 0xbf, 0x02, 0xb8, 0x7c, 0x45, 0xad, 0x55, 0x5d, 0x51,
0xcd, 0x8b, 0x68, 0xfd, 0x65, 0x17, 0xd1, 0xc6, 0xab, 0x5d, 0x44, 0x17, 0xaa, 0x2e, 0xa2, 0xbd,
0xff, 0x76, 0x80, 0x95, 0x79, 0x89, 0xdd, 0x17, 0x77, 0xe4, 0x90, 0x8f, 0xa5, 0x4e, 0xfa, 0xc5,
0x57, 0xe3, 0x47, 0xb5, 0x77, 0xea, 0x6b, 0x14, 0x0c, 0x53, 0xe9, 0x98, 0x2e, 0xd2, 0x8a, 0x57,
0xd5, 0x55, 0xb8, 0x1a, 0x37, 0x5e, 0x7e, 0x35, 0x5e, 0x78, 0xf9, 0xd5, 0x78, 0xb1, 0x78, 0x35,
0xee, 0xfd, 0xbe, 0x03, 0x1b, 0x15, 0x87, 0xfe, 0xf3, 0x5b, 0x38, 0x1e, 0x93, 0xa5, 0x0b, 0x6a,
0xf2, 0x98, 0x4c, 0x60, 0xef, 0xb7, 0x60, 0xc5, 0x62, 0xf4, 0x9f, 0xdf, 0xfc, 0x45, 0x2f, 0x4f,
0xf0, 0x99, 0x05, 0xeb, 0xfd, 0xb8, 0x06, 0xac, 0x2c, 0x6c, 0xff, 0xaf, 0x34, 0x94, 0xf7, 0xa9,
0x5e, 0xb1, 0x4f, 0xff, 0xa7, 0x76, 0xe0, 0x2d, 0x58, 0x97, 0x59, 0x4f, 0x23, 0x4a, 0x22, 0x38,
0xa6, 0xdc, 0x81, 0x7e, 0xae, 0x1d, 0x97, 0x58, 0xb6, 0xb2, 0x65, 0x86, 0x31, 0x2c, 0x84, 0x27,
0xdc, 0x2d, 0xd8, 0x14, 0x59, 0xd4, 0x5b, 0x62, 0x28, 0x65, 0x57, 0xfe, 0xcc, 0x81, 0xf3, 0x85,
0x8e, 0x3c, 0xb7, 0x23, 0x4c, 0x87, 0x6d, 0x4f, 0x6c, 0x20, 0xd2, 0x2f, 0xe5, 0xc8, 0xa0, 0x5f,
0x70, 0x5b, 0xb9, 0x03, 0xf7, 0x67, 0x1a, 0x96, 0xf1, 0xc5, 0xae, 0x57, 0x75, 0xb9, 0xdb, 0x22,
0xd7, 0x1b, 0xf2, 0x71, 0x81, 0xf0, 0x63, 0x91, 0x9d, 0x35, 0x3b, 0xf2, 0xe0, 0xb0, 0x4d, 0xb2,
0x6a, 0xa2, 0x17, 0x68, 0x99, 0x29, 0x9b, 0xde, 0xca, 0x3e, 0xf7, 0x6f, 0x1c, 0x60, 0x5f, 0x99,
0xf2, 0x64, 0x46, 0x39, 0x1e, 0x1d, 0x9e, 0xd9, 0x2e, 0xc6, 0x31, 0x16, 0xe3, 0xe9, 0xd1, 0x97,
0xf8, 0x4c, 0x65, 0x02, 0x6b, 0x79, 0x26, 0xf0, 0x75, 0x00, 0xbc, 0x7e, 0xe9, 0xc4, 0x11, 0xf2,
0x02, 0xde, 0x7b, 0xc5, 0x80, 0x95, 0xc9, 0xba, 0xc6, 0xcb, 0x93, 0x75, 0x0b, 0x2f, 0x4b, 0xd6,
0xbd, 0x0f, 0x1b, 0x16, 0xdd, 0xfa, 0x58, 0x55, 0x0a, 0xcb, 0x79, 0x41, 0x0a, 0xeb, 0x3f, 0x1d,
0xa8, 0xef, 0x47, 0xb1, 0x19, 0xae, 0x74, 0xec, 0x70, 0xa5, 0xb4, 0x25, 0x7d, 0x6d, 0x2a, 0xa4,
0x8a, 0xb1, 0x80, 0x6c, 0x07, 0x3a, 0xfe, 0x24, 0xc3, 0x6b, 0xf7, 0x71, 0x94, 0x9c, 0xf9, 0xc9,
0x50, 0x9c, 0xf5, 0xad, 0x5a, 0xd7, 0xf1, 0x0a, 0x3d, 0x6c, 0x13, 0xea, 0x5a, 0xe9, 0x12, 0x02,
0x36, 0xd1, 0x71, 0xa3, 0xa8, 0xed, 0x4c, 0x46, 0x0c, 0x64, 0x0b, 0x59, 0xc9, 0xfe, 0x5e, 0xb8,
0xca, 0x42, 0x74, 0xaa, 0xba, 0xd0, 0xae, 0xe1, 0xf6, 0x11, 0x9a, 0x0c, 0xf5, 0xa8, 0xb6, 0xfb,
0xef, 0x0e, 0x2c, 0xd0, 0x0e, 0xa0, 0xb0, 0x0b, 0x0e, 0xa7, 0xdc, 0x38, 0x85, 0x98, 0x1d, 0x21,
0xec, 0x05, 0x30, 0x73, 0xad, 0x8c, 0x79, 0x4d, 0x93, 0x6d, 0x66, 0xcd, 0x2f, 0x43, 0x53, 0xb4,
0x74, 0x76, 0x98, 0x50, 0x72, 0x20, 0xbb, 0x04, 0x8d, 0x93, 0x28, 0x56, 0xde, 0x09, 0xa8, 0x08,
0x63, 0x14, 0x7b, 0x04, 0xcf, 0xe9, 0xc1, 0xf1, 0x04, 0xf1, 0xc2, 0xe6, 0x14, 0xc1, 0x68, 0x75,
0xf5, 0xb0, 0xe6, 0x66, 0x14, 0xa0, 0xee, 0x0e, 0xac, 0x3e, 0x8c, 0x86, 0xdc, 0x88, 0x29, 0xcd,
0xe5, 0x66, 0xf7, 0xb7, 0x1d, 0x58, 0x56, 0xc8, 0xec, 0x1a, 0x34, 0xd0, 0x95, 0x28, 0x5c, 0x14,
0x74, 0x66, 0x01, 0xf1, 0x3c, 0xc2, 0x40, 0xdd, 0x4b, 0x11, 0x87, 0xdc, 0xad, 0x54, 0xf1, 0x86,
0xdc, 0x6b, 0xd2, 0xe4, 0x16, 0x9c, 0x8d, 0x02, 0xd4, 0xfd, 0x4b, 0x07, 0x56, 0xac, 0x39, 0xf0,
0x7a, 0x38, 0xf6, 0xd3, 0x4c, 0x46, 0x6b, 0xe5, 0xf1, 0x98, 0x20, 0x33, 0xca, 0x58, 0xb3, 0xa3,
0x8c, 0x3a, 0xfe, 0x55, 0x37, 0xe3, 0x5f, 0x37, 0xa0, 0x99, 0xd7, 0x35, 0x34, 0x2c, 0x9d, 0x8a,
0x33, 0xaa, 0x9c, 0x49, 0x8e, 0x84, 0xe3, 0x0c, 0xa2, 0x71, 0x94, 0xc8, 0x6c, 0xbd, 0x68, 0xb8,
0xef, 0x43, 0xcb, 0xc0, 0x47, 0x32, 0x42, 0x9e, 0x9d, 0x45, 0xc9, 0x53, 0x15, 0xec, 0x94, 0x4d,
0x9d, 0x1a, 0xac, 0xe5, 0xa9, 0x41, 0xf7, 0xaf, 0x1c, 0x58, 0x41, 0x1e, 0x0c, 0xc2, 0xd1, 0x41,
0x34, 0x0e, 0x06, 0x33, 0x3a, 0x7b, 0xc5, 0x6e, 0x52, 0x33, 0x28, 0x5e, 0xb4, 0xc1, 0xc8, 0xdb,
0xea, 0x76, 0x28, 0x05, 0x51, 0xb7, 0x51, 0x52, 0x91, 0xcf, 0x8f, 0xfc, 0x54, 0x32, 0xbf, 0x34,
0x72, 0x16, 0x10, 0xe5, 0x09, 0x01, 0x89, 0x9f, 0xf1, 0xfe, 0x24, 0x18, 0x8f, 0x03, 0x81, 0x2b,
0x5c, 0xa0, 0xaa, 0x2e, 0xf7, 0x87, 0x35, 0x68, 0x49, 0x15, 0x7c, 0x77, 0x38, 0x12, 0x69, 0x05,
0xe9, 0x48, 0x6a, 0x75, 0x61, 0x40, 0x54, 0xbf, 0xe5, 0x7a, 0x1a, 0x90, 0xe2, 0xb1, 0xd6, 0xcb,
0xc7, 0x7a, 0x11, 0x9a, 0xc8, 0x5e, 0x6f, 0x93, 0x8f, 0x2b, 0xca, 0x60, 0x72, 0x80, 0xea, 0xdd,
0xa3, 0xde, 0x85, 0xbc, 0x97, 0x00, 0x96, 0x57, 0xbb, 0x58, 0xf0, 0x6a, 0xdf, 0x85, 0xb6, 0x1c,
0x86, 0xf6, 0x9d, 0xb4, 0x43, 0xce, 0xe0, 0xd6, 0x99, 0x78, 0x16, 0xa6, 0xfa, 0x72, 0x4f, 0x7d,
0xb9, 0xfc, 0xb2, 0x2f, 0x15, 0x26, 0x65, 0xd9, 0xc4, 0xde, 0xdc, 0x4f, 0xfc, 0xf8, 0x44, 0x99,
0xb5, 0xa1, 0x2e, 0x1d, 0x20, 0x30, 0xdb, 0x81, 0x05, 0xfc, 0x4c, 0x69, 0xeb, 0x6a, 0xa1, 0x13,
0x28, 0xec, 0x1a, 0x2c, 0xf0, 0xe1, 0x88, 0xab, 0x5b, 0x1c, 0xb3, 0xef, 0xd3, 0x78, 0x46, 0x9e,
0x40, 0x40, 0x15, 0x80, 0xd0, 0x82, 0x0a, 0xb0, 0x35, 0xfd, 0x22, 0x36, 0x3f, 0x18, 0xba, 0x9b,
0xc0, 0x1e, 0x0a, 0xae, 0x35, 0xa3, 0xd0, 0xbf, 0x57, 0x87, 0x96, 0x01, 0x46, 0x69, 0x1e, 0x21,
0xc1, 0xfd, 0x61, 0xe0, 0x4f, 0x78, 0xc6, 0x13, 0xc9, 0xa9, 0x05, 0x28, 0xe2, 0xf9, 0xa7, 0xa3,
0x7e, 0x34, 0xcd, 0xfa, 0x43, 0x3e, 0x4a, 0xb8, 0x30, 0xbe, 0x68, 0x0c, 0x2c, 0x28, 0xe2, 0x4d,
0xfc, 0x67, 0x26, 0x9e, 0xe0, 0x87, 0x02, 0x54, 0xc5, 0x94, 0xc5, 0x1e, 0x35, 0xf2, 0x98, 0xb2,
0xd8, 0x91, 0xa2, 0x1e, 0x5a, 0xa8, 0xd0, 0x43, 0xef, 0xc0, 0x96, 0xd0, 0x38, 0x52, 0x36, 0xfb,
0x05, 0x36, 0x99, 0xd3, 0xcb, 0x76, 0x60, 0x0d, 0x69, 0x56, 0x0c, 0x9e, 0x06, 0xdf, 0x16, 0x51,
0x1e, 0xc7, 0x2b, 0xc1, 0x11, 0x17, 0xc5, 0xd1, 0xc2, 0x15, 0x79, 0xb7, 0x12, 0x9c, 0x70, 0xfd,
0x67, 0x36, 0x6e, 0x53, 0xe2, 0x16, 0xe0, 0xee, 0x0a, 0xb4, 0x0e, 0xb3, 0x28, 0x56, 0x87, 0xd2,
0x81, 0xb6, 0x68, 0xca, 0x2c, 0xeb, 0x6b, 0x70, 0x81, 0xb8, 0xe8, 0x51, 0x14, 0x47, 0xe3, 0x68,
0x34, 0x3b, 0x9c, 0x1e, 0xa5, 0x83, 0x24, 0x88, 0xf1, 0xc6, 0xe3, 0xfe, 0xa3, 0x03, 0x1b, 0x56,
0xaf, 0x0c, 0x0b, 0x7d, 0x46, 0xb0, 0xb4, 0x4e, 0x8f, 0x09, 0xc6, 0x5b, 0x37, 0xd4, 0xa1, 0x40,
0x14, 0x01, 0xb9, 0xc7, 0x32, 0x63, 0x76, 0x13, 0x56, 0x15, 0x65, 0xea, 0x43, 0xc1, 0x85, 0xdd,
0x32, 0x17, 0xca, 0xef, 0x3b, 0xf2, 0x03, 0x35, 0xc4, 0xaf, 0x08, 0x87, 0x9d, 0x0f, 0x69, 0x8d,
0x2a, 0x3e, 0xd0, 0x53, 0xdf, 0x9b, 0xb7, 0x04, 0x45, 0xc1, 0x40, 0x03, 0x53, 0xf7, 0x0f, 0x1d,
0x80, 0x9c, 0x3a, 0x64, 0x8c, 0x5c, 0xa5, 0x8b, 0x92, 0x4e, 0x43, 0x7d, 0xbf, 0x09, 0x6d, 0x9d,
0x19, 0xc9, 0xad, 0x44, 0x4b, 0xc1, 0xd0, 0x91, 0xbb, 0x0a, 0xab, 0xa3, 0x71, 0x74, 0x44, 0x26,
0x96, 0xd2, 0xf6, 0xa9, 0xcc, 0x35, 0x77, 0x04, 0xf8, 0x9e, 0x84, 0xe6, 0x26, 0xa5, 0x61, 0x98,
0x14, 0xf7, 0x3b, 0x35, 0x1d, 0x4f, 0xcf, 0xd7, 0x3c, 0x57, 0xca, 0xd8, 0x5e, 0x49, 0x39, 0xce,
0x09, 0x5f, 0x53, 0x24, 0xec, 0xe0, 0xa5, 0x17, 0xf5, 0xf7, 0xa1, 0x93, 0x08, 0xed, 0xa3, 0x54,
0x53, 0xe3, 0x05, 0xaa, 0x69, 0x25, 0xb1, 0xec, 0xce, 0xa7, 0x60, 0xcd, 0x1f, 0x9e, 0xf2, 0x24,
0x0b, 0xe8, 0xaa, 0x44, 0x46, 0x5f, 0x28, 0xd4, 0x55, 0x03, 0x4e, 0xb6, 0xf8, 0x2a, 0xac, 0xca,
0xfc, 0xbe, 0xc6, 0x94, 0x35, 0x69, 0x39, 0x18, 0x11, 0xdd, 0xbf, 0x50, 0xa1, 0x7b, 0xfb, 0x0c,
0xe7, 0xef, 0x88, 0xb9, 0xba, 0x5a, 0x61, 0x75, 0x9f, 0x90, 0x61, 0xf4, 0xa1, 0xba, 0x8f, 0xc9,
0x84, 0x86, 0x00, 0xca, 0xb4, 0x87, 0xbd, 0xa5, 0x8d, 0x57, 0xd9, 0x52, 0xf7, 0x63, 0x07, 0x96,
0xf6, 0xa3, 0x78, 0x5f, 0xa6, 0xea, 0x49, 0x10, 0x74, 0xf5, 0x8c, 0x6a, 0x9a, 0x5e, 0x71, 0xad,
0xe4, 0x15, 0x97, 0x6d, 0xed, 0x4a, 0xd1, 0xd6, 0xfe, 0x2a, 0xbc, 0x46, 0xd1, 0x80, 0x24, 0x8a,
0xa3, 0x04, 0x85, 0xd1, 0x1f, 0x0b, 0xc3, 0x1a, 0x85, 0xd9, 0x89, 0x52, 0x63, 0x2f, 0x42, 0xa1,
0x6b, 0x17, 0x5e, 0x17, 0x84, 0x33, 0x2c, 0x7d, 0x03, 0xa1, 0xdd, 0xca, 0x1d, 0xee, 0xe7, 0xa0,
0x49, 0xce, 0x2d, 0x2d, 0xeb, 0x2d, 0x68, 0x9e, 0x44, 0x71, 0xff, 0x24, 0x08, 0x33, 0x25, 0xdc,
0x9d, 0xdc, 0xeb, 0xdc, 0xa7, 0x0d, 0xd1, 0x08, 0xee, 0x8f, 0xeb, 0xb0, 0xf4, 0x41, 0x78, 0x1a,
0x05, 0x03, 0x8a, 0xf2, 0x4f, 0xf8, 0x24, 0x52, 0xb5, 0x44, 0xf8, 0x1b, 0xb7, 0x82, 0xf2, 0xea,
0x71, 0x26, 0xc3, 0xf4, 0xaa, 0x89, 0xe6, 0x3e, 0xc9, 0xeb, 0xfd, 0x84, 0xe8, 0x18, 0x10, 0x74,
0xec, 0x13, 0xb3, 0x34, 0x52, 0xb6, 0xf2, 0x62, 0xac, 0x05, 0xa3, 0x18, 0x8b, 0x72, 0x42, 0xa2,
0x64, 0x80, 0xf8, 0x6b, 0xd9, 0x53, 0x4d, 0xba, 0x88, 0x24, 0x5c, 0x44, 0x71, 0xc8, 0x71, 0x58,
0x92, 0x17, 0x11, 0x13, 0x88, 0xce, 0x85, 0xf8, 0x40, 0xe0, 0x08, 0xe5, 0x6b, 0x82, 0xd0, 0xd9,
0x2a, 0x56, 0x57, 0x36, 0x05, 0xcf, 0x17, 0xc0, 0xa8, 0xa1, 0x87, 0x5c, 0x2b, 0x52, 0xb1, 0x06,
0x10, 0xf5, 0x8c, 0x45, 0xb8, 0x71, 0x7d, 0x11, 0xa5, 0x0f, 0xea, 0xfa, 0x82, 0x8c, 0xe2, 0x8f,
0xc7, 0x47, 0xfe, 0xe0, 0x29, 0x15, 0xcf, 0x52, 0xa5, 0x43, 0xd3, 0xb3, 0x81, 0x48, 0xb5, 0x71,
0x9a, 0x94, 0x55, 0x6c, 0x78, 0x26, 0x88, 0xed, 0x41, 0x8b, 0xae, 0x6c, 0xf2, 0x3c, 0x3b, 0x74,
0x9e, 0x6b, 0xe6, 0x9d, 0x8e, 0x4e, 0xd4, 0x44, 0x32, 0x33, 0x0f, 0xab, 0x76, 0x31, 0xc2, 0x57,
0x81, 0xdd, 0x1c, 0x0e, 0xe5, 0x79, 0xeb, 0x2b, 0x63, 0x7e, 0x52, 0x8e, 0x75, 0x52, 0x15, 0x3b,
0x56, 0xab, 0xdc, 0x31, 0xf7, 0x2e, 0xb4, 0x0e, 0x8c, 0xb2, 0x4b, 0x62, 0x0d, 0x55, 0x70, 0x29,
0xd9, 0xc9, 0x80, 0x18, 0x13, 0xd6, 0xcc, 0x09, 0xdd, 0x5f, 0x02, 0xf6, 0x20, 0x48, 0x33, 0x4d,
0x5f, 0x5e, 0xe7, 0xa9, 0x6e, 0xee, 0x79, 0xa9, 0x44, 0x4b, 0xc2, 0xa8, 0x84, 0xe1, 0xa6, 0xa8,
0xb1, 0x28, 0x2e, 0x6c, 0x07, 0x96, 0x03, 0x01, 0x2a, 0x4a, 0x82, 0xc2, 0xd4, 0xfd, 0xe8, 0xaf,
0x49, 0xa0, 0x65, 0x45, 0x7f, 0xe8, 0xc0, 0x92, 0x5c, 0x1a, 0x7a, 0x1b, 0xa5, 0x82, 0xd3, 0xa6,
0x67, 0xc1, 0xaa, 0x4b, 0x0d, 0xcb, 0x3c, 0x5c, 0xaf, 0xe2, 0x61, 0x06, 0x8d, 0xd8, 0xcf, 0x4e,
0xe8, 0x82, 0xd2, 0xf4, 0xe8, 0x37, 0x5b, 0x13, 0x97, 0x66, 0x21, 0x2b, 0x74, 0x61, 0xae, 0xaa,
0xb6, 0x15, 0x2a, 0xb9, 0x04, 0xc7, 0x45, 0x51, 0x55, 0x82, 0x80, 0xeb, 0x64, 0x83, 0xac, 0xf8,
0xc8, 0xc1, 0xf9, 0x7e, 0xc9, 0x21, 0x8a, 0xfb, 0x25, 0x51, 0x3d, 0xdd, 0xef, 0xf6, 0xa0, 0x7b,
0x87, 0x8f, 0x79, 0xc6, 0x6f, 0x8e, 0xc7, 0xc5, 0xf1, 0x5f, 0x83, 0x0b, 0x15, 0x7d, 0xd2, 0x69,
0xb9, 0x07, 0xeb, 0x77, 0xf8, 0xd1, 0x74, 0xf4, 0x80, 0x9f, 0xe6, 0x19, 0x41, 0x06, 0x8d, 0xf4,
0x24, 0x3a, 0x93, 0x67, 0x4b, 0xbf, 0xd9, 0xeb, 0x00, 0x63, 0xc4, 0xe9, 0xa7, 0x31, 0x1f, 0xa8,
0x22, 0x3b, 0x82, 0x1c, 0xc6, 0x7c, 0xe0, 0xbe, 0x03, 0xcc, 0x1c, 0x47, 0x2e, 0x01, 0xf5, 0xc0,
0xf4, 0xa8, 0x9f, 0xce, 0xd2, 0x8c, 0x4f, 0x54, 0xf5, 0xa0, 0x09, 0x72, 0xaf, 0x42, 0xfb, 0xc0,
0x9f, 0x79, 0xfc, 0x5b, 0xb2, 0xe6, 0x17, 0xef, 0xc6, 0xfe, 0x0c, 0x59, 0x59, 0xdf, 0x8d, 0xa9,
0xdb, 0xfd, 0xaf, 0x1a, 0x2c, 0x0a, 0x4c, 0x1c, 0x75, 0xc8, 0xd3, 0x2c, 0x08, 0x45, 0x36, 0x4c,
0x8e, 0x6a, 0x80, 0x4a, 0xbc, 0x51, 0xab, 0xe0, 0x0d, 0xe9, 0xad, 0xaa, 0x82, 0x25, 0xc9, 0x04,
0x16, 0x0c, 0xdd, 0x9a, 0xbc, 0xca, 0x40, 0x5c, 0xce, 0x72, 0x40, 0x21, 0x58, 0x92, 0x6b, 0x1b,
0x41, 0x9f, 0x62, 0x5a, 0xc9, 0x0e, 0x26, 0xa8, 0x52, 0xa7, 0x2d, 0x09, 0xae, 0x29, 0xe9, 0xb4,
0x92, 0xee, 0x5a, 0x7e, 0x05, 0xdd, 0x25, 0x5c, 0xd8, 0x17, 0xe9, 0x2e, 0x78, 0x05, 0xdd, 0xe5,
0x32, 0x58, 0xbb, 0xc7, 0xb9, 0xc7, 0xd1, 0x2a, 0x2a, 0x76, 0xfa, 0x9e, 0x03, 0x6b, 0xd2, 0xa0,
0xeb, 0x3e, 0xf6, 0xa6, 0x65, 0xfd, 0x9d, 0xaa, 0x44, 0xc7, 0x15, 0x58, 0x21, 0x9b, 0xac, 0xa3,
0x42, 0x32, 0x84, 0x65, 0x01, 0x71, 0x1d, 0x2a, 0x74, 0x3f, 0x09, 0xc6, 0xf2, 0x50, 0x4c, 0x90,
0x0a, 0x2c, 0xe1, 0xfd, 0x98, 0x8e, 0xc4, 0xf1, 0x74, 0xdb, 0xfd, 0x5b, 0x07, 0xd6, 0x0d, 0x82,
0x25, 0x17, 0xbe, 0x0f, 0xaa, 0x0a, 0x41, 0x04, 0x8f, 0x84, 0x30, 0x6d, 0xdb, 0xce, 0x49, 0xfe,
0x99, 0x85, 0x4c, 0x87, 0xe9, 0xcf, 0x88, 0xc0, 0x74, 0x3a, 0x91, 0x1e, 0x88, 0x09, 0x42, 0x46,
0x3a, 0xe3, 0xfc, 0xa9, 0x46, 0xa9, 0x13, 0x8a, 0x05, 0xa3, 0x24, 0x33, 0xfa, 0x12, 0x1a, 0x49,
0xd4, 0x55, 0xd9, 0x40, 0xf7, 0x9f, 0x1d, 0xd8, 0x10, 0x4e, 0xa1, 0x74, 0xb9, 0x75, 0xcd, 0xe7,
0xa2, 0xf0, 0x82, 0x85, 0x44, 0xee, 0x9f, 0xf3, 0x64, 0x9b, 0x7d, 0xf6, 0x15, 0x1d, 0x59, 0x5d,
0x5c, 0x30, 0xe7, 0x2c, 0xea, 0x55, 0x67, 0xf1, 0x82, 0x9d, 0xae, 0x0a, 0x96, 0x2c, 0x54, 0x06,
0x4b, 0x6e, 0x2d, 0xc1, 0x42, 0x3a, 0x88, 0x62, 0xee, 0x6e, 0xc1, 0xa6, 0xbd, 0x38, 0xa9, 0x82,
0xbe, 0xef, 0x40, 0xf7, 0x9e, 0x08, 0x1d, 0x06, 0xe1, 0x68, 0x3f, 0x48, 0xb3, 0x28, 0xd1, 0x45,
0xee, 0x97, 0x00, 0xd2, 0xcc, 0x4f, 0x32, 0x51, 0xfc, 0x25, 0xc3, 0x1c, 0x39, 0x04, 0x69, 0xe4,
0xe1, 0x50, 0xf4, 0x8a, 0xb3, 0xd1, 0x6d, 0x3c, 0x18, 0x2a, 0x7c, 0xe8, 0x47, 0xc7, 0xc7, 0x29,
0xd7, 0x6e, 0xab, 0x09, 0xc3, 0x9b, 0x2f, 0x4a, 0x3c, 0xde, 0xf5, 0xf8, 0x29, 0xa9, 0x5a, 0xe1,
0x0f, 0x16, 0xa0, 0xee, 0x5f, 0x3b, 0xb0, 0x9a, 0x13, 0x79, 0x17, 0x81, 0xb6, 0x76, 0x10, 0xa4,
0x19, 0xda, 0x41, 0x05, 0x60, 0x82, 0x61, 0x3f, 0x08, 0x25, 0x6d, 0x06, 0x84, 0x24, 0x56, 0xb6,
0xa2, 0xa9, 0x2a, 0xb4, 0x33, 0x41, 0x22, 0x8b, 0x9e, 0xe1, 0xd7, 0xa2, 0xca, 0x4e, 0xb6, 0xa8,
0x76, 0x6f, 0x92, 0xd1, 0x57, 0x8b, 0xc2, 0x21, 0x96, 0x4d, 0x65, 0x9f, 0x96, 0x08, 0x8a, 0x3f,
0xdd, 0x3f, 0x72, 0xe0, 0x42, 0xc5, 0xe6, 0x4a, 0xc9, 0xb8, 0x03, 0xeb, 0xc7, 0xba, 0x53, 0x6d,
0x80, 0x10, 0x8f, 0x2d, 0x15, 0xeb, 0xb6, 0x17, 0xed, 0x95, 0x3f, 0x40, 0xf7, 0x98, 0xe2, 0x46,
0x62, 0x4b, 0xad, 0x02, 0x94, 0x72, 0xc7, 0xde, 0x1f, 0xd7, 0xa1, 0x23, 0x72, 0x20, 0xe2, 0xb9,
0x19, 0x4f, 0xd8, 0x87, 0xb0, 0x24, 0x9f, 0x0b, 0xb2, 0xf3, 0x72, 0x5a, 0xfb, 0x81, 0x62, 0x6f,
0xab, 0x08, 0x96, 0xbc, 0xb3, 0xf1, 0xbb, 0x1f, 0xff, 0xeb, 0x9f, 0xd4, 0x56, 0x58, 0x6b, 0xf7,
0xf4, 0xed, 0xdd, 0x11, 0x0f, 0x53, 0x1c, 0xe3, 0x37, 0x00, 0xf2, 0x87, 0x74, 0xac, 0xab, 0x9d,
0x8c, 0xc2, 0x0b, 0xc1, 0xde, 0x85, 0x8a, 0x1e, 0x39, 0xee, 0x05, 0x1a, 0x77, 0xc3, 0xed, 0xe0,
0xb8, 0x41, 0x18, 0x64, 0xe2, 0x55, 0xdd, 0x7b, 0xce, 0x0e, 0x1b, 0x42, 0xdb, 0x7c, 0x27, 0xc7,
0xd4, 0x95, 0xb9, 0xe2, 0x95, 0x5e, 0xef, 0xb5, 0xca, 0x3e, 0x15, 0x2f, 0xa0, 0x39, 0xce, 0xbb,
0x6b, 0x38, 0xc7, 0x94, 0x30, 0xf2, 0x59, 0xc6, 0xd0, 0xb1, 0x9f, 0xc3, 0xb1, 0x8b, 0x86, 0x58,
0x97, 0x1e, 0xe3, 0xf5, 0x5e, 0x9f, 0xd3, 0x2b, 0xe7, 0x7a, 0x9d, 0xe6, 0xda, 0x76, 0x19, 0xce,
0x35, 0x20, 0x1c, 0xf5, 0x18, 0xef, 0x3d, 0x67, 0x67, 0xef, 0xbb, 0xaf, 0x43, 0x53, 0x07, 0xb9,
0xd8, 0x37, 0x61, 0xc5, 0x4a, 0x52, 0x31, 0xb5, 0x8c, 0xaa, 0x9c, 0x56, 0xef, 0x62, 0x75, 0xa7,
0x9c, 0xf8, 0x12, 0x4d, 0xdc, 0x65, 0x5b, 0x38, 0xb1, 0xcc, 0xf2, 0xec, 0x52, 0x6a, 0x4e, 0x54,
0x06, 0x3e, 0x15, 0xeb, 0xcc, 0x13, 0x4b, 0xd6, 0x3a, 0x4b, 0x89, 0x28, 0x6b, 0x9d, 0xe5, 0x6c,
0x94, 0x7b, 0x91, 0xa6, 0xdb, 0x62, 0x9b, 0xe6, 0x74, 0x3a, 0xf8, 0xc4, 0xa9, 0x96, 0xd3, 0x7c,
0x2d, 0xc7, 0x5e, 0xd7, 0x8c, 0x55, 0xf5, 0x8a, 0x4e, 0xb3, 0x48, 0xf9, 0x29, 0x9d, 0xdb, 0xa5,
0xa9, 0x18, 0xa3, 0xe3, 0x33, 0x1f, 0xcb, 0xb1, 0xaf, 0x43, 0x53, 0x3f, 0x0d, 0x61, 0xdb, 0xc6,
0x7b, 0x1c, 0xf3, 0xbd, 0x4a, 0xaf, 0x5b, 0xee, 0xa8, 0x62, 0x0c, 0x73, 0x64, 0x64, 0x8c, 0x07,
0x70, 0x5e, 0xba, 0xc4, 0x47, 0xfc, 0x27, 0x59, 0x49, 0xc5, 0x1b, 0xbf, 0x1b, 0x0e, 0x7b, 0x1f,
0x96, 0xd5, 0x8b, 0x1b, 0xb6, 0x55, 0xfd, 0x72, 0xa8, 0xb7, 0x5d, 0x82, 0x4b, 0xed, 0x71, 0x13,
0x20, 0x7f, 0x2d, 0xa2, 0xe5, 0xac, 0xf4, 0x86, 0x45, 0x6f, 0x62, 0xc5, 0xd3, 0x92, 0x11, 0xbd,
0x8d, 0xb1, 0x1f, 0xa3, 0xb0, 0x37, 0x72, 0xfc, 0xca, 0x67, 0x2a, 0x2f, 0x18, 0xd0, 0xdd, 0xa2,
0xbd, 0x5b, 0x63, 0x24, 0xb8, 0x21, 0x3f, 0x53, 0x55, 0xcd, 0x77, 0xa0, 0x65, 0xbc, 0x40, 0x61,
0x6a, 0x84, 0xf2, 0xeb, 0x95, 0x5e, 0xaf, 0xaa, 0x4b, 0x92, 0xfb, 0x45, 0x58, 0xb1, 0x9e, 0x92,
0x68, 0xc9, 0xa8, 0x7a, 0xa8, 0xa2, 0x25, 0xa3, 0xfa, 0xf5, 0xc9, 0xd7, 0xa0, 0x65, 0x3c, 0xfc,
0x60, 0x46, 0x35, 0x57, 0xe1, 0xc9, 0x87, 0xa6, 0xa8, 0xea, 0x9d, 0xc8, 0x26, 0xad, 0xb7, 0xe3,
0x36, 0x71, 0xbd, 0x54, 0xda, 0x8b, 0x4c, 0xf2, 0x4d, 0xe8, 0xd8, 0x4f, 0x41, 0xb4, 0x54, 0x55,
0x3e, 0x2a, 0xd1, 0x52, 0x35, 0xe7, 0xfd, 0x88, 0x64, 0xc8, 0x9d, 0x0d, 0x3d, 0xc9, 0xee, 0x47,
0x32, 0xc5, 0xf3, 0x9c, 0x7d, 0x05, 0x55, 0x87, 0xac, 0xb5, 0x66, 0xf9, 0x03, 0x18, 0xbb, 0x22,
0x5b, 0x73, 0x7b, 0xa9, 0x2c, 0xdb, 0x5d, 0xa7, 0xc1, 0x5b, 0x2c, 0x5f, 0x81, 0xb0, 0x07, 0x54,
0x73, 0x6d, 0xd8, 0x03, 0xb3, 0x2c, 0xdb, 0xb0, 0x07, 0x56, 0x69, 0x76, 0xd1, 0x1e, 0x64, 0x01,
0x8e, 0x11, 0xc2, 0x6a, 0xa1, 0x9c, 0x41, 0x0b, 0x4b, 0x75, 0xfd, 0x57, 0xef, 0xd2, 0x8b, 0xab,
0x20, 0x6c, 0x35, 0xa3, 0xd4, 0xcb, 0xae, 0x2a, 0xd7, 0xfb, 0x4d, 0x68, 0x9b, 0x25, 0xfc, 0xda,
0x42, 0x54, 0x3c, 0x3c, 0xd0, 0x16, 0xa2, 0xaa, 0xe6, 0x5f, 0x1d, 0x2e, 0x6b, 0x9b, 0xd3, 0xb0,
0xaf, 0xc1, 0xaa, 0x51, 0xbf, 0x73, 0x38, 0x0b, 0x07, 0x9a, 0x79, 0xca, 0xd5, 0x9d, 0xbd, 0x2a,
0x6f, 0xd0, 0xdd, 0xa6, 0x81, 0xd7, 0x5d, 0x6b, 0x60, 0x64, 0x9c, 0xdb, 0xd0, 0x32, 0x6b, 0x83,
0x5e, 0x30, 0xee, 0xb6, 0xd1, 0x65, 0x16, 0x3a, 0xde, 0x70, 0xd8, 0x9f, 0x3a, 0xd0, 0xb6, 0x2a,
0x6d, 0xac, 0xa8, 0x72, 0x61, 0x9c, 0xae, 0xd9, 0x67, 0x0e, 0xe4, 0x7a, 0x44, 0xe4, 0x83, 0x9d,
0x2f, 0x5a, 0x9b, 0xfc, 0x91, 0x75, 0xab, 0xb8, 0x5e, 0x7c, 0x9d, 0xf9, 0xbc, 0x88, 0x60, 0x56,
0xc0, 0x3e, 0xbf, 0xe1, 0xb0, 0xf7, 0xc4, 0xfb, 0x63, 0x15, 0x45, 0x60, 0x86, 0x72, 0x2b, 0x6e,
0x99, 0xf9, 0xf8, 0xf6, 0x9a, 0x73, 0xc3, 0x61, 0xdf, 0x10, 0x8f, 0x30, 0xe5, 0xb7, 0xb4, 0xf3,
0xaf, 0xfa, 0xbd, 0x7b, 0x85, 0x56, 0x73, 0xc9, 0xbd, 0x60, 0xad, 0xa6, 0xa8, 0xdd, 0x6f, 0x0a,
0xea, 0xe4, 0xdb, 0xda, 0x5c, 0x4d, 0x95, 0xde, 0xdb, 0xce, 0x27, 0x72, 0x22, 0x88, 0x94, 0xe8,
0x16, 0x7b, 0xbc, 0xe2, 0x30, 0xee, 0x0e, 0xd1, 0x7a, 0xc5, 0x7d, 0x63, 0x2e, 0xad, 0xbb, 0x74,
0x4b, 0x44, 0x8a, 0x0f, 0x00, 0xf2, 0x20, 0x16, 0x2b, 0x44, 0x74, 0xb4, 0xa6, 0x2e, 0xc7, 0xb9,
0x6c, 0x1e, 0x54, 0x81, 0x1f, 0x1c, 0xf1, 0xeb, 0x42, 0x7c, 0x24, 0x7e, 0xaa, 0xa9, 0x2f, 0x07,
0xa3, 0x7a, 0xbd, 0xaa, 0xae, 0x2a, 0xe1, 0x51, 0xe3, 0xb3, 0xc7, 0xb0, 0xf2, 0x20, 0x8a, 0x9e,
0x4e, 0x63, 0x1d, 0x66, 0xb5, 0x63, 0x2a, 0xfb, 0x7e, 0x7a, 0xd2, 0x2b, 0xac, 0xc2, 0xbd, 0x4c,
0x43, 0xf5, 0x58, 0xd7, 0x18, 0x6a, 0xf7, 0xa3, 0x3c, 0x84, 0xf6, 0x9c, 0xf9, 0xb0, 0xae, 0xad,
0xb2, 0x26, 0xbc, 0x67, 0x0f, 0x63, 0x46, 0xb2, 0x4a, 0x53, 0x58, 0x7e, 0x92, 0xa2, 0x76, 0x37,
0x55, 0x63, 0xde, 0x70, 0xd8, 0x01, 0xb4, 0xef, 0xf0, 0x41, 0x34, 0xe4, 0x32, 0x0a, 0xb2, 0x91,
0x13, 0xae, 0xc3, 0x27, 0xbd, 0x15, 0x0b, 0x68, 0xeb, 0xa9, 0xd8, 0x9f, 0x25, 0xfc, 0x5b, 0xbb,
0x1f, 0xc9, 0xf8, 0xca, 0x73, 0xa5, 0xa7, 0x54, 0x4c, 0xc8, 0xd2, 0x53, 0x85, 0x20, 0x92, 0xa5,
0xa7, 0x4a, 0x41, 0x24, 0x6b, 0xab, 0x55, 0x4c, 0x8a, 0x8d, 0x61, 0xbd, 0x14, 0x77, 0xd2, 0xb6,
0x7d, 0x5e, 0xb4, 0xaa, 0x77, 0x79, 0x3e, 0x82, 0x3d, 0xdb, 0x8e, 0x3d, 0xdb, 0x21, 0xac, 0xdc,
0xe1, 0x62, 0xb3, 0x44, 0x2e, 0xb7, 0x67, 0x2b, 0x3e, 0x33, 0xef, 0x5b, 0x54, 0x8a, 0xd4, 0x67,
0x1b, 0x22, 0x4a, 0xa4, 0xb2, 0xaf, 0x43, 0xeb, 0x3e, 0xcf, 0x54, 0xf2, 0x56, 0x7b, 0x48, 0x85,
0x6c, 0x6e, 0xaf, 0x22, 0xf7, 0x6b, 0xf3, 0x0c, 0x8d, 0xb6, 0xcb, 0x87, 0x23, 0x2e, 0xd4, 0x53,
0x3f, 0x18, 0x3e, 0x67, 0xbf, 0x46, 0x83, 0xeb, 0x7a, 0x8f, 0x2d, 0x23, 0xe7, 0x67, 0x0e, 0xbe,
0x5a, 0x80, 0x57, 0x8d, 0x1c, 0x46, 0x43, 0x6e, 0x98, 0xe4, 0x10, 0x5a, 0x46, 0x31, 0x92, 0x16,
0xa0, 0x72, 0x61, 0x95, 0x16, 0xa0, 0x8a, 0xda, 0x25, 0xf7, 0x1a, 0xcd, 0xe3, 0xb2, 0xcb, 0xf9,
0x3c, 0xa2, 0x5e, 0x29, 0x9f, 0x69, 0xf7, 0x23, 0x7f, 0x92, 0x3d, 0x67, 0x4f, 0xe8, 0xd9, 0x94,
0x99, 0xa0, 0xce, 0x3d, 0xb4, 0x62, 0x2e, 0x5b, 0x6f, 0x96, 0xd1, 0x65, 0x7b, 0x6d, 0x62, 0x2a,
0xb2, 0xdc, 0x9f, 0x05, 0x38, 0xcc, 0xa2, 0xf8, 0x8e, 0xcf, 0x27, 0x51, 0x98, 0xeb, 0xda, 0x3c,
0x09, 0x9b, 0xeb, 0x2f, 0x23, 0x13, 0xcb, 0x9e, 0x18, 0x3e, 0xb2, 0x95, 0xdf, 0x57, 0xcc, 0x35,
0x37, 0x4f, 0xab, 0x37, 0xa4, 0x22, 0x57, 0x7b, 0xc3, 0x41, 0x8f, 0x37, 0x8f, 0x72, 0x6a, 0x8f,
0xb7, 0x14, 0x40, 0xd5, 0x6a, 0xaf, 0x22, 0x24, 0x7a, 0x00, 0xcd, 0x3c, 0x6c, 0xb6, 0x9d, 0x17,
0x94, 0x59, 0x41, 0x36, 0x6d, 0x15, 0x4b, 0xc1, 0x2c, 0x77, 0x8d, 0xb6, 0x0a, 0xd8, 0x32, 0x6e,
0x15, 0x45, 0xa8, 0x02, 0xd8, 0x10, 0x04, 0x6a, 0x13, 0x4f, 0x69, 0x45, 0xb5, 0x92, 0x8a, 0x80,
0x92, 0x96, 0xe6, 0xca, 0x78, 0x8c, 0x75, 0xf7, 0x45, 0x6e, 0x15, 0x29, 0x4d, 0x54, 0xcd, 0x13,
0x58, 0x2f, 0x05, 0x13, 0xb4, 0x48, 0xcf, 0x8b, 0xe1, 0x68, 0x91, 0x9e, 0x1b, 0x87, 0x70, 0xcf,
0xd3, 0x94, 0xab, 0x2e, 0xe0, 0x94, 0xe9, 0x59, 0x90, 0x0d, 0x4e, 0xde, 0x73, 0x76, 0x8e, 0x16,
0xe9, 0xff, 0x8a, 0x3e, 0xfd, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xa1, 0x16, 0xf4, 0xe1,
0x48, 0x00, 0x00,
}

View File

@ -646,6 +646,16 @@ message TransactionDetails {
repeated Transaction transactions = 1 [json_name = "transactions"];
}
message FeeLimit {
oneof limit {
/// The fee limit expressed as a fixed amount of satoshis.
int64 fixed = 1;
/// The fee limit expressed as a percentage of the payment amount.
int64 percent = 2;
}
}
message SendRequest {
/// The identity pubkey of the payment recipient
bytes dest = 1;
@ -653,7 +663,7 @@ message SendRequest {
/// The hex-encoded identity pubkey of the payment recipient
string dest_string = 2;
/// Number of satoshis to send.
/// Number of satoshis to send.
int64 amt = 3;
/// The hash to use within the payment's HTLC
@ -669,8 +679,19 @@ message SendRequest {
*/
string payment_request = 6;
/// The CLTV delta from the current height that should be used to set the timelock for the final hop.
/**
The CLTV delta from the current height that should be used to set the
timelock for the final hop.
*/
int32 final_cltv_delta = 7;
/**
The maximum number of satoshis that will be paid as a fee of the payment.
This value can be represented either as a percentage of the amount being
sent, or as a fixed amount of the maximum fee the user is willing the pay to
send the payment.
*/
FeeLimit fee_limit = 8;
}
message SendResponse {
string payment_error = 1 [json_name = "payment_error"];
@ -1230,9 +1251,17 @@ message QueryRoutesRequest {
/// An optional CLTV delta from the current height that should be used for the timelock of the final hop
int32 final_cltv_delta = 4;
/**
The maximum number of satoshis that will be paid as a fee of the payment.
This value can be represented either as a percentage of the amount being
sent, or as a fixed amount of the maximum fee the user is willing the pay to
send the payment.
*/
FeeLimit fee_limit = 5;
}
message QueryRoutesResponse {
repeated Route routes = 1 [ json_name = "routes"];
repeated Route routes = 1 [json_name = "routes"];
}
message Hop {

View File

@ -474,6 +474,22 @@
"required": false,
"type": "integer",
"format": "int32"
},
{
"name": "fee_limit.fixed",
"description": "/ The fee limit expressed as a fixed amount of satoshis.",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
},
{
"name": "fee_limit.percent",
"description": "/ The fee limit expressed as a percentage of the payment amount.",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
}
],
"tags": [
@ -1332,6 +1348,21 @@
"lnrpcDisconnectPeerResponse": {
"type": "object"
},
"lnrpcFeeLimit": {
"type": "object",
"properties": {
"fixed": {
"type": "string",
"format": "int64",
"description": "/ The fee limit expressed as a fixed amount of satoshis."
},
"percent": {
"type": "string",
"format": "int64",
"description": "/ The fee limit expressed as a percentage of the payment amount."
}
}
},
"lnrpcFeeReportResponse": {
"type": "object",
"properties": {
@ -2380,7 +2411,11 @@
"final_cltv_delta": {
"type": "integer",
"format": "int32",
"description": "/ The CLTV delta from the current height that should be used to set the timelock for the final hop."
"description": "*\nThe CLTV delta from the current height that should be used to set the\ntimelock for the final hop."
},
"fee_limit": {
"$ref": "#/definitions/lnrpcFeeLimit",
"description": "*\nThe maximum number of satoshis that will be paid as a fee of the payment.\nThis value can be represented either as a percentage of the amount being\nsent, or as a fixed amount of the maximum fee the user is willing the pay to\nsend the payment."
}
}
},

View File

@ -42,6 +42,10 @@ const (
// ErrPaymentAttemptTimeout is an error that indicates that a payment
// attempt timed out before we were able to successfully route an HTLC.
ErrPaymentAttemptTimeout
// ErrFeeLimitExceeded is returned when the total fees of a route exceed
// the user-specified fee limit.
ErrFeeLimitExceeded
)
// routerError is a structure that represent the error inside the routing package,

View File

@ -384,7 +384,8 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
// a route by applying the time-lock and fee requirements.
sourceVertex := Vertex(p.mc.selfNode.PubKeyBytes)
route, err := newRoute(
payment.Amount, sourceVertex, path, height, finalCltvDelta,
payment.Amount, payment.FeeLimit, sourceVertex, path, height,
finalCltvDelta,
)
if err != nil {
// TODO(roasbeef): return which edge/vertex didn't work

View File

@ -248,7 +248,7 @@ func (r *Route) ToHopPayloads() []sphinx.HopData {
//
// NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
// the source to the target node of the path finding attempt.
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
func newRoute(amtToSend, feeLimit lnwire.MilliSatoshi, sourceVertex Vertex,
pathEdges []*ChannelHop, currentHeight uint32,
finalCLTVDelta uint16) (*Route, error) {
@ -265,9 +265,6 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
prevHopMap: make(map[Vertex]*ChannelHop),
}
// TODO(roasbeef): need to do sanity check to ensure we don't make a
// "dust" payment: over x% of money sending to fees
// We'll populate the next hop map for the _source_ node with the
// information for the first hop so the mapping is sound.
route.nextHopMap[sourceVertex] = pathEdges[0]
@ -342,6 +339,13 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex Vertex,
route.TotalFees += nextHop.Fee
// Invalidate this route if its total fees exceed our fee limit.
if route.TotalFees > feeLimit {
err := fmt.Sprintf("total route fees exceeded fee "+
"limit of %v", feeLimit)
return nil, newErrf(ErrFeeLimitExceeded, err)
}
// As a sanity check, we ensure that the selected channel has
// enough capacity to forward the required amount which
// includes the fee dictated at each hop.

View File

@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"io/ioutil"
"math"
"math/big"
"net"
"os"
@ -40,6 +41,11 @@ const (
// implementations will use in order to ensure that they're calculating
// the payload for each hop in path properly.
specExampleFilePath = "testdata/spec_example.json"
// noFeeLimit is the maximum value of a payment through Lightning. We
// can use this value to signal there is no fee limit since payments
// should never be larger than this.
noFeeLimit = lnwire.MilliSatoshi(math.MaxUint32)
)
var (
@ -318,8 +324,11 @@ func TestBasicGraphPathFinding(t *testing.T) {
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
route, err := newRoute(paymentAmt, sourceVertex, path, startingHeight,
finalHopCLTV)
route, err := newRoute(
paymentAmt, noFeeLimit, sourceVertex, path, startingHeight,
finalHopCLTV,
)
if err != nil {
t.Fatalf("unable to create path: %v", err)
}
@ -460,8 +469,11 @@ func TestBasicGraphPathFinding(t *testing.T) {
if err != nil {
t.Fatalf("unable to find route: %v", err)
}
route, err = newRoute(paymentAmt, sourceVertex, path, startingHeight,
finalHopCLTV)
route, err = newRoute(
paymentAmt, noFeeLimit, sourceVertex, path, startingHeight,
finalHopCLTV,
)
if err != nil {
t.Fatalf("unable to create path: %v", err)
}
@ -729,6 +741,8 @@ func TestPathInsufficientCapacity(t *testing.T) {
// TestRouteFailMinHTLC tests that if we attempt to route an HTLC which is
// smaller than the advertised minHTLC of an edge, then path finding fails.
func TestRouteFailMinHTLC(t *testing.T) {
t.Parallel()
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
defer cleanUp()
if err != nil {
@ -760,6 +774,8 @@ func TestRouteFailMinHTLC(t *testing.T) {
// that's disabled, then that edge is disqualified, and the routing attempt
// will fail.
func TestRouteFailDisabledEdge(t *testing.T) {
t.Parallel()
graph, cleanUp, aliases, err := parseTestGraph(basicGraphFilePath)
defer cleanUp()
if err != nil {
@ -807,6 +823,48 @@ func TestRouteFailDisabledEdge(t *testing.T) {
}
}
// TestRouteExceededFeeLimit tests that routes respect the fee limit imposed.
func TestRouteExceededFeeLimit(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)
}
sourceVertex := Vertex(sourceNode.PubKeyBytes)
ignoredVertices := make(map[Vertex]struct{})
ignoredEdges := make(map[uint64]struct{})
// Find a path to send 100 satoshis from roasbeef to sophon.
target := aliases["sophon"]
amt := lnwire.NewMSatFromSatoshis(100)
path, err := findPath(
nil, graph, nil, sourceNode, target, ignoredVertices,
ignoredEdges, amt, nil,
)
if err != nil {
t.Fatalf("unable to find path from roasbeef to phamnuwen for "+
"100 satoshis: %v", err)
}
// We'll now purposefully set a fee limit of 0 to trigger the exceeded
// fee limit error. This should work since the path retrieved spans
// multiple hops incurring a fee.
feeLimit := lnwire.NewMSatFromSatoshis(0)
_, err = newRoute(amt, feeLimit, sourceVertex, path, 100, 1)
if !IsError(err, ErrFeeLimitExceeded) {
t.Fatalf("route should've exceeded fee limit: %v", err)
}
}
func TestPathInsufficientCapacityWithFee(t *testing.T) {
t.Parallel()
@ -852,7 +910,7 @@ func TestPathFindSpecExample(t *testing.T) {
// Query for a route of 4,999,999 mSAT to carol.
carol := ctx.aliases["C"]
const amt lnwire.MilliSatoshi = 4999999
routes, err := ctx.router.FindRoutes(carol, amt, 100)
routes, err := ctx.router.FindRoutes(carol, amt, noFeeLimit, 100)
if err != nil {
t.Fatalf("unable to find route: %v", err)
}
@ -912,7 +970,7 @@ func TestPathFindSpecExample(t *testing.T) {
// We'll now request a route from A -> B -> C.
ctx.router.routeCache = make(map[routeTuple][]*Route)
routes, err = ctx.router.FindRoutes(carol, amt, 100)
routes, err = ctx.router.FindRoutes(carol, amt, noFeeLimit, 100)
if err != nil {
t.Fatalf("unable to find routes: %v", err)
}

View File

@ -1258,8 +1258,9 @@ func pruneChannelFromRoutes(routes []*Route, skipChan uint64) []*Route {
// fee information attached. The set of routes returned may be less than the
// initial set of paths as it's possible we drop a route if it can't handle the
// total payment flow after fees are calculated.
func pathsToFeeSortedRoutes(source Vertex, paths [][]*ChannelHop, finalCLTVDelta uint16,
amt lnwire.MilliSatoshi, currentHeight uint32) ([]*Route, error) {
func pathsToFeeSortedRoutes(source Vertex, paths [][]*ChannelHop,
finalCLTVDelta uint16, amt, feeLimit lnwire.MilliSatoshi,
currentHeight uint32) ([]*Route, error) {
validRoutes := make([]*Route, 0, len(paths))
for _, path := range paths {
@ -1267,7 +1268,8 @@ func pathsToFeeSortedRoutes(source Vertex, paths [][]*ChannelHop, finalCLTVDelta
// hop in the path as it contains a "self-hop" that is inserted
// by our KSP algorithm.
route, err := newRoute(
amt, source, path[1:], currentHeight, finalCLTVDelta,
amt, feeLimit, source, path[1:], currentHeight,
finalCLTVDelta,
)
if err != nil {
// TODO(roasbeef): report straw breaking edge?
@ -1316,7 +1318,8 @@ func pathsToFeeSortedRoutes(source Vertex, paths [][]*ChannelHop, finalCLTVDelta
// route that will be ranked the highest is the one with the lowest cumulative
// fee along the route.
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
amt lnwire.MilliSatoshi, numPaths uint32, finalExpiry ...uint16) ([]*Route, error) {
amt, feeLimit lnwire.MilliSatoshi, numPaths uint32,
finalExpiry ...uint16) ([]*Route, error) {
var finalCLTVDelta uint16
if len(finalExpiry) == 0 {
@ -1402,7 +1405,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// factored in.
sourceVertex := Vertex(r.selfNode.PubKeyBytes)
validRoutes, err := pathsToFeeSortedRoutes(
sourceVertex, shortestPaths, finalCLTVDelta, amt,
sourceVertex, shortestPaths, finalCLTVDelta, amt, feeLimit,
uint32(currentHeight),
)
if err != nil {
@ -1502,6 +1505,11 @@ type LightningPayment struct {
// milli-satoshis.
Amount lnwire.MilliSatoshi
// FeeLimit is the maximum fee in millisatoshis that the payment should
// accept when sending it through the network. The payment will fail
// if there isn't a route with lower fees than this limit.
FeeLimit lnwire.MilliSatoshi
// PaymentHash is the r-hash value to use within the HTLC extended to
// the first hop.
PaymentHash [32]byte

View File

@ -175,8 +175,10 @@ func TestFindRoutesFeeSorting(t *testing.T) {
// Execute a query for all possible routes between roasbeef and luo ji.
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := ctx.aliases["luoji"]
routes, err := ctx.router.FindRoutes(target, paymentAmt,
defaultNumRoutes, DefaultFinalCLTVDelta)
routes, err := ctx.router.FindRoutes(
target, paymentAmt, noFeeLimit, defaultNumRoutes,
DefaultFinalCLTVDelta,
)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
@ -206,6 +208,59 @@ func TestFindRoutesFeeSorting(t *testing.T) {
}
}
// TestFindRoutesWithFeeLimit asserts that routes found by the FindRoutes method
// within the channel router contain a total fee less than or equal to the fee
// limit.
func TestFindRoutesWithFeeLimit(t *testing.T) {
t.Parallel()
const startingBlockHeight = 101
ctx, cleanUp, err := createTestCtx(
startingBlockHeight, basicGraphFilePath,
)
defer cleanUp()
if err != nil {
t.Fatalf("unable to create router: %v", err)
}
// This test will attempt to find routes from roasbeef to sophon for 100
// satoshis with a fee limit of 10 satoshis. There are two routes from
// roasbeef to sophon:
// 1. roasbeef -> songoku -> sophon
// 2. roasbeef -> phamnuwen -> sophon
// The second route violates our fee limit, so we should only expect to
// see the first route.
target := ctx.aliases["sophon"]
paymentAmt := lnwire.NewMSatFromSatoshis(100)
feeLimit := lnwire.NewMSatFromSatoshis(10)
routes, err := ctx.router.FindRoutes(
target, paymentAmt, feeLimit, defaultNumRoutes,
DefaultFinalCLTVDelta,
)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
if len(routes) != 1 {
t.Fatalf("expected 1 route, got %d", len(routes))
}
if routes[0].TotalFees > feeLimit {
t.Fatalf("route exceeded fee limit: %v", spew.Sdump(routes[0]))
}
hops := routes[0].Hops
if len(hops) != 2 {
t.Fatalf("expected 2 hops, got %d", len(hops))
}
if hops[0].Channel.Node.Alias != "songoku" {
t.Fatalf("expected first hop through songoku, got %s",
hops[0].Channel.Node.Alias)
}
}
// TestSendPaymentRouteFailureFallback tests that when sending a payment, if
// one of the target routes is seen as unavailable, then the next route in the
// queue is used instead. This process should continue until either a payment
@ -221,11 +276,13 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
}
// Craft a LightningPayment struct that'll send a payment from roasbeef
// to luo ji for 100 satoshis.
// to luo ji for 1000 satoshis, with a maximum of 1000 satoshis in fees.
var payHash [32]byte
paymentAmt := lnwire.NewMSatFromSatoshis(1000)
payment := LightningPayment{
Target: ctx.aliases["luoji"],
Amount: lnwire.NewMSatFromSatoshis(1000),
Amount: paymentAmt,
FeeLimit: noFeeLimit,
PaymentHash: payHash,
}
@ -299,9 +356,11 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
// Craft a LightningPayment struct that'll send a payment from roasbeef
// to luo ji for 100 satoshis.
var payHash [32]byte
amt := lnwire.NewMSatFromSatoshis(1000)
payment := LightningPayment{
Target: ctx.aliases["sophon"],
Amount: lnwire.NewMSatFromSatoshis(1000),
Amount: amt,
FeeLimit: noFeeLimit,
PaymentHash: payHash,
}
@ -397,9 +456,11 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
// Craft a LightningPayment struct that'll send a payment from roasbeef
// to sophon for 1k satoshis.
var payHash [32]byte
amt := lnwire.NewMSatFromSatoshis(1000)
payment := LightningPayment{
Target: ctx.aliases["sophon"],
Amount: lnwire.NewMSatFromSatoshis(1000),
Amount: amt,
FeeLimit: noFeeLimit,
PaymentHash: payHash,
}
@ -524,11 +585,13 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
}
// Craft a LightningPayment struct that'll send a payment from roasbeef
// to luo ji for 100 satoshis.
// to luo ji for 1000 satoshis, with a maximum of 1000 satoshis in fees.
var payHash [32]byte
paymentAmt := lnwire.NewMSatFromSatoshis(1000)
payment := LightningPayment{
Target: ctx.aliases["luoji"],
Amount: lnwire.NewMSatFromSatoshis(1000),
Amount: paymentAmt,
FeeLimit: noFeeLimit,
PaymentHash: payHash,
}
@ -966,8 +1029,10 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// We should now be able to find two routes to node 2.
paymentAmt := lnwire.NewMSatFromSatoshis(100)
targetNode := priv2.PubKey()
routes, err := ctx.router.FindRoutes(targetNode, paymentAmt,
defaultNumRoutes, DefaultFinalCLTVDelta)
routes, err := ctx.router.FindRoutes(
targetNode, paymentAmt, noFeeLimit, defaultNumRoutes,
DefaultFinalCLTVDelta,
)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
@ -1009,8 +1074,10 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// Should still be able to find the routes, and the info should be
// updated.
routes, err = ctx.router.FindRoutes(targetNode, paymentAmt,
defaultNumRoutes, DefaultFinalCLTVDelta)
routes, err = ctx.router.FindRoutes(
targetNode, paymentAmt, noFeeLimit, defaultNumRoutes,
DefaultFinalCLTVDelta,
)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}

View File

@ -1765,6 +1765,27 @@ type rpcPaymentRequest struct {
routes []*routing.Route
}
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
// based fee limit has been requested, we'll factor in the ratio provided with
// the amount of the payment.
func calculateFeeLimit(feeLimit *lnrpc.FeeLimit,
amount lnwire.MilliSatoshi) lnwire.MilliSatoshi {
switch feeLimit.GetLimit().(type) {
case *lnrpc.FeeLimit_Fixed:
return lnwire.NewMSatFromSatoshis(
btcutil.Amount(feeLimit.GetFixed()),
)
case *lnrpc.FeeLimit_Percent:
return amount * lnwire.MilliSatoshi(feeLimit.GetPercent()) / 100
default:
// If a fee limit was not specified, we'll use the payment's
// amount as an upper bound in order to avoid payment attempts
// from incurring fees higher than the payment amount itself.
return amount
}
}
// SendPayment dispatches a bi-directional streaming RPC for sending payments
// through the Lightning Network. A single RPC invocation creates a persistent
// bi-directional stream allowing clients to rapidly send payments through the
@ -1831,6 +1852,7 @@ func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error
// directly to the channel router for dispatching.
type rpcPaymentIntent struct {
msat lnwire.MilliSatoshi
feeLimit lnwire.MilliSatoshi
dest *btcec.PublicKey
rHash [32]byte
cltvDelta uint16
@ -1897,57 +1919,68 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
payIntent.msat = *payReq.MilliSat
}
// Calculate the fee limit that should be used for this payment.
payIntent.feeLimit = calculateFeeLimit(
rpcPayReq.FeeLimit, payIntent.msat,
)
copy(payIntent.rHash[:], payReq.PaymentHash[:])
payIntent.dest = payReq.Destination
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
payIntent.routeHints = payReq.RouteHints
return payIntent, nil
} else {
// At this point, a destination MUST be specified, so we'll convert it
// into the proper representation now. The destination will either be
// encoded as raw bytes, or via a hex string.
if len(rpcPayReq.Dest) != 0 {
payIntent.dest, err = btcec.ParsePubKey(
rpcPayReq.Dest, btcec.S256(),
)
if err != nil {
return payIntent, err
}
}
} else {
pubBytes, err := hex.DecodeString(rpcPayReq.DestString)
if err != nil {
return payIntent, err
}
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil {
return payIntent, err
}
}
// Otherwise, If the payment request field was not specified
// (and a custom route wasn't specified), construct the payment
// from the other fields.
payIntent.msat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.Amt),
// At this point, a destination MUST be specified, so we'll convert it
// into the proper representation now. The destination will either be
// encoded as raw bytes, or via a hex string.
if len(rpcPayReq.Dest) != 0 {
payIntent.dest, err = btcec.ParsePubKey(
rpcPayReq.Dest, btcec.S256(),
)
payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta)
// If the user is manually specifying payment details, then the
// payment hash may be encoded as a string.
if rpcPayReq.PaymentHashString != "" {
paymentHash, err := hex.DecodeString(
rpcPayReq.PaymentHashString,
)
if err != nil {
return payIntent, err
}
copy(payIntent.rHash[:], paymentHash)
} else {
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
if err != nil {
return payIntent, err
}
} else {
pubBytes, err := hex.DecodeString(rpcPayReq.DestString)
if err != nil {
return payIntent, err
}
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil {
return payIntent, err
}
}
// Otherwise, If the payment request field was not specified
// (and a custom route wasn't specified), construct the payment
// from the other fields.
payIntent.msat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.Amt),
)
// Calculate the fee limit that should be used for this payment.
payIntent.feeLimit = calculateFeeLimit(
rpcPayReq.FeeLimit, payIntent.msat,
)
payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta)
// If the user is manually specifying payment details, then the
// payment hash may be encoded as a string.
if rpcPayReq.PaymentHashString != "" {
paymentHash, err := hex.DecodeString(
rpcPayReq.PaymentHashString,
)
if err != nil {
return payIntent, err
}
copy(payIntent.rHash[:], paymentHash)
} else {
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
}
// If we're in debug HTLC mode, then all outgoing HTLCs will pay to the
@ -1993,6 +2026,7 @@ func (r *rpcServer) dispatchPaymentIntent(payIntent *rpcPaymentIntent) (*routing
payment := &routing.LightningPayment{
Target: payIntent.dest,
Amount: payIntent.msat,
FeeLimit: payIntent.feeLimit,
PaymentHash: payIntent.rHash,
RouteHints: payIntent.routeHints,
}
@ -2052,9 +2086,6 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
payChan := make(chan *rpcPaymentIntent)
errChan := make(chan error, 1)
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
// * limit either a %, or absolute, or iff more than sending
// We don't allow payments to be sent while the daemon itself is still
// syncing as we may be trying to sent a payment over a "stale"
// channel.
@ -2241,9 +2272,6 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
func (r *rpcServer) sendPaymentSync(ctx context.Context,
nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
// * limit either a %, or absolute, or iff more than sending
// We don't allow payments to be sent while the daemon itself is still
// syncing as we may be trying to sent a payment over a "stale"
// channel.
@ -3053,6 +3081,8 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
"allowed is %v", amt, maxPaymentMSat.ToSatoshis())
}
feeLimit := calculateFeeLimit(in.FeeLimit, amtMSat)
// Query the channel router for a possible path to the destination that
// can carry `in.Amt` satoshis _including_ the total fee required on
// the route.
@ -3062,11 +3092,12 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
)
if in.FinalCltvDelta == 0 {
routes, findErr = r.server.chanRouter.FindRoutes(
pubKey, amtMSat, uint32(in.NumRoutes),
pubKey, amtMSat, feeLimit, uint32(in.NumRoutes),
)
} else {
routes, findErr = r.server.chanRouter.FindRoutes(
pubKey, amtMSat, uint32(in.NumRoutes), uint16(in.FinalCltvDelta),
pubKey, amtMSat, feeLimit, uint32(in.NumRoutes),
uint16(in.FinalCltvDelta),
)
}
if findErr != nil {