Merge pull request #2022 from joostjager/holdinvoice

htlcswitch: hodl invoice
This commit is contained in:
Olaoluwa Osuntokun 2019-03-15 13:26:53 -07:00 committed by GitHub
commit aa1cd04dbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3160 additions and 1434 deletions

@ -2,7 +2,6 @@ package channeldb
import ( import (
"crypto/rand" "crypto/rand"
"crypto/sha256"
"reflect" "reflect"
"testing" "testing"
"time" "time"
@ -67,17 +66,18 @@ func TestInvoiceWorkflow(t *testing.T) {
copy(fakeInvoice.Terms.PaymentPreimage[:], rev[:]) copy(fakeInvoice.Terms.PaymentPreimage[:], rev[:])
fakeInvoice.Terms.Value = lnwire.NewMSatFromSatoshis(10000) fakeInvoice.Terms.Value = lnwire.NewMSatFromSatoshis(10000)
paymentHash := fakeInvoice.Terms.PaymentPreimage.Hash()
// Add the invoice to the database, this should succeed as there aren't // Add the invoice to the database, this should succeed as there aren't
// any existing invoices within the database with the same payment // any existing invoices within the database with the same payment
// hash. // hash.
if _, err := db.AddInvoice(fakeInvoice); err != nil { if _, err := db.AddInvoice(fakeInvoice, paymentHash); err != nil {
t.Fatalf("unable to find invoice: %v", err) t.Fatalf("unable to find invoice: %v", err)
} }
// Attempt to retrieve the invoice which was just added to the // Attempt to retrieve the invoice which was just added to the
// database. It should be found, and the invoice returned should be // database. It should be found, and the invoice returned should be
// identical to the one created above. // identical to the one created above.
paymentHash := sha256.Sum256(fakeInvoice.Terms.PaymentPreimage[:])
dbInvoice, err := db.LookupInvoice(paymentHash) dbInvoice, err := db.LookupInvoice(paymentHash)
if err != nil { if err != nil {
t.Fatalf("unable to find invoice: %v", err) t.Fatalf("unable to find invoice: %v", err)
@ -99,7 +99,7 @@ func TestInvoiceWorkflow(t *testing.T) {
// now have the settled bit toggle to true and a non-default // now have the settled bit toggle to true and a non-default
// SettledDate // SettledDate
payAmt := fakeInvoice.Terms.Value * 2 payAmt := fakeInvoice.Terms.Value * 2
if _, err := db.SettleInvoice(paymentHash, payAmt); err != nil { if _, err := db.AcceptOrSettleInvoice(paymentHash, payAmt); err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
dbInvoice2, err := db.LookupInvoice(paymentHash) dbInvoice2, err := db.LookupInvoice(paymentHash)
@ -126,7 +126,7 @@ func TestInvoiceWorkflow(t *testing.T) {
// Attempt to insert generated above again, this should fail as // Attempt to insert generated above again, this should fail as
// duplicates are rejected by the processing logic. // duplicates are rejected by the processing logic.
if _, err := db.AddInvoice(fakeInvoice); err != ErrDuplicateInvoice { if _, err := db.AddInvoice(fakeInvoice, paymentHash); err != ErrDuplicateInvoice {
t.Fatalf("invoice insertion should fail due to duplication, "+ t.Fatalf("invoice insertion should fail due to duplication, "+
"instead %v", err) "instead %v", err)
} }
@ -149,7 +149,8 @@ func TestInvoiceWorkflow(t *testing.T) {
t.Fatalf("unable to create invoice: %v", err) t.Fatalf("unable to create invoice: %v", err)
} }
if _, err := db.AddInvoice(invoice); err != nil { hash := invoice.Terms.PaymentPreimage.Hash()
if _, err := db.AddInvoice(invoice, hash); err != nil {
t.Fatalf("unable to add invoice %v", err) t.Fatalf("unable to add invoice %v", err)
} }
@ -198,7 +199,9 @@ func TestInvoiceAddTimeSeries(t *testing.T) {
t.Fatalf("unable to create invoice: %v", err) t.Fatalf("unable to create invoice: %v", err)
} }
if _, err := db.AddInvoice(invoice); err != nil { paymentHash := invoice.Terms.PaymentPreimage.Hash()
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
t.Fatalf("unable to add invoice %v", err) t.Fatalf("unable to add invoice %v", err)
} }
@ -256,11 +259,9 @@ func TestInvoiceAddTimeSeries(t *testing.T) {
for i := 10; i < len(invoices); i++ { for i := 10; i < len(invoices); i++ {
invoice := &invoices[i] invoice := &invoices[i]
paymentHash := sha256.Sum256( paymentHash := invoice.Terms.PaymentPreimage.Hash()
invoice.Terms.PaymentPreimage[:],
)
_, err := db.SettleInvoice(paymentHash, 0) _, err := db.AcceptOrSettleInvoice(paymentHash, 0)
if err != nil { if err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
@ -334,13 +335,14 @@ func TestDuplicateSettleInvoice(t *testing.T) {
t.Fatalf("unable to create invoice: %v", err) t.Fatalf("unable to create invoice: %v", err)
} }
if _, err := db.AddInvoice(invoice); err != nil { payHash := invoice.Terms.PaymentPreimage.Hash()
if _, err := db.AddInvoice(invoice, payHash); err != nil {
t.Fatalf("unable to add invoice %v", err) t.Fatalf("unable to add invoice %v", err)
} }
// With the invoice in the DB, we'll now attempt to settle the invoice. // With the invoice in the DB, we'll now attempt to settle the invoice.
payHash := sha256.Sum256(invoice.Terms.PaymentPreimage[:]) dbInvoice, err := db.AcceptOrSettleInvoice(payHash, amt)
dbInvoice, err := db.SettleInvoice(payHash, amt)
if err != nil { if err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
@ -360,7 +362,7 @@ func TestDuplicateSettleInvoice(t *testing.T) {
// If we try to settle the invoice again, then we should get the very // If we try to settle the invoice again, then we should get the very
// same invoice back, but with an error this time. // same invoice back, but with an error this time.
dbInvoice, err = db.SettleInvoice(payHash, amt) dbInvoice, err = db.AcceptOrSettleInvoice(payHash, amt)
if err != ErrInvoiceAlreadySettled { if err != ErrInvoiceAlreadySettled {
t.Fatalf("expected ErrInvoiceAlreadySettled") t.Fatalf("expected ErrInvoiceAlreadySettled")
} }
@ -397,14 +399,15 @@ func TestQueryInvoices(t *testing.T) {
t.Fatalf("unable to create invoice: %v", err) t.Fatalf("unable to create invoice: %v", err)
} }
if _, err := db.AddInvoice(invoice); err != nil { paymentHash := invoice.Terms.PaymentPreimage.Hash()
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
t.Fatalf("unable to add invoice: %v", err) t.Fatalf("unable to add invoice: %v", err)
} }
// We'll only settle half of all invoices created. // We'll only settle half of all invoices created.
if i%2 == 0 { if i%2 == 0 {
paymentHash := sha256.Sum256(invoice.Terms.PaymentPreimage[:]) if _, err := db.AcceptOrSettleInvoice(paymentHash, i); err != nil {
if _, err := db.SettleInvoice(paymentHash, i); err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
} }

@ -2,7 +2,6 @@ package channeldb
import ( import (
"bytes" "bytes"
"crypto/sha256"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -16,6 +15,10 @@ import (
) )
var ( var (
// UnknownPreimage is an all-zeroes preimage that indicates that the
// preimage for this invoice is not yet known.
UnknownPreimage lntypes.Preimage
// invoiceBucket is the name of the bucket within the database that // invoiceBucket is the name of the bucket within the database that
// stores all data related to invoices no matter their final state. // stores all data related to invoices no matter their final state.
// Within the invoice bucket, each invoice is keyed by its invoice ID // Within the invoice bucket, each invoice is keyed by its invoice ID
@ -66,6 +69,13 @@ var (
// ErrInvoiceAlreadyCanceled is returned when the invoice is already // ErrInvoiceAlreadyCanceled is returned when the invoice is already
// canceled. // canceled.
ErrInvoiceAlreadyCanceled = errors.New("invoice already canceled") ErrInvoiceAlreadyCanceled = errors.New("invoice already canceled")
// ErrInvoiceAlreadyAccepted is returned when the invoice is already
// accepted.
ErrInvoiceAlreadyAccepted = errors.New("invoice already accepted")
// ErrInvoiceStillOpen is returned when the invoice is still open.
ErrInvoiceStillOpen = errors.New("invoice still open")
) )
const ( const (
@ -97,6 +107,10 @@ const (
// ContractCanceled means the invoice has been canceled. // ContractCanceled means the invoice has been canceled.
ContractCanceled ContractState = 2 ContractCanceled ContractState = 2
// ContractAccepted means the HTLC has been accepted but not settled
// yet.
ContractAccepted ContractState = 3
) )
// String returns a human readable identifier for the ContractState type. // String returns a human readable identifier for the ContractState type.
@ -108,6 +122,8 @@ func (c ContractState) String() string {
return "Settled" return "Settled"
case ContractCanceled: case ContractCanceled:
return "Canceled" return "Canceled"
case ContractAccepted:
return "Accepted"
} }
return "Unknown" return "Unknown"
@ -213,11 +229,14 @@ func validateInvoice(i *Invoice) error {
return nil return nil
} }
// AddInvoice inserts the targeted invoice into the database. If the invoice // AddInvoice inserts the targeted invoice into the database. If the invoice has
// has *any* payment hashes which already exists within the database, then the // *any* payment hashes which already exists within the database, then the
// insertion will be aborted and rejected due to the strict policy banning any // insertion will be aborted and rejected due to the strict policy banning any
// duplicate payment hashes. // duplicate payment hashes. A side effect of this function is that it sets
func (d *DB) AddInvoice(newInvoice *Invoice) (uint64, error) { // AddIndex on newInvoice.
func (d *DB) AddInvoice(newInvoice *Invoice, paymentHash lntypes.Hash) (
uint64, error) {
if err := validateInvoice(newInvoice); err != nil { if err := validateInvoice(newInvoice); err != nil {
return 0, err return 0, err
} }
@ -244,9 +263,6 @@ func (d *DB) AddInvoice(newInvoice *Invoice) (uint64, error) {
// Ensure that an invoice an identical payment hash doesn't // Ensure that an invoice an identical payment hash doesn't
// already exist within the index. // already exist within the index.
paymentHash := sha256.Sum256(
newInvoice.Terms.PaymentPreimage[:],
)
if invoiceIndex.Get(paymentHash[:]) != nil { if invoiceIndex.Get(paymentHash[:]) != nil {
return ErrDuplicateInvoice return ErrDuplicateInvoice
} }
@ -268,6 +284,7 @@ func (d *DB) AddInvoice(newInvoice *Invoice) (uint64, error) {
newIndex, err := putInvoice( newIndex, err := putInvoice(
invoices, invoiceIndex, addIndex, newInvoice, invoiceNum, invoices, invoiceIndex, addIndex, newInvoice, invoiceNum,
paymentHash,
) )
if err != nil { if err != nil {
return err return err
@ -607,11 +624,14 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
return resp, nil return resp, nil
} }
// SettleInvoice attempts to mark an invoice corresponding to the passed // AcceptOrSettleInvoice attempts to mark an invoice corresponding to the passed
// payment hash as fully settled. If an invoice matching the passed payment // payment hash as settled. If an invoice matching the passed payment hash
// hash doesn't existing within the database, then the action will fail with a // doesn't existing within the database, then the action will fail with a "not
// "not found" error. // found" error.
func (d *DB) SettleInvoice(paymentHash [32]byte, //
// When the preimage for the invoice is unknown (hold invoice), the invoice is
// marked as accepted.
func (d *DB) AcceptOrSettleInvoice(paymentHash [32]byte,
amtPaid lnwire.MilliSatoshi) (*Invoice, error) { amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
var settledInvoice *Invoice var settledInvoice *Invoice
@ -640,7 +660,7 @@ func (d *DB) SettleInvoice(paymentHash [32]byte,
return ErrInvoiceNotFound return ErrInvoiceNotFound
} }
settledInvoice, err = settleInvoice( settledInvoice, err = acceptOrSettleInvoice(
invoices, settleIndex, invoiceNum, amtPaid, invoices, settleIndex, invoiceNum, amtPaid,
) )
@ -650,6 +670,46 @@ func (d *DB) SettleInvoice(paymentHash [32]byte,
return settledInvoice, err return settledInvoice, err
} }
// SettleHoldInvoice sets the preimage of a hodl invoice and marks the invoice
// as settled.
func (d *DB) SettleHoldInvoice(preimage lntypes.Preimage) (*Invoice, error) {
var updatedInvoice *Invoice
hash := preimage.Hash()
err := d.Update(func(tx *bbolt.Tx) error {
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
if err != nil {
return err
}
invoiceIndex, err := invoices.CreateBucketIfNotExists(
invoiceIndexBucket,
)
if err != nil {
return err
}
settleIndex, err := invoices.CreateBucketIfNotExists(
settleIndexBucket,
)
if err != nil {
return err
}
// Check the invoice index to see if an invoice paying to this
// hash exists within the DB.
invoiceNum := invoiceIndex.Get(hash[:])
if invoiceNum == nil {
return ErrInvoiceNotFound
}
updatedInvoice, err = settleHoldInvoice(
invoices, settleIndex, invoiceNum, preimage,
)
return err
})
return updatedInvoice, err
}
// CancelInvoice attempts to cancel the invoice corresponding to the passed // CancelInvoice attempts to cancel the invoice corresponding to the passed
// payment hash. // payment hash.
func (d *DB) CancelInvoice(paymentHash lntypes.Hash) (*Invoice, error) { func (d *DB) CancelInvoice(paymentHash lntypes.Hash) (*Invoice, error) {
@ -743,7 +803,8 @@ func (d *DB) InvoicesSettledSince(sinceSettleIndex uint64) ([]Invoice, error) {
} }
func putInvoice(invoices, invoiceIndex, addIndex *bbolt.Bucket, func putInvoice(invoices, invoiceIndex, addIndex *bbolt.Bucket,
i *Invoice, invoiceNum uint32) (uint64, error) { i *Invoice, invoiceNum uint32, paymentHash lntypes.Hash) (
uint64, error) {
// Create the invoice key which is just the big-endian representation // Create the invoice key which is just the big-endian representation
// of the invoice number. // of the invoice number.
@ -762,7 +823,6 @@ func putInvoice(invoices, invoiceIndex, addIndex *bbolt.Bucket,
// Add the payment hash to the invoice index. This will let us quickly // Add the payment hash to the invoice index. This will let us quickly
// identify if we can settle an incoming payment, and also to possibly // identify if we can settle an incoming payment, and also to possibly
// allow a single invoice to have multiple payment installations. // allow a single invoice to have multiple payment installations.
paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
err := invoiceIndex.Put(paymentHash[:], invoiceKey[:]) err := invoiceIndex.Put(paymentHash[:], invoiceKey[:])
if err != nil { if err != nil {
return 0, err return 0, err
@ -928,7 +988,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
return invoice, nil return invoice, nil
} }
func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte, func acceptOrSettleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
amtPaid lnwire.MilliSatoshi) (*Invoice, error) { amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
invoice, err := fetchInvoice(invoiceNum, invoices) invoice, err := fetchInvoice(invoiceNum, invoices)
@ -936,32 +996,90 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
return nil, err return nil, err
} }
switch invoice.Terms.State { state := invoice.Terms.State
case ContractSettled:
switch {
case state == ContractAccepted:
return &invoice, ErrInvoiceAlreadyAccepted
case state == ContractSettled:
return &invoice, ErrInvoiceAlreadySettled return &invoice, ErrInvoiceAlreadySettled
case ContractCanceled: case state == ContractCanceled:
return &invoice, ErrInvoiceAlreadyCanceled return &invoice, ErrInvoiceAlreadyCanceled
} }
holdInvoice := invoice.Terms.PaymentPreimage == UnknownPreimage
if holdInvoice {
invoice.Terms.State = ContractAccepted
} else {
err := setSettleFields(settleIndex, invoiceNum, &invoice)
if err != nil {
return nil, err
}
}
invoice.AmtPaid = amtPaid
var buf bytes.Buffer
if err := serializeInvoice(&buf, &invoice); err != nil {
return nil, err
}
if err := invoices.Put(invoiceNum[:], buf.Bytes()); err != nil {
return nil, err
}
return &invoice, nil
}
func setSettleFields(settleIndex *bbolt.Bucket, invoiceNum []byte,
invoice *Invoice) error {
// Now that we know the invoice hasn't already been settled, we'll // Now that we know the invoice hasn't already been settled, we'll
// update the settle index so we can place this settle event in the // update the settle index so we can place this settle event in the
// proper location within our time series. // proper location within our time series.
nextSettleSeqNo, err := settleIndex.NextSequence() nextSettleSeqNo, err := settleIndex.NextSequence()
if err != nil { if err != nil {
return nil, err return err
} }
var seqNoBytes [8]byte var seqNoBytes [8]byte
byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo) byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo)
if err := settleIndex.Put(seqNoBytes[:], invoiceNum); err != nil { if err := settleIndex.Put(seqNoBytes[:], invoiceNum); err != nil {
return nil, err return err
} }
invoice.AmtPaid = amtPaid
invoice.Terms.State = ContractSettled invoice.Terms.State = ContractSettled
invoice.SettleDate = time.Now() invoice.SettleDate = time.Now()
invoice.SettleIndex = nextSettleSeqNo invoice.SettleIndex = nextSettleSeqNo
return nil
}
func settleHoldInvoice(invoices, settleIndex *bbolt.Bucket,
invoiceNum []byte, preimage lntypes.Preimage) (*Invoice,
error) {
invoice, err := fetchInvoice(invoiceNum, invoices)
if err != nil {
return nil, err
}
switch invoice.Terms.State {
case ContractOpen:
return &invoice, ErrInvoiceStillOpen
case ContractCanceled:
return &invoice, ErrInvoiceAlreadyCanceled
case ContractSettled:
return &invoice, ErrInvoiceAlreadySettled
}
invoice.Terms.PaymentPreimage = preimage
err = setSettleFields(settleIndex, invoiceNum, &invoice)
if err != nil {
return nil, err
}
var buf bytes.Buffer var buf bytes.Buffer
if err := serializeInvoice(&buf, &invoice); err != nil { if err := serializeInvoice(&buf, &invoice); err != nil {
return nil, err return nil, err
@ -991,6 +1109,9 @@ func cancelInvoice(invoices *bbolt.Bucket, invoiceNum []byte) (
invoice.Terms.State = ContractCanceled invoice.Terms.State = ContractCanceled
// Set AmtPaid back to 0, in case the invoice was already accepted.
invoice.AmtPaid = 0
var buf bytes.Buffer var buf bytes.Buffer
if err := serializeInvoice(&buf, &invoice); err != nil { if err := serializeInvoice(&buf, &invoice); err != nil {
return nil, err return nil, err

@ -7,6 +7,8 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"strconv"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -15,6 +17,8 @@ import (
func invoicesCommands() []cli.Command { func invoicesCommands() []cli.Command {
return []cli.Command{ return []cli.Command{
cancelInvoiceCommand, cancelInvoiceCommand,
addHoldInvoiceCommand,
settleInvoiceCommand,
} }
} }
@ -28,6 +32,60 @@ func getInvoicesClient(ctx *cli.Context) (invoicesrpc.InvoicesClient, func()) {
return invoicesrpc.NewInvoicesClient(conn), cleanUp return invoicesrpc.NewInvoicesClient(conn), cleanUp
} }
var settleInvoiceCommand = cli.Command{
Name: "settleinvoice",
Category: "Payments",
Usage: "Reveal a preimage and use it to settle the corresponding invoice.",
Description: `
Todo.`,
ArgsUsage: "preimage",
Flags: []cli.Flag{
cli.StringFlag{
Name: "preimage",
Usage: "the hex-encoded preimage (32 byte) which will " +
"allow settling an incoming HTLC payable to this " +
"preimage.",
},
},
Action: actionDecorator(settleInvoice),
}
func settleInvoice(ctx *cli.Context) error {
var (
preimage []byte
err error
)
client, cleanUp := getInvoicesClient(ctx)
defer cleanUp()
args := ctx.Args()
switch {
case ctx.IsSet("preimage"):
preimage, err = hex.DecodeString(ctx.String("preimage"))
case args.Present():
preimage, err = hex.DecodeString(args.First())
}
if err != nil {
return fmt.Errorf("unable to parse preimage: %v", err)
}
invoice := &invoicesrpc.SettleInvoiceMsg{
Preimage: preimage,
}
resp, err := client.SettleInvoice(context.Background(), invoice)
if err != nil {
return err
}
printJSON(resp)
return nil
}
var cancelInvoiceCommand = cli.Command{ var cancelInvoiceCommand = cli.Command{
Name: "cancelinvoice", Name: "cancelinvoice",
Category: "Payments", Category: "Payments",
@ -80,3 +138,120 @@ func cancelInvoice(ctx *cli.Context) error {
return nil return nil
} }
var addHoldInvoiceCommand = cli.Command{
Name: "addholdinvoice",
Category: "Payments",
Usage: "Add a new hold invoice.",
Description: `
Add a new invoice, expressing intent for a future payment.
Invoices without an amount can be created by not supplying any
parameters or providing an amount of 0. These invoices allow the payee
to specify the amount of satoshis they wish to send.`,
ArgsUsage: "hash [amt]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "memo",
Usage: "a description of the payment to attach along " +
"with the invoice (default=\"\")",
},
cli.Int64Flag{
Name: "amt",
Usage: "the amt of satoshis in this invoice",
},
cli.StringFlag{
Name: "description_hash",
Usage: "SHA-256 hash of the description of the payment. " +
"Used if the purpose of payment cannot naturally " +
"fit within the memo. If provided this will be " +
"used instead of the description(memo) field in " +
"the encoded invoice.",
},
cli.StringFlag{
Name: "fallback_addr",
Usage: "fallback on-chain address that can be used in " +
"case the lightning payment fails",
},
cli.Int64Flag{
Name: "expiry",
Usage: "the invoice's expiry time in seconds. If not " +
"specified, an expiry of 3600 seconds (1 hour) " +
"is implied.",
},
cli.BoolTFlag{
Name: "private",
Usage: "encode routing hints in the invoice with " +
"private channels in order to assist the " +
"payer in reaching you",
},
},
Action: actionDecorator(addHoldInvoice),
}
func addHoldInvoice(ctx *cli.Context) error {
var (
descHash []byte
amt int64
err error
)
client, cleanUp := getInvoicesClient(ctx)
defer cleanUp()
args := ctx.Args()
if ctx.NArg() == 0 {
cli.ShowCommandHelp(ctx, "addholdinvoice")
return nil
}
hash, err := hex.DecodeString(args.First())
if err != nil {
return fmt.Errorf("unable to parse hash: %v", err)
}
args = args.Tail()
switch {
case ctx.IsSet("amt"):
amt = ctx.Int64("amt")
case args.Present():
amt, err = strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode amt argument: %v", err)
}
}
if err != nil {
return fmt.Errorf("unable to parse preimage: %v", err)
}
descHash, err = hex.DecodeString(ctx.String("description_hash"))
if err != nil {
return fmt.Errorf("unable to parse description_hash: %v", err)
}
invoice := &invoicesrpc.AddHoldInvoiceRequest{
Memo: ctx.String("memo"),
Hash: hash,
Value: amt,
DescriptionHash: descHash,
FallbackAddr: ctx.String("fallback_addr"),
Expiry: ctx.Int64("expiry"),
Private: ctx.Bool("private"),
}
resp, err := client.AddHoldInvoice(context.Background(), invoice)
if err != nil {
return err
}
printJSON(struct {
PayReq string `json:"pay_req"`
}{
PayReq: resp.PaymentRequest,
})
return nil
}

@ -12,7 +12,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
@ -137,10 +137,9 @@ type ChainArbitratorConfig struct {
// Sweeper allows resolvers to sweep their final outputs. // Sweeper allows resolvers to sweep their final outputs.
Sweeper *sweep.UtxoSweeper Sweeper *sweep.UtxoSweeper
// SettleInvoice attempts to settle an existing invoice on-chain with // Registry is the invoice database that is used by resolvers to lookup
// the given payment hash. ErrInvoiceNotFound is returned if an invoice // preimages and settle invoices.
// is not found. Registry *invoices.InvoiceRegistry
SettleInvoice func(lntypes.Hash, lnwire.MilliSatoshi) error
// NotifyClosedChannel is a function closure that the ChainArbitrator // NotifyClosedChannel is a function closure that the ChainArbitrator
// will use to notify the ChannelNotifier about a newly closed channel. // will use to notify the ChannelNotifier about a newly closed channel.

@ -11,7 +11,6 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -177,9 +176,6 @@ func createTestChannelArbitrator(log ArbitratorLog) (*ChannelArbitrator,
*lnwallet.IncomingHtlcResolution, uint32) error { *lnwallet.IncomingHtlcResolution, uint32) error {
return nil return nil
}, },
SettleInvoice: func(lntypes.Hash, lnwire.MilliSatoshi) error {
return nil
},
} }
// We'll use the resolvedChan to synchronize on call to // We'll use the resolvedChan to synchronize on call to

@ -1,11 +1,13 @@
package contractcourt package contractcourt
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/invoices"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
) )
@ -70,11 +72,18 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
return nil, h.Checkpoint(h) return nil, h.Checkpoint(h)
} }
// applyPreimage is a helper function that will populate our internal // tryApplyPreimage is a helper function that will populate our internal
// resolver with the preimage we learn of. This should be called once // resolver with the preimage we learn of. This should be called once
// the preimage is revealed so the inner resolver can properly complete // the preimage is revealed so the inner resolver can properly complete
// its duties. // its duties. The boolean return value indicates whether the preimage
applyPreimage := func(preimage lntypes.Preimage) { // was properly applied.
tryApplyPreimage := func(preimage lntypes.Preimage) bool {
// Check to see if this preimage matches our htlc.
if !preimage.Matches(h.payHash) {
return false
}
// Update htlcResolution with the matching preimage.
h.htlcResolution.Preimage = preimage h.htlcResolution.Preimage = preimage
log.Infof("%T(%v): extracted preimage=%v from beacon!", h, log.Infof("%T(%v): extracted preimage=%v from beacon!", h,
@ -93,6 +102,8 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
// preimage. // preimage.
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:] h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
} }
return true
} }
// If the HTLC hasn't expired yet, then we may still be able to claim // If the HTLC hasn't expired yet, then we may still be able to claim
@ -112,6 +123,26 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
blockEpochs.Cancel() blockEpochs.Cancel()
}() }()
// Create a buffered hodl chan to prevent deadlock.
hodlChan := make(chan interface{}, 1)
// Notify registry that we are potentially settling as exit hop
// on-chain, so that we will get a hodl event when a corresponding hodl
// invoice is settled.
event, err := h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt, hodlChan)
if err != nil && err != channeldb.ErrInvoiceNotFound {
return nil, err
}
defer h.Registry.HodlUnsubscribeAll(hodlChan)
// If the htlc can be settled directly, we can progress to the inner
// resolver immediately.
if event != nil && event.Preimage != nil {
if tryApplyPreimage(*event.Preimage) {
return &h.htlcSuccessResolver, nil
}
}
// With the epochs and preimage subscriptions initialized, we'll query // With the epochs and preimage subscriptions initialized, we'll query
// to see if we already know the preimage. // to see if we already know the preimage.
preimage, ok := h.PreimageDB.LookupPreimage(h.payHash) preimage, ok := h.PreimageDB.LookupPreimage(h.payHash)
@ -119,26 +150,35 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
// If we do, then this means we can claim the HTLC! However, // If we do, then this means we can claim the HTLC! However,
// we don't know how to ourselves, so we'll return our inner // we don't know how to ourselves, so we'll return our inner
// resolver which has the knowledge to do so. // resolver which has the knowledge to do so.
applyPreimage(preimage) if tryApplyPreimage(preimage) {
return &h.htlcSuccessResolver, nil return &h.htlcSuccessResolver, nil
}
} }
for { for {
select { select {
case preimage := <-preimageSubscription.WitnessUpdates: case preimage := <-preimageSubscription.WitnessUpdates:
// If this isn't our preimage, then we'll continue if !tryApplyPreimage(preimage) {
// onwards.
hash := preimage.Hash()
preimageMatches := bytes.Equal(hash[:], h.payHash[:])
if !preimageMatches {
continue continue
} }
// Otherwise, we've learned of the preimage! We'll add // We've learned of the preimage and this information
// this information to our inner resolver, then return // has been added to our inner resolver. We return it so
// it so it can continue contract resolution. // it can continue contract resolution.
applyPreimage(preimage) return &h.htlcSuccessResolver, nil
case hodlItem := <-hodlChan:
hodlEvent := hodlItem.(invoices.HodlEvent)
// Only process settle events.
if hodlEvent.Preimage == nil {
continue
}
if !tryApplyPreimage(*hodlEvent.Preimage) {
continue
}
return &h.htlcSuccessResolver, nil return &h.htlcSuccessResolver, nil
case newBlock, ok := <-blockEpochs.Epochs: case newBlock, ok := <-blockEpochs.Epochs:

@ -178,8 +178,13 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
} }
// With the HTLC claimed, we can attempt to settle its // With the HTLC claimed, we can attempt to settle its
// corresponding invoice if we were the original destination. // corresponding invoice if we were the original destination. As
err = h.SettleInvoice(h.payHash, h.htlcAmt) // the htlc is already settled at this point, we don't need to
// read on the hodl channel.
hodlChan := make(chan interface{}, 1)
_, err = h.Registry.NotifyExitHopHtlc(
h.payHash, h.htlcAmt, hodlChan,
)
if err != nil && err != channeldb.ErrInvoiceNotFound { if err != nil && err != channeldb.ErrInvoiceNotFound {
log.Errorf("Unable to settle invoice with payment "+ log.Errorf("Unable to settle invoice with payment "+
"hash %x: %v", h.payHash, err) "hash %x: %v", h.payHash, err)
@ -251,8 +256,11 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
} }
// With the HTLC claimed, we can attempt to settle its corresponding // With the HTLC claimed, we can attempt to settle its corresponding
// invoice if we were the original destination. // invoice if we were the original destination. As the htlc is already
err = h.SettleInvoice(h.payHash, h.htlcAmt) // settled at this point, we don't need to read on the hodl
// channel.
hodlChan := make(chan interface{}, 1)
_, err = h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt, hodlChan)
if err != nil && err != channeldb.ErrInvoiceNotFound { if err != nil && err != channeldb.ErrInvoiceNotFound {
log.Errorf("Unable to settle invoice with payment "+ log.Errorf("Unable to settle invoice with payment "+
"hash %x: %v", h.payHash, err) "hash %x: %v", h.payHash, err)

@ -3,6 +3,7 @@ package htlcswitch
import ( import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -17,13 +18,23 @@ type InvoiceDatabase interface {
// extended to us gives us enough time to settle as we prescribe. // extended to us gives us enough time to settle as we prescribe.
LookupInvoice(lntypes.Hash) (channeldb.Invoice, uint32, error) LookupInvoice(lntypes.Hash) (channeldb.Invoice, uint32, error)
// SettleInvoice attempts to mark an invoice corresponding to the // NotifyExitHopHtlc attempts to mark an invoice as settled. If the
// passed payment hash as fully settled. // invoice is a debug invoice, then this method is a noop as debug
SettleInvoice(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi) error // invoices are never fully settled. The return value describes how the
// htlc should be resolved. If the htlc cannot be resolved immediately,
// the resolution is sent on the passed in hodlChan later.
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
hodlChan chan<- interface{}) (*invoices.HodlEvent, error)
// CancelInvoice attempts to cancel the invoice corresponding to the // CancelInvoice attempts to cancel the invoice corresponding to the
// passed payment hash. // passed payment hash.
CancelInvoice(payHash lntypes.Hash) error CancelInvoice(payHash lntypes.Hash) error
// SettleHodlInvoice settles a hold invoice.
SettleHodlInvoice(preimage lntypes.Preimage) error
// HodlUnsubscribeAll unsubscribes from all hodl events.
HodlUnsubscribeAll(subscriber chan<- interface{})
} }
// ChannelLink is an interface which represents the subsystem for managing the // ChannelLink is an interface which represents the subsystem for managing the

@ -16,10 +16,12 @@ import (
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/htlcswitch/hodl" "github.com/lightningnetwork/lnd/htlcswitch/hodl"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
) )
@ -344,10 +346,24 @@ type channelLink struct {
sync.RWMutex sync.RWMutex
// hodlQueue is used to receive exit hop htlc resolutions from invoice
// registry.
hodlQueue *queue.ConcurrentQueue
// hodlMap stores a list of htlc data structs per hash. It allows
// resolving those htlcs when we receive a message on hodlQueue.
hodlMap map[lntypes.Hash][]hodlHtlc
wg sync.WaitGroup wg sync.WaitGroup
quit chan struct{} quit chan struct{}
} }
// hodlHtlc contains htlc data that is required for resolution.
type hodlHtlc struct {
pd *lnwallet.PaymentDescriptor
obfuscator ErrorEncrypter
}
// NewChannelLink creates a new instance of a ChannelLink given a configuration // NewChannelLink creates a new instance of a ChannelLink given a configuration
// and active channel that will be used to verify/apply updates to. // and active channel that will be used to verify/apply updates to.
func NewChannelLink(cfg ChannelLinkConfig, func NewChannelLink(cfg ChannelLinkConfig,
@ -361,6 +377,8 @@ func NewChannelLink(cfg ChannelLinkConfig,
logCommitTimer: time.NewTimer(300 * time.Millisecond), logCommitTimer: time.NewTimer(300 * time.Millisecond),
overflowQueue: newPacketQueue(input.MaxHTLCNumber / 2), overflowQueue: newPacketQueue(input.MaxHTLCNumber / 2),
htlcUpdates: make(chan []channeldb.HTLC), htlcUpdates: make(chan []channeldb.HTLC),
hodlMap: make(map[lntypes.Hash][]hodlHtlc),
hodlQueue: queue.NewConcurrentQueue(10),
quit: make(chan struct{}), quit: make(chan struct{}),
} }
} }
@ -384,6 +402,7 @@ func (l *channelLink) Start() error {
l.mailBox.ResetMessages() l.mailBox.ResetMessages()
l.overflowQueue.Start() l.overflowQueue.Start()
l.hodlQueue.Start()
// Before launching the htlcManager messages, revert any circuits that // Before launching the htlcManager messages, revert any circuits that
// were marked open in the switch's circuit map, but did not make it // were marked open in the switch's circuit map, but did not make it
@ -449,12 +468,17 @@ func (l *channelLink) Stop() {
log.Infof("ChannelLink(%v) is stopping", l) log.Infof("ChannelLink(%v) is stopping", l)
// As the link is stopping, we are no longer interested in hodl events
// coming from the invoice registry.
l.cfg.Registry.HodlUnsubscribeAll(l.hodlQueue.ChanIn())
if l.cfg.ChainEvents.Cancel != nil { if l.cfg.ChainEvents.Cancel != nil {
l.cfg.ChainEvents.Cancel() l.cfg.ChainEvents.Cancel()
} }
l.updateFeeTimer.Stop() l.updateFeeTimer.Stop()
l.overflowQueue.Stop() l.overflowQueue.Stop()
l.hodlQueue.Stop()
close(l.quit) close(l.quit)
l.wg.Wait() l.wg.Wait()
@ -1058,12 +1082,119 @@ out:
case msg := <-l.upstream: case msg := <-l.upstream:
l.handleUpstreamMsg(msg) l.handleUpstreamMsg(msg)
// A hodl event is received. This means that we now have a
// resolution for a previously accepted htlc.
case hodlItem := <-l.hodlQueue.ChanOut():
hodlEvent := hodlItem.(invoices.HodlEvent)
err := l.processHodlQueue(hodlEvent)
if err != nil {
l.fail(LinkFailureError{code: ErrInternalError},
fmt.Sprintf("process hodl queue: %v",
err.Error()),
)
break out
}
case <-l.quit: case <-l.quit:
break out break out
} }
} }
} }
// processHodlQueue processes a received hodl event and continues reading from
// the hodl queue until no more events remain. When this function returns
// without an error, the commit tx should be updated.
func (l *channelLink) processHodlQueue(firstHodlEvent invoices.HodlEvent) error {
// Try to read all waiting resolution messages, so that they can all be
// processed in a single commitment tx update.
hodlEvent := firstHodlEvent
loop:
for {
if err := l.processHodlMapEvent(hodlEvent); err != nil {
return err
}
select {
case item := <-l.hodlQueue.ChanOut():
hodlEvent = item.(invoices.HodlEvent)
default:
break loop
}
}
// Update the commitment tx.
if err := l.updateCommitTx(); err != nil {
return fmt.Errorf("unable to update commitment: %v", err)
}
return nil
}
// processHodlMapEvent resolves stored hodl htlcs based using the information in
// hodlEvent.
func (l *channelLink) processHodlMapEvent(hodlEvent invoices.HodlEvent) error {
// Lookup all hodl htlcs that can be failed or settled with this event.
// The hodl htlc must be present in the map.
hash := hodlEvent.Hash
hodlHtlcs, ok := l.hodlMap[hash]
if !ok {
return fmt.Errorf("hodl htlc not found: %v", hash)
}
if err := l.processHodlEvent(hodlEvent, hodlHtlcs...); err != nil {
return err
}
// Clean up hodl map.
delete(l.hodlMap, hash)
return nil
}
// processHodlEvent applies a received hodl event to the provided htlc. When
// this function returns without an error, the commit tx should be updated.
func (l *channelLink) processHodlEvent(hodlEvent invoices.HodlEvent,
htlcs ...hodlHtlc) error {
hash := hodlEvent.Hash
if hodlEvent.Preimage == nil {
l.debugf("Received hodl cancel event for %v", hash)
} else {
l.debugf("Received hodl settle event for %v", hash)
}
// Determine required action for the resolution.
var hodlAction func(htlc hodlHtlc) error
if hodlEvent.Preimage != nil {
hodlAction = func(htlc hodlHtlc) error {
return l.settleHTLC(
*hodlEvent.Preimage, htlc.pd.HtlcIndex,
htlc.pd.SourceRef,
)
}
} else {
hodlAction = func(htlc hodlHtlc) error {
failure := lnwire.NewFailUnknownPaymentHash(
htlc.pd.Amount,
)
l.sendHTLCError(
htlc.pd.HtlcIndex, failure, htlc.obfuscator,
htlc.pd.SourceRef,
)
return nil
}
}
// Apply action for all htlcs matching this hash.
for _, htlc := range htlcs {
if err := hodlAction(htlc); err != nil {
return err
}
}
return nil
}
// randomFeeUpdateTimeout returns a random timeout between the bounds defined // randomFeeUpdateTimeout returns a random timeout between the bounds defined
// within the link's configuration that will be used to determine when the link // within the link's configuration that will be used to determine when the link
// should propose an update to its commitment fee rate. // should propose an update to its commitment fee rate.
@ -2354,228 +2485,20 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
fwdInfo := chanIterator.ForwardingInstructions() fwdInfo := chanIterator.ForwardingInstructions()
switch fwdInfo.NextHop { switch fwdInfo.NextHop {
case exitHop: case exitHop:
// If hodl.ExitSettle is requested, we will not validate updated, err := l.processExitHop(
// the final hop's ADD, nor will we settle the pd, obfuscator, fwdInfo, heightNow,
// corresponding invoice or respond with the preimage.
if l.cfg.DebugHTLC &&
l.cfg.HodlMask.Active(hodl.ExitSettle) {
l.warnf(hodl.ExitSettle.Warning())
continue
}
// First, we'll check the expiry of the HTLC itself
// against, the current block height. If the timeout is
// too soon, then we'll reject the HTLC.
if pd.Timeout-expiryGraceDelta <= heightNow {
log.Errorf("htlc(%x) has an expiry that's too "+
"soon: expiry=%v, best_height=%v",
pd.RHash[:], pd.Timeout, heightNow)
failure := lnwire.FailFinalExpiryTooSoon{}
l.sendHTLCError(
pd.HtlcIndex, &failure, obfuscator, pd.SourceRef,
)
needUpdate = true
continue
}
// We're the designated payment destination. Therefore
// we attempt to see if we have an invoice locally
// which'll allow us to settle this htlc.
invoiceHash := lntypes.Hash(pd.RHash)
invoice, minCltvDelta, err := l.cfg.Registry.LookupInvoice(
invoiceHash,
)
if err != nil {
log.Errorf("unable to query invoice registry: "+
" %v", err)
failure := lnwire.NewFailUnknownPaymentHash(
pd.Amount,
)
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator, pd.SourceRef,
)
needUpdate = true
continue
}
// Reject htlcs for canceled invoices.
if invoice.Terms.State == channeldb.ContractCanceled {
l.errorf("Rejecting htlc due to canceled " +
"invoice")
failure := lnwire.NewFailUnknownPaymentHash(
pd.Amount,
)
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator,
pd.SourceRef,
)
needUpdate = true
continue
}
// If the invoice is already settled, we choose to
// accept the payment to simplify failure recovery.
//
// NOTE: Though our recovery and forwarding logic is
// predominately batched, settling invoices happens
// iteratively. We may reject one of two payments
// for the same rhash at first, but then restart and
// reject both after seeing that the invoice has been
// settled. Without any record of which one settles
// first, it is ambiguous as to which one actually
// settled the invoice. Thus, by accepting all
// payments, we eliminate the race condition that can
// lead to this inconsistency.
//
// TODO(conner): track ownership of settlements to
// properly recover from failures? or add batch invoice
// settlement
if invoice.Terms.State != channeldb.ContractOpen {
log.Warnf("Accepting duplicate payment for "+
"hash=%x", pd.RHash[:])
}
// If we're not currently in debug mode, and the
// extended htlc doesn't meet the value requested, then
// we'll fail the htlc. Otherwise, we settle this htlc
// within our local state update log, then send the
// update entry to the remote party.
//
// NOTE: We make an exception when the value requested
// by the invoice is zero. This means the invoice
// allows the payee to specify the amount of satoshis
// they wish to send. So since we expect the htlc to
// have a different amount, we should not fail.
if !l.cfg.DebugHTLC && invoice.Terms.Value > 0 &&
pd.Amount < invoice.Terms.Value {
log.Errorf("rejecting htlc due to incorrect "+
"amount: expected %v, received %v",
invoice.Terms.Value, pd.Amount)
failure := lnwire.NewFailUnknownPaymentHash(
pd.Amount,
)
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator, pd.SourceRef,
)
needUpdate = true
continue
}
// As we're the exit hop, we'll double check the
// hop-payload included in the HTLC to ensure that it
// was crafted correctly by the sender and matches the
// HTLC we were extended.
//
// NOTE: We make an exception when the value requested
// by the invoice is zero. This means the invoice
// allows the payee to specify the amount of satoshis
// they wish to send. So since we expect the htlc to
// have a different amount, we should not fail.
if !l.cfg.DebugHTLC && invoice.Terms.Value > 0 &&
fwdInfo.AmountToForward < invoice.Terms.Value {
log.Errorf("Onion payload of incoming htlc(%x) "+
"has incorrect value: expected %v, "+
"got %v", pd.RHash, invoice.Terms.Value,
fwdInfo.AmountToForward)
failure := lnwire.NewFailUnknownPaymentHash(
pd.Amount,
)
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator, pd.SourceRef,
)
needUpdate = true
continue
}
// We'll also ensure that our time-lock value has been
// computed correctly.
expectedHeight := heightNow + minCltvDelta
switch {
case !l.cfg.DebugHTLC && pd.Timeout < expectedHeight:
log.Errorf("Incoming htlc(%x) has an "+
"expiration that is too soon: "+
"expected at least %v, got %v",
pd.RHash[:], expectedHeight, pd.Timeout)
failure := lnwire.FailFinalExpiryTooSoon{}
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator,
pd.SourceRef,
)
needUpdate = true
continue
case !l.cfg.DebugHTLC && pd.Timeout != fwdInfo.OutgoingCTLV:
log.Errorf("HTLC(%x) has incorrect "+
"time-lock: expected %v, got %v",
pd.RHash[:], pd.Timeout,
fwdInfo.OutgoingCTLV)
failure := lnwire.NewFinalIncorrectCltvExpiry(
fwdInfo.OutgoingCTLV,
)
l.sendHTLCError(
pd.HtlcIndex, failure, obfuscator, pd.SourceRef,
)
needUpdate = true
continue
}
preimage := invoice.Terms.PaymentPreimage
err = l.channel.SettleHTLC(
preimage, pd.HtlcIndex, pd.SourceRef, nil, nil,
) )
if err != nil { if err != nil {
l.fail(LinkFailureError{code: ErrInternalError}, l.fail(LinkFailureError{code: ErrInternalError},
"unable to settle htlc: %v", err) err.Error(),
)
return false return false
} }
if updated {
// Notify the invoiceRegistry of the invoices we just needUpdate = true
// settled (with the amount accepted at settle time)
// with this latest commitment update.
err = l.cfg.Registry.SettleInvoice(
invoiceHash, pd.Amount,
)
if err != nil {
l.fail(LinkFailureError{code: ErrInternalError},
"unable to settle invoice: %v", err)
return false
} }
l.infof("settling %x as exit hop", pd.RHash)
// If the link is in hodl.BogusSettle mode, replace the
// preimage with a fake one before sending it to the
// peer.
if l.cfg.DebugHTLC &&
l.cfg.HodlMask.Active(hodl.BogusSettle) {
l.warnf(hodl.BogusSettle.Warning())
preimage = [32]byte{}
copy(preimage[:], bytes.Repeat([]byte{2}, 32))
}
// HTLC was successfully settled locally send
// notification about it remote peer.
l.cfg.Peer.SendMessage(false, &lnwire.UpdateFulfillHTLC{
ChanID: l.ChanID(),
ID: pd.HtlcIndex,
PaymentPreimage: preimage,
})
needUpdate = true
// There are additional channels left within this route. So // There are additional channels left within this route. So
// we'll simply do some forwarding package book-keeping. // we'll simply do some forwarding package book-keeping.
default: default:
@ -2733,6 +2656,208 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
return needUpdate return needUpdate
} }
// processExitHop handles an htlc for which this link is the exit hop. It
// returns a boolean indicating whether the commitment tx needs an update.
func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, heightNow uint32) (
bool, error) {
// If hodl.ExitSettle is requested, we will not validate the final hop's
// ADD, nor will we settle the corresponding invoice or respond with the
// preimage.
if l.cfg.DebugHTLC && l.cfg.HodlMask.Active(hodl.ExitSettle) {
l.warnf(hodl.ExitSettle.Warning())
return false, nil
}
// First, we'll check the expiry of the HTLC itself against, the current
// block height. If the timeout is too soon, then we'll reject the HTLC.
if pd.Timeout-expiryGraceDelta <= heightNow {
log.Errorf("htlc(%x) has an expiry that's too soon: expiry=%v"+
", best_height=%v", pd.RHash[:], pd.Timeout, heightNow)
failure := lnwire.NewFinalExpiryTooSoon()
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
}
// We're the designated payment destination. Therefore we attempt to
// see if we have an invoice locally which'll allow us to settle this
// htlc.
//
// Only the immutable data from LookupInvoice is used, because otherwise
// a race condition may be created with concurrent writes to the invoice
// registry. For example: cancelation of an invoice.
invoiceHash := lntypes.Hash(pd.RHash)
invoice, minCltvDelta, err := l.cfg.Registry.LookupInvoice(invoiceHash)
if err != nil {
log.Errorf("unable to query invoice registry: %v", err)
failure := lnwire.NewFailUnknownPaymentHash(pd.Amount)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
}
// If the invoice is already settled, we choose to accept the payment to
// simplify failure recovery.
//
// NOTE: Though our recovery and forwarding logic is predominately
// batched, settling invoices happens iteratively. We may reject one of
// two payments for the same rhash at first, but then restart and reject
// both after seeing that the invoice has been settled. Without any
// record of which one settles first, it is ambiguous as to which one
// actually settled the invoice. Thus, by accepting all payments, we
// eliminate the race condition that can lead to this inconsistency.
//
// TODO(conner): track ownership of settlements to properly recover from
// failures? or add batch invoice settlement
//
// TODO(joostjager): The log statement below is not always accurate, as
// the invoice may have been canceled after the LookupInvoice call.
// Leaving it as is for now, because fixing this would involve changing
// the signature of InvoiceRegistry.SettleInvoice just because of this
// log statement.
if invoice.Terms.State == channeldb.ContractSettled {
log.Warnf("Accepting duplicate payment for hash=%x",
pd.RHash[:])
}
// If we're not currently in debug mode, and the extended htlc doesn't
// meet the value requested, then we'll fail the htlc. Otherwise, we
// settle this htlc within our local state update log, then send the
// update entry to the remote party.
//
// NOTE: We make an exception when the value requested by the invoice is
// zero. This means the invoice allows the payee to specify the amount
// of satoshis they wish to send. So since we expect the htlc to have a
// different amount, we should not fail.
if !l.cfg.DebugHTLC && invoice.Terms.Value > 0 &&
pd.Amount < invoice.Terms.Value {
log.Errorf("rejecting htlc due to incorrect amount: expected "+
"%v, received %v", invoice.Terms.Value, pd.Amount)
failure := lnwire.NewFailUnknownPaymentHash(pd.Amount)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
}
// As we're the exit hop, we'll double check the hop-payload included in
// the HTLC to ensure that it was crafted correctly by the sender and
// matches the HTLC we were extended.
//
// NOTE: We make an exception when the value requested by the invoice is
// zero. This means the invoice allows the payee to specify the amount
// of satoshis they wish to send. So since we expect the htlc to have a
// different amount, we should not fail.
if !l.cfg.DebugHTLC && invoice.Terms.Value > 0 &&
fwdInfo.AmountToForward < invoice.Terms.Value {
log.Errorf("Onion payload of incoming htlc(%x) has incorrect "+
"value: expected %v, got %v", pd.RHash,
invoice.Terms.Value, fwdInfo.AmountToForward)
failure := lnwire.NewFailUnknownPaymentHash(pd.Amount)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
}
// We'll also ensure that our time-lock value has been computed
// correctly.
expectedHeight := heightNow + minCltvDelta
switch {
case !l.cfg.DebugHTLC && pd.Timeout < expectedHeight:
log.Errorf("Incoming htlc(%x) has an expiration that is too "+
"soon: expected at least %v, got %v",
pd.RHash[:], expectedHeight, pd.Timeout)
failure := lnwire.FailFinalExpiryTooSoon{}
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
case !l.cfg.DebugHTLC && pd.Timeout != fwdInfo.OutgoingCTLV:
log.Errorf("HTLC(%x) has incorrect time-lock: expected %v, "+
"got %v", pd.RHash[:], pd.Timeout, fwdInfo.OutgoingCTLV)
failure := lnwire.NewFinalIncorrectCltvExpiry(
fwdInfo.OutgoingCTLV,
)
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
return true, nil
}
// Notify the invoiceRegistry of the exit hop htlc. If we crash right
// after this, this code will be re-executed after restart. We will
// receive back a resolution event.
event, err := l.cfg.Registry.NotifyExitHopHtlc(
invoiceHash, pd.Amount, l.hodlQueue.ChanIn(),
)
if err != nil {
return false, err
}
// Create a hodlHtlc struct and decide either resolved now or later.
htlc := hodlHtlc{
pd: pd,
obfuscator: obfuscator,
}
if event == nil {
// Save payment descriptor for future reference.
hodlHtlcs := l.hodlMap[invoiceHash]
l.hodlMap[invoiceHash] = append(hodlHtlcs, htlc)
return false, nil
}
// Process the received resolution.
err = l.processHodlEvent(*event, htlc)
if err != nil {
return false, err
}
return true, nil
}
// settleHTLC settles the HTLC on the channel.
func (l *channelLink) settleHTLC(preimage lntypes.Preimage, htlcIndex uint64,
sourceRef *channeldb.AddRef) error {
hash := preimage.Hash()
l.infof("settling htlc %v as exit hop", hash)
err := l.channel.SettleHTLC(
preimage, htlcIndex, sourceRef, nil, nil,
)
if err != nil {
return fmt.Errorf("unable to settle htlc: %v", err)
}
// If the link is in hodl.BogusSettle mode, replace the preimage with a
// fake one before sending it to the peer.
if l.cfg.DebugHTLC && l.cfg.HodlMask.Active(hodl.BogusSettle) {
l.warnf(hodl.BogusSettle.Warning())
preimage = [32]byte{}
copy(preimage[:], bytes.Repeat([]byte{2}, 32))
}
// HTLC was successfully settled locally send notification about it
// remote peer.
l.cfg.Peer.SendMessage(false, &lnwire.UpdateFulfillHTLC{
ChanID: l.ChanID(),
ID: htlcIndex,
PaymentPreimage: preimage,
})
return nil
}
// forwardBatch forwards the given htlcPackets to the switch, and waits on the // forwardBatch forwards the given htlcPackets to the switch, and waits on the
// err chan for the individual responses. This method is intended to be spawned // err chan for the individual responses. This method is intended to be spawned
// as a goroutine so the responses can be handled in the background. // as a goroutine so the responses can be handled in the background.

@ -1066,22 +1066,15 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Generate payment: invoice and htlc. // Generate payment invoice and htlc, but don't add this invoice to the
invoice, htlc, err := generatePayment(amount, htlcAmt, totalTimelock, // receiver registry. This should trigger an unknown payment hash
// failure.
_, htlc, err := generatePayment(amount, htlcAmt, totalTimelock,
blob) blob)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// We need to have wrong rhash for that reason we should change the
// preimage. Inverse first byte by xoring with 0xff.
invoice.Terms.PaymentPreimage[0] ^= byte(255)
// Check who is last in the route and add invoice to server registry.
if err := n.carolServer.registry.AddInvoice(*invoice); err != nil {
t.Fatalf("unable to add invoice in carol registry: %v", err)
}
// Send payment and expose err channel. // Send payment and expose err channel.
_, err = n.aliceServer.htlcSwitch.SendHTLC( _, err = n.aliceServer.htlcSwitch.SendHTLC(
n.firstBobChannelLink.ShortChanID(), htlc, n.firstBobChannelLink.ShortChanID(), htlc,
@ -1095,12 +1088,6 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
// Wait for Alice to receive the revocation. // Wait for Alice to receive the revocation.
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
// Check that alice invoice wasn't settled and bandwidth of htlc
// links hasn't been changed.
if invoice.Terms.State == channeldb.ContractSettled {
t.Fatal("alice invoice was settled")
}
if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore { if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore {
t.Fatal("the bandwidth of alice channel link which handles " + t.Fatal("the bandwidth of alice channel link which handles " +
"alice->bob channel should be the same") "alice->bob channel should be the same")
@ -2059,7 +2046,9 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
// We must add the invoice to the registry, such that Alice expects // We must add the invoice to the registry, such that Alice expects
// this payment. // this payment.
err = coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(*invoice) err = coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(
*invoice, htlc.PaymentHash,
)
if err != nil { if err != nil {
t.Fatalf("unable to add invoice to registry: %v", err) t.Fatalf("unable to add invoice to registry: %v", err)
} }
@ -2161,7 +2150,9 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to create payment: %v", err) t.Fatalf("unable to create payment: %v", err)
} }
err = coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(*invoice) err = coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(
*invoice, htlc.PaymentHash,
)
if err != nil { if err != nil {
t.Fatalf("unable to add invoice to registry: %v", err) t.Fatalf("unable to add invoice to registry: %v", err)
} }
@ -3817,7 +3808,9 @@ func TestChannelLinkAcceptDuplicatePayment(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := n.carolServer.registry.AddInvoice(*invoice); err != nil {
err = n.carolServer.registry.AddInvoice(*invoice, htlc.PaymentHash)
if err != nil {
t.Fatalf("unable to add invoice in carol registry: %v", err) t.Fatalf("unable to add invoice in carol registry: %v", err)
} }
@ -4195,7 +4188,8 @@ func generateHtlc(t *testing.T, coreLink *channelLink,
// We must add the invoice to the registry, such that Alice // We must add the invoice to the registry, such that Alice
// expects this payment. // expects this payment.
err := coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice( err := coreLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(
*invoice) *invoice, htlc.PaymentHash,
)
if err != nil { if err != nil {
t.Fatalf("unable to add invoice to registry: %v", err) t.Fatalf("unable to add invoice to registry: %v", err)
} }
@ -5595,3 +5589,169 @@ func TestChannelLinkCanceledInvoice(t *testing.T) {
t.Fatalf("expected unknown payment hash, but got %v", err) t.Fatalf("expected unknown payment hash, but got %v", err)
} }
} }
type hodlInvoiceTestCtx struct {
n *twoHopNetwork
startBandwidthAlice lnwire.MilliSatoshi
startBandwidthBob lnwire.MilliSatoshi
hash lntypes.Hash
preimage lntypes.Preimage
amount lnwire.MilliSatoshi
errChan chan error
cleanUp func()
}
func newHodlInvoiceTestCtx(t *testing.T) (*hodlInvoiceTestCtx, error) {
// Setup a alice-bob network.
aliceChannel, bobChannel, cleanUp, err := createTwoClusterChannels(
btcutil.SatoshiPerBitcoin*3,
btcutil.SatoshiPerBitcoin*5,
)
if err != nil {
t.Fatalf("unable to create channel: %v", err)
}
n := newTwoHopNetwork(t, aliceChannel, bobChannel, testStartingHeight)
if err := n.start(); err != nil {
t.Fatal(err)
}
aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()
bobBandwidthBefore := n.bobChannelLink.Bandwidth()
debug := false
if debug {
// Log message that alice receives.
n.aliceServer.intersect(
createLogFunc("alice", n.aliceChannelLink.ChanID()),
)
// Log message that bob receives.
n.bobServer.intersect(
createLogFunc("bob", n.bobChannelLink.ChanID()),
)
}
amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
htlcAmt, totalTimelock, hops := generateHops(
amount, testStartingHeight, n.bobChannelLink,
)
// Generate hold invoice preimage.
r, err := generateRandomBytes(sha256.Size)
if err != nil {
t.Fatal(err)
}
preimage, err := lntypes.MakePreimage(r)
if err != nil {
t.Fatal(err)
}
hash := preimage.Hash()
// Have alice pay the hodl invoice, wait for bob's commitment state to
// be updated and the invoice state to be updated.
receiver := n.bobServer
receiver.registry.settleChan = make(chan lntypes.Hash)
firstHop := n.bobChannelLink.ShortChanID()
errChan := n.makeHoldPayment(
n.aliceServer, receiver, firstHop, hops, amount, htlcAmt,
totalTimelock, preimage,
)
select {
case err := <-errChan:
t.Fatalf("no payment result expected: %v", err)
case <-time.After(time.Second):
t.Fatal("timeout")
case h := <-receiver.registry.settleChan:
if hash != h {
t.Fatal("unexpect invoice settled")
}
}
return &hodlInvoiceTestCtx{
n: n,
startBandwidthAlice: aliceBandwidthBefore,
startBandwidthBob: bobBandwidthBefore,
preimage: preimage,
hash: hash,
amount: amount,
errChan: errChan,
cleanUp: func() {
cleanUp()
n.stop()
},
}, nil
}
// TestChannelLinkHoldInvoiceSettle asserts that a hodl invoice can be settled.
func TestChannelLinkHoldInvoiceSettle(t *testing.T) {
t.Parallel()
ctx, err := newHodlInvoiceTestCtx(t)
if err != nil {
t.Fatal(err)
}
defer ctx.cleanUp()
err = ctx.n.bobServer.registry.SettleHodlInvoice(ctx.preimage)
if err != nil {
t.Fatal(err)
}
// Wait for payment to succeed.
select {
case err := <-ctx.errChan:
if err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("timeout")
}
// Wait for Bob to receive the revocation.
if ctx.startBandwidthAlice-ctx.amount !=
ctx.n.aliceChannelLink.Bandwidth() {
t.Fatal("alice bandwidth should have decrease on payment " +
"amount")
}
if ctx.startBandwidthBob+ctx.amount !=
ctx.n.bobChannelLink.Bandwidth() {
t.Fatalf("bob bandwidth isn't match: expected %v, got %v",
ctx.startBandwidthBob+ctx.amount,
ctx.n.bobChannelLink.Bandwidth())
}
}
// TestChannelLinkHoldInvoiceSettle asserts that a hodl invoice can be canceled.
func TestChannelLinkHoldInvoiceCancel(t *testing.T) {
t.Parallel()
ctx, err := newHodlInvoiceTestCtx(t)
if err != nil {
t.Fatal(err)
}
defer ctx.cleanUp()
err = ctx.n.bobServer.registry.CancelInvoice(ctx.hash)
if err != nil {
t.Fatal(err)
}
// Wait for payment to succeed.
select {
case err := <-ctx.errChan:
if !strings.Contains(err.Error(),
lnwire.CodeUnknownPaymentHash.String()) {
t.Fatal("expected unknown payment hash")
}
case <-time.After(5 * time.Second):
t.Fatal("timeout")
}
}

@ -8,6 +8,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
"os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
@ -23,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
@ -131,6 +133,7 @@ type mockServer struct {
htlcSwitch *Switch htlcSwitch *Switch
registry *mockInvoiceRegistry registry *mockInvoiceRegistry
pCache *mockPreimageCache
interceptorFuncs []messageInterceptor interceptorFuncs []messageInterceptor
} }
@ -186,19 +189,24 @@ func newMockServer(t testing.TB, name string, startingHeight uint32,
h := sha256.Sum256([]byte(name)) h := sha256.Sum256([]byte(name))
copy(id[:], h[:]) copy(id[:], h[:])
pCache := newMockPreimageCache()
htlcSwitch, err := initSwitchWithDB(startingHeight, db) htlcSwitch, err := initSwitchWithDB(startingHeight, db)
if err != nil { if err != nil {
return nil, err return nil, err
} }
registry := newMockRegistry(defaultDelta)
return &mockServer{ return &mockServer{
t: t, t: t,
id: id, id: id,
name: name, name: name,
messages: make(chan lnwire.Message, 3000), messages: make(chan lnwire.Message, 3000),
quit: make(chan struct{}), quit: make(chan struct{}),
registry: newMockRegistry(defaultDelta), registry: registry,
htlcSwitch: htlcSwitch, htlcSwitch: htlcSwitch,
pCache: pCache,
interceptorFuncs: make([]messageInterceptor, 0), interceptorFuncs: make([]messageInterceptor, 0),
}, nil }, nil
} }
@ -697,82 +705,92 @@ func (f *mockChannelLink) UpdateShortChanID() (lnwire.ShortChannelID, error) {
var _ ChannelLink = (*mockChannelLink)(nil) var _ ChannelLink = (*mockChannelLink)(nil)
type mockInvoiceRegistry struct { func newDB() (*channeldb.DB, func(), error) {
sync.Mutex // First, create a temporary directory to be used for the duration of
// this test.
tempDirName, err := ioutil.TempDir("", "channeldb")
if err != nil {
return nil, nil, err
}
invoices map[lntypes.Hash]channeldb.Invoice // Next, create channeldb for the first time.
finalDelta uint32 cdb, err := channeldb.Open(tempDirName)
if err != nil {
os.RemoveAll(tempDirName)
return nil, nil, err
}
cleanUp := func() {
cdb.Close()
os.RemoveAll(tempDirName)
}
return cdb, cleanUp, nil
}
type mockInvoiceRegistry struct {
settleChan chan lntypes.Hash
registry *invoices.InvoiceRegistry
cleanup func()
} }
func newMockRegistry(minDelta uint32) *mockInvoiceRegistry { func newMockRegistry(minDelta uint32) *mockInvoiceRegistry {
cdb, cleanup, err := newDB()
if err != nil {
panic(err)
}
decodeExpiry := func(invoice string) (uint32, error) {
return 3, nil
}
registry := invoices.NewRegistry(cdb, decodeExpiry)
registry.Start()
return &mockInvoiceRegistry{ return &mockInvoiceRegistry{
finalDelta: minDelta, registry: registry,
invoices: make(map[lntypes.Hash]channeldb.Invoice), cleanup: cleanup,
} }
} }
func (i *mockInvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (channeldb.Invoice, uint32, error) { func (i *mockInvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (channeldb.Invoice, uint32, error) {
i.Lock() return i.registry.LookupInvoice(rHash)
defer i.Unlock()
invoice, ok := i.invoices[rHash]
if !ok {
return channeldb.Invoice{}, 0, fmt.Errorf("can't find mock "+
"invoice: %x", rHash[:])
}
return invoice, i.finalDelta, nil
} }
func (i *mockInvoiceRegistry) SettleInvoice(rhash lntypes.Hash, func (i *mockInvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
amt lnwire.MilliSatoshi) error { return i.registry.SettleHodlInvoice(preimage)
}
i.Lock() func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
defer i.Unlock() amt lnwire.MilliSatoshi, hodlChan chan<- interface{}) (
*invoices.HodlEvent, error) {
invoice, ok := i.invoices[rhash] event, err := i.registry.NotifyExitHopHtlc(rhash, amt, hodlChan)
if !ok { if err != nil {
return fmt.Errorf("can't find mock invoice: %x", rhash[:]) return nil, err
}
if i.settleChan != nil {
i.settleChan <- rhash
} }
if invoice.Terms.State == channeldb.ContractSettled { return event, nil
return nil
}
invoice.Terms.State = channeldb.ContractSettled
invoice.AmtPaid = amt
i.invoices[rhash] = invoice
return nil
} }
func (i *mockInvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error { func (i *mockInvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
i.Lock() return i.registry.CancelInvoice(payHash)
defer i.Unlock()
invoice, ok := i.invoices[payHash]
if !ok {
return channeldb.ErrInvoiceNotFound
}
if invoice.Terms.State == channeldb.ContractCanceled {
return nil
}
invoice.Terms.State = channeldb.ContractCanceled
i.invoices[payHash] = invoice
return nil
} }
func (i *mockInvoiceRegistry) AddInvoice(invoice channeldb.Invoice) error { func (i *mockInvoiceRegistry) AddInvoice(invoice channeldb.Invoice,
i.Lock() paymentHash lntypes.Hash) error {
defer i.Unlock()
rhash := invoice.Terms.PaymentPreimage.Hash() _, err := i.registry.AddInvoice(&invoice, paymentHash)
i.invoices[rhash] = invoice return err
}
return nil func (i *mockInvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
i.registry.HodlUnsubscribeAll(subscriber)
} }
var _ InvoiceDatabase = (*mockInvoiceRegistry)(nil) var _ InvoiceDatabase = (*mockInvoiceRegistry)(nil)

@ -520,19 +520,12 @@ func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
return chanID, nil return chanID, nil
} }
// generatePayment generates the htlc add request by given path blob and // generateHoldPayment generates the htlc add request by given path blob and
// invoice which should be added by destination peer. // invoice which should be added by destination peer.
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32, func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC, error) { timelock uint32, blob [lnwire.OnionPacketSize]byte,
preimage, rhash [32]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC,
var preimage [sha256.Size]byte error) {
r, err := generateRandomBytes(sha256.Size)
if err != nil {
return nil, nil, err
}
copy(preimage[:], r)
rhash := fastsha256.Sum256(preimage[:])
invoice := &channeldb.Invoice{ invoice := &channeldb.Invoice{
CreationDate: time.Now(), CreationDate: time.Now(),
@ -552,6 +545,24 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
return invoice, htlc, nil return invoice, htlc, nil
} }
// generatePayment generates the htlc add request by given path blob and
// invoice which should be added by destination peer.
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC, error) {
var preimage [sha256.Size]byte
r, err := generateRandomBytes(sha256.Size)
if err != nil {
return nil, nil, err
}
copy(preimage[:], r)
rhash := fastsha256.Sum256(preimage[:])
return generatePaymentWithPreimage(
invoiceAmt, htlcAmt, timelock, blob, preimage, rhash,
)
}
// generateRoute generates the path blob by given array of peers. // generateRoute generates the path blob by given array of peers.
func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) { func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) {
var blob [lnwire.OnionPacketSize]byte var blob [lnwire.OnionPacketSize]byte
@ -742,7 +753,8 @@ func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
} }
// Check who is last in the route and add invoice to server registry. // Check who is last in the route and add invoice to server registry.
if err := receiver.registry.AddInvoice(*invoice); err != nil { hash := invoice.Terms.PaymentPreimage.Hash()
if err := receiver.registry.AddInvoice(*invoice, hash); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -971,7 +983,6 @@ type hopNetwork struct {
feeEstimator *mockFeeEstimator feeEstimator *mockFeeEstimator
globalPolicy ForwardingPolicy globalPolicy ForwardingPolicy
obfuscator ErrorEncrypter obfuscator ErrorEncrypter
pCache *mockPreimageCache
defaultDelta uint32 defaultDelta uint32
} }
@ -979,8 +990,6 @@ type hopNetwork struct {
func newHopNetwork() *hopNetwork { func newHopNetwork() *hopNetwork {
defaultDelta := uint32(6) defaultDelta := uint32(6)
pCache := newMockPreimageCache()
globalPolicy := ForwardingPolicy{ globalPolicy := ForwardingPolicy{
MinHTLC: lnwire.NewMSatFromSatoshis(5), MinHTLC: lnwire.NewMSatFromSatoshis(5),
BaseFee: lnwire.NewMSatFromSatoshis(1), BaseFee: lnwire.NewMSatFromSatoshis(1),
@ -997,7 +1006,6 @@ func newHopNetwork() *hopNetwork {
feeEstimator: feeEstimator, feeEstimator: feeEstimator,
globalPolicy: globalPolicy, globalPolicy: globalPolicy,
obfuscator: obfuscator, obfuscator: obfuscator,
pCache: pCache,
defaultDelta: defaultDelta, defaultDelta: defaultDelta,
} }
} }
@ -1028,7 +1036,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
FetchLastChannelUpdate: mockGetChanUpdateMessage, FetchLastChannelUpdate: mockGetChanUpdateMessage,
Registry: server.registry, Registry: server.registry,
FeeEstimator: h.feeEstimator, FeeEstimator: h.feeEstimator,
PreimageCache: h.pCache, PreimageCache: server.pCache,
UpdateContractSignals: func(*contractcourt.ContractSignals) error { UpdateContractSignals: func(*contractcourt.ContractSignals) error {
return nil return nil
}, },
@ -1136,7 +1144,7 @@ func newTwoHopNetwork(t testing.TB,
} }
} }
// start starts the three hop network alice,bob,carol servers. // start starts the two hop network alice,bob servers.
func (n *twoHopNetwork) start() error { func (n *twoHopNetwork) start() error {
if err := n.aliceServer.Start(); err != nil { if err := n.aliceServer.Start(); err != nil {
return err return err
@ -1166,3 +1174,48 @@ func (n *twoHopNetwork) stop() {
<-done <-done
} }
} }
func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
firstHop lnwire.ShortChannelID, hops []ForwardingInfo,
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
timelock uint32, preimage lntypes.Preimage) chan error {
paymentErr := make(chan error, 1)
sender := sendingPeer.(*mockServer)
receiver := receivingPeer.(*mockServer)
// Generate route convert it to blob, and return next destination for
// htlc add request.
blob, err := generateRoute(hops...)
if err != nil {
paymentErr <- err
return paymentErr
}
rhash := preimage.Hash()
// Generate payment: invoice and htlc.
invoice, htlc, err := generatePaymentWithPreimage(invoiceAmt, htlcAmt, timelock, blob,
channeldb.UnknownPreimage, rhash)
if err != nil {
paymentErr <- err
return paymentErr
}
// Check who is last in the route and add invoice to server registry.
if err := receiver.registry.AddInvoice(*invoice, rhash); err != nil {
paymentErr <- err
return paymentErr
}
// Send payment and expose err channel.
go func() {
_, err := sender.htlcSwitch.SendHTLC(
firstHop, htlc, newMockDeobfuscator(),
)
paymentErr <- err
}()
return paymentErr
}

@ -7,14 +7,12 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/zpay32"
) )
var ( var (
@ -29,6 +27,14 @@ var (
DebugHash = DebugPre.Hash() DebugHash = DebugPre.Hash()
) )
// HodlEvent describes how an htlc should be resolved. If HodlEvent.Preimage is
// set, the event indicates a settle event. If Preimage is nil, it is a cancel
// event.
type HodlEvent struct {
Preimage *lntypes.Preimage
Hash lntypes.Hash
}
// InvoiceRegistry is a central registry of all the outstanding invoices // InvoiceRegistry is a central registry of all the outstanding invoices
// created by the daemon. The registry is a thin wrapper around a map in order // created by the daemon. The registry is a thin wrapper around a map in order
// to ensure that all updates/reads are thread safe. // to ensure that all updates/reads are thread safe.
@ -52,7 +58,17 @@ type InvoiceRegistry struct {
// that *all* nodes are able to fully settle. // that *all* nodes are able to fully settle.
debugInvoices map[lntypes.Hash]*channeldb.Invoice debugInvoices map[lntypes.Hash]*channeldb.Invoice
activeNetParams *chaincfg.Params // decodeFinalCltvExpiry is a function used to decode the final expiry
// value from the payment request.
decodeFinalCltvExpiry func(invoice string) (uint32, error)
// subscriptions is a map from a payment hash to a list of subscribers.
// It is used for efficient notification of links.
hodlSubscriptions map[lntypes.Hash]map[chan<- interface{}]struct{}
// reverseSubscriptions tracks hashes subscribed to per subscriber. This
// is used to unsubscribe from all hashes efficiently.
hodlReverseSubscriptions map[chan<- interface{}]map[lntypes.Hash]struct{}
wg sync.WaitGroup wg sync.WaitGroup
quit chan struct{} quit chan struct{}
@ -62,8 +78,8 @@ type InvoiceRegistry struct {
// wraps the persistent on-disk invoice storage with an additional in-memory // wraps the persistent on-disk invoice storage with an additional in-memory
// layer. The in-memory layer is in place such that debug invoices can be added // layer. The in-memory layer is in place such that debug invoices can be added
// which are volatile yet available system wide within the daemon. // which are volatile yet available system wide within the daemon.
func NewRegistry(cdb *channeldb.DB, func NewRegistry(cdb *channeldb.DB, decodeFinalCltvExpiry func(invoice string) (
activeNetParams *chaincfg.Params) *InvoiceRegistry { uint32, error)) *InvoiceRegistry {
return &InvoiceRegistry{ return &InvoiceRegistry{
cdb: cdb, cdb: cdb,
@ -74,7 +90,9 @@ func NewRegistry(cdb *channeldb.DB,
newSingleSubscriptions: make(chan *SingleInvoiceSubscription), newSingleSubscriptions: make(chan *SingleInvoiceSubscription),
subscriptionCancels: make(chan uint32), subscriptionCancels: make(chan uint32),
invoiceEvents: make(chan *invoiceEvent, 100), invoiceEvents: make(chan *invoiceEvent, 100),
activeNetParams: activeNetParams, hodlSubscriptions: make(map[lntypes.Hash]map[chan<- interface{}]struct{}),
hodlReverseSubscriptions: make(map[chan<- interface{}]map[lntypes.Hash]struct{}),
decodeFinalCltvExpiry: decodeFinalCltvExpiry,
quit: make(chan struct{}), quit: make(chan struct{}),
} }
} }
@ -163,8 +181,10 @@ func (i *InvoiceRegistry) invoiceEventNotifier() {
// dispatch notifications to all registered clients. // dispatch notifications to all registered clients.
case event := <-i.invoiceEvents: case event := <-i.invoiceEvents:
// For backwards compatibility, do not notify all // For backwards compatibility, do not notify all
// invoice subscribers of cancel events // invoice subscribers of cancel and accept events.
if event.state != channeldb.ContractCanceled { if event.state != channeldb.ContractCanceled &&
event.state != channeldb.ContractAccepted {
i.dispatchToClients(event) i.dispatchToClients(event)
} }
i.dispatchToSingleClients(event) i.dispatchToSingleClients(event)
@ -380,7 +400,8 @@ func (i *InvoiceRegistry) AddDebugInvoice(amt btcutil.Amount,
// daemon add/forward HTLCs are able to obtain the proper preimage required for // daemon add/forward HTLCs are able to obtain the proper preimage required for
// redemption in the case that we're the final destination. We also return the // redemption in the case that we're the final destination. We also return the
// addIndex of the newly created invoice which monotonically increases for each // addIndex of the newly created invoice which monotonically increases for each
// new invoice added. // new invoice added. A side effect of this function is that it also sets
// AddIndex on the invoice argument.
func (i *InvoiceRegistry) AddInvoice(invoice *channeldb.Invoice, func (i *InvoiceRegistry) AddInvoice(invoice *channeldb.Invoice,
paymentHash lntypes.Hash) (uint64, error) { paymentHash lntypes.Hash) (uint64, error) {
@ -391,7 +412,7 @@ func (i *InvoiceRegistry) AddInvoice(invoice *channeldb.Invoice,
return spew.Sdump(invoice) return spew.Sdump(invoice)
})) }))
addIndex, err := i.cdb.AddInvoice(invoice) addIndex, err := i.cdb.AddInvoice(invoice, paymentHash)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -429,53 +450,110 @@ func (i *InvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (channeldb.Invoice,
return channeldb.Invoice{}, 0, err return channeldb.Invoice{}, 0, err
} }
payReq, err := zpay32.Decode( expiry, err := i.decodeFinalCltvExpiry(string(invoice.PaymentRequest))
string(invoice.PaymentRequest), i.activeNetParams,
)
if err != nil { if err != nil {
return channeldb.Invoice{}, 0, err return channeldb.Invoice{}, 0, err
} }
return invoice, uint32(payReq.MinFinalCLTVExpiry()), nil return invoice, expiry, nil
} }
// SettleInvoice attempts to mark an invoice as settled. If the invoice is a // NotifyExitHopHtlc attempts to mark an invoice as settled. If the invoice is a
// debug invoice, then this method is a noop as debug invoices are never fully // debug invoice, then this method is a noop as debug invoices are never fully
// settled. // settled. The return value describes how the htlc should be resolved.
func (i *InvoiceRegistry) SettleInvoice(rHash lntypes.Hash, //
amtPaid lnwire.MilliSatoshi) error { // When the preimage of the invoice is not yet known (hodl invoice), this
// function moves the invoice to the accepted state. When SettleHoldInvoice is
// called later, a resolution message will be send back to the caller via the
// provided hodlChan. Invoice registry sends on this channel what action needs
// to be taken on the htlc (settle or cancel). The caller needs to ensure that
// the channel is either buffered or received on from another goroutine to
// prevent deadlock.
func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
amtPaid lnwire.MilliSatoshi, hodlChan chan<- interface{}) (
*HodlEvent, error) {
i.Lock() i.Lock()
defer i.Unlock() defer i.Unlock()
log.Debugf("Settling invoice %x", rHash[:]) log.Debugf("Settling invoice %x", rHash[:])
createEvent := func(preimage *lntypes.Preimage) *HodlEvent {
return &HodlEvent{
Hash: rHash,
Preimage: preimage,
}
}
// First check the in-memory debug invoice index to see if this is an // First check the in-memory debug invoice index to see if this is an
// existing invoice added for debugging. // existing invoice added for debugging.
if _, ok := i.debugInvoices[rHash]; ok { if invoice, ok := i.debugInvoices[rHash]; ok {
// Debug invoices are never fully settled, so we simply return // Debug invoices are never fully settled, so we just settle the
// immediately in this case. // htlc in this case.
return nil return createEvent(&invoice.Terms.PaymentPreimage), nil
} }
// If this isn't a debug invoice, then we'll attempt to settle an // If this isn't a debug invoice, then we'll attempt to settle an
// invoice matching this rHash on disk (if one exists). // invoice matching this rHash on disk (if one exists).
invoice, err := i.cdb.SettleInvoice(rHash, amtPaid) invoice, err := i.cdb.AcceptOrSettleInvoice(rHash, amtPaid)
switch err {
// Implement idempotency by returning success if the invoice was already // If invoice is already settled, settle htlc. This means we accept more
// settled. // payments to the same invoice hash.
if err == channeldb.ErrInvoiceAlreadySettled { case channeldb.ErrInvoiceAlreadySettled:
log.Debugf("Invoice %v already settled", rHash) return createEvent(&invoice.Terms.PaymentPreimage), nil
return nil
// If invoice is already canceled, cancel htlc.
case channeldb.ErrInvoiceAlreadyCanceled:
return createEvent(nil), nil
// If invoice is already accepted, add this htlc to the list of
// subscribers.
case channeldb.ErrInvoiceAlreadyAccepted:
i.hodlSubscribe(hodlChan, rHash)
return nil, nil
// If this call settled the invoice, settle the htlc. Otherwise
// subscribe for a future hodl event.
case nil:
i.notifyClients(rHash, invoice, invoice.Terms.State)
switch invoice.Terms.State {
case channeldb.ContractSettled:
return createEvent(&invoice.Terms.PaymentPreimage), nil
case channeldb.ContractAccepted:
// Subscribe to updates to this invoice.
i.hodlSubscribe(hodlChan, rHash)
return nil, nil
default:
return nil, fmt.Errorf("unexpected invoice state %v",
invoice.Terms.State)
}
default:
return nil, err
} }
}
// SettleHodlInvoice sets the preimage of a hodl invoice.
func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
i.Lock()
defer i.Unlock()
invoice, err := i.cdb.SettleHoldInvoice(preimage)
if err != nil { if err != nil {
log.Errorf("Invoice SetPreimage %v: %v", preimage, err)
return err return err
} }
log.Infof("Payment received: %v", spew.Sdump(invoice)) hash := preimage.Hash()
log.Infof("Notifying clients of set preimage to %v",
invoice.Terms.PaymentPreimage)
i.notifyClients(rHash, invoice, channeldb.ContractSettled) i.notifyHodlSubscribers(HodlEvent{
Hash: hash,
Preimage: &preimage,
})
i.notifyClients(hash, invoice, invoice.Terms.State)
return nil return nil
} }
@ -496,13 +574,14 @@ func (i *InvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
log.Debugf("Invoice %v already canceled", payHash) log.Debugf("Invoice %v already canceled", payHash)
return nil return nil
} }
if err != nil { if err != nil {
return err return err
} }
log.Infof("Invoice %v canceled", payHash) log.Infof("Invoice %v canceled", payHash)
i.notifyHodlSubscribers(HodlEvent{
Hash: payHash,
})
i.notifyClients(payHash, invoice, channeldb.ContractCanceled) i.notifyClients(payHash, invoice, channeldb.ContractCanceled)
return nil return nil
@ -754,3 +833,60 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
return client return client
} }
// notifyHodlSubscribers sends out the hodl event to all current subscribers.
func (i *InvoiceRegistry) notifyHodlSubscribers(hodlEvent HodlEvent) {
subscribers, ok := i.hodlSubscriptions[hodlEvent.Hash]
if !ok {
return
}
// Notify all interested subscribers and remove subscription from both
// maps. The subscription can be removed as there only ever will be a
// single resolution for each hash.
for subscriber := range subscribers {
select {
case subscriber <- hodlEvent:
case <-i.quit:
return
}
delete(i.hodlReverseSubscriptions[subscriber], hodlEvent.Hash)
}
delete(i.hodlSubscriptions, hodlEvent.Hash)
}
// hodlSubscribe adds a new invoice subscription.
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
hash lntypes.Hash) {
log.Debugf("Hodl subscribe for %v", hash)
subscriptions, ok := i.hodlSubscriptions[hash]
if !ok {
subscriptions = make(map[chan<- interface{}]struct{})
i.hodlSubscriptions[hash] = subscriptions
}
subscriptions[subscriber] = struct{}{}
reverseSubscriptions, ok := i.hodlReverseSubscriptions[subscriber]
if !ok {
reverseSubscriptions = make(map[lntypes.Hash]struct{})
i.hodlReverseSubscriptions[subscriber] = reverseSubscriptions
}
reverseSubscriptions[hash] = struct{}{}
}
// HodlUnsubscribeAll cancels the subscription.
func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
i.Lock()
defer i.Unlock()
hashes := i.hodlReverseSubscriptions[subscriber]
for hash := range hashes {
delete(i.hodlSubscriptions[hash], subscriber)
}
delete(i.hodlReverseSubscriptions, subscriber)
}

@ -10,6 +10,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
) )
var ( var (
@ -28,22 +29,49 @@ var (
testPayReq = "lnbc500u1pwywxzwpp5nd2u9xzq02t0tuf2654as7vma42lwkcjptx4yzfq0umq4swpa7cqdqqcqzysmlpc9ewnydr8rr8dnltyxphdyf6mcqrsd6dml8zajtyhwe6a45d807kxtmzayuf0hh2d9tn478ecxkecdg7c5g85pntupug5kakm7xcpn63zqk" testPayReq = "lnbc500u1pwywxzwpp5nd2u9xzq02t0tuf2654as7vma42lwkcjptx4yzfq0umq4swpa7cqdqqcqzysmlpc9ewnydr8rr8dnltyxphdyf6mcqrsd6dml8zajtyhwe6a45d807kxtmzayuf0hh2d9tn478ecxkecdg7c5g85pntupug5kakm7xcpn63zqk"
) )
// TestSettleInvoice tests settling of an invoice and related notifications. func decodeExpiry(payReq string) (uint32, error) {
func TestSettleInvoice(t *testing.T) { invoice, err := zpay32.Decode(payReq, &chaincfg.MainNetParams)
if err != nil {
return 0, err
}
return uint32(invoice.MinFinalCLTVExpiry()), nil
}
var (
testInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: preimage,
Value: lnwire.MilliSatoshi(100000),
},
PaymentRequest: []byte(testPayReq),
}
)
func newTestContext(t *testing.T) (*InvoiceRegistry, func()) {
cdb, cleanup, err := newDB() cdb, cleanup, err := newDB()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer cleanup()
// Instantiate and start the invoice registry. // Instantiate and start the invoice registry.
registry := NewRegistry(cdb, &chaincfg.MainNetParams) registry := NewRegistry(cdb, decodeExpiry)
err = registry.Start() err = registry.Start()
if err != nil { if err != nil {
cleanup()
t.Fatal(err) t.Fatal(err)
} }
defer registry.Stop()
return registry, func() {
registry.Stop()
cleanup()
}
}
// TestSettleInvoice tests settling of an invoice and related notifications.
func TestSettleInvoice(t *testing.T) {
registry, cleanup := newTestContext(t)
defer cleanup()
allSubscriptions := registry.SubscribeNotifications(0, 0) allSubscriptions := registry.SubscribeNotifications(0, 0)
defer allSubscriptions.Cancel() defer allSubscriptions.Cancel()
@ -57,15 +85,7 @@ func TestSettleInvoice(t *testing.T) {
} }
// Add the invoice. // Add the invoice.
invoice := &channeldb.Invoice{ addIdx, err := registry.AddInvoice(testInvoice, hash)
Terms: channeldb.ContractTerm{
PaymentPreimage: preimage,
Value: lnwire.MilliSatoshi(100000),
},
PaymentRequest: []byte(testPayReq),
}
addIdx, err := registry.AddInvoice(invoice, hash)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -97,9 +117,11 @@ func TestSettleInvoice(t *testing.T) {
t.Fatal("no update received") t.Fatal("no update received")
} }
hodlChan := make(chan interface{}, 1)
// Settle invoice with a slightly higher amount. // Settle invoice with a slightly higher amount.
amtPaid := lnwire.MilliSatoshi(100500) amtPaid := lnwire.MilliSatoshi(100500)
err = registry.SettleInvoice(hash, amtPaid) _, err = registry.NotifyExitHopHtlc(hash, amtPaid, hodlChan)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -131,13 +153,13 @@ func TestSettleInvoice(t *testing.T) {
} }
// Try to settle again. // Try to settle again.
err = registry.SettleInvoice(hash, amtPaid) _, err = registry.NotifyExitHopHtlc(hash, amtPaid, hodlChan)
if err != nil { if err != nil {
t.Fatal("expected duplicate settle to succeed") t.Fatal("expected duplicate settle to succeed")
} }
// Try to settle again with a different amount. // Try to settle again with a different amount.
err = registry.SettleInvoice(hash, amtPaid+600) _, err = registry.NotifyExitHopHtlc(hash, amtPaid+600, hodlChan)
if err != nil { if err != nil {
t.Fatal("expected duplicate settle to succeed") t.Fatal("expected duplicate settle to succeed")
} }
@ -156,30 +178,25 @@ func TestSettleInvoice(t *testing.T) {
if err != channeldb.ErrInvoiceAlreadySettled { if err != channeldb.ErrInvoiceAlreadySettled {
t.Fatal("expected cancelation of a settled invoice to fail") t.Fatal("expected cancelation of a settled invoice to fail")
} }
// As this is a direct sette, we expect nothing on the hodl chan.
select {
case <-hodlChan:
t.Fatal("unexpected event")
default:
}
} }
// TestCancelInvoice tests cancelation of an invoice and related notifications. // TestCancelInvoice tests cancelation of an invoice and related notifications.
func TestCancelInvoice(t *testing.T) { func TestCancelInvoice(t *testing.T) {
cdb, cleanup, err := newDB() registry, cleanup := newTestContext(t)
if err != nil {
t.Fatal(err)
}
defer cleanup() defer cleanup()
// Instantiate and start the invoice registry.
registry := NewRegistry(cdb, &chaincfg.MainNetParams)
err = registry.Start()
if err != nil {
t.Fatal(err)
}
defer registry.Stop()
allSubscriptions := registry.SubscribeNotifications(0, 0) allSubscriptions := registry.SubscribeNotifications(0, 0)
defer allSubscriptions.Cancel() defer allSubscriptions.Cancel()
// Try to cancel the not yet existing invoice. This should fail. // Try to cancel the not yet existing invoice. This should fail.
err = registry.CancelInvoice(hash) err := registry.CancelInvoice(hash)
if err != channeldb.ErrInvoiceNotFound { if err != channeldb.ErrInvoiceNotFound {
t.Fatalf("expected ErrInvoiceNotFound, but got %v", err) t.Fatalf("expected ErrInvoiceNotFound, but got %v", err)
} }
@ -194,14 +211,7 @@ func TestCancelInvoice(t *testing.T) {
// Add the invoice. // Add the invoice.
amt := lnwire.MilliSatoshi(100000) amt := lnwire.MilliSatoshi(100000)
invoice := &channeldb.Invoice{ _, err = registry.AddInvoice(testInvoice, hash)
Terms: channeldb.ContractTerm{
PaymentPreimage: preimage,
Value: amt,
},
}
_, err = registry.AddInvoice(invoice, hash)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -261,10 +271,144 @@ func TestCancelInvoice(t *testing.T) {
t.Fatal("expected cancelation of a canceled invoice to succeed") t.Fatal("expected cancelation of a canceled invoice to succeed")
} }
// Try to settle. This should not be possible. // Notify arrival of a new htlc paying to this invoice. This should
err = registry.SettleInvoice(hash, amt) // succeed.
if err != channeldb.ErrInvoiceAlreadyCanceled { hodlChan := make(chan interface{})
t.Fatal("expected settlement of a canceled invoice to fail") event, err := registry.NotifyExitHopHtlc(hash, amt, hodlChan)
if err != nil {
t.Fatal("expected settlement of a canceled invoice to succeed")
}
if event.Preimage != nil {
t.Fatal("expected cancel hodl event")
}
}
// TestHoldInvoice tests settling of a hold invoice and related notifications.
func TestHoldInvoice(t *testing.T) {
defer timeout(t)()
cdb, cleanup, err := newDB()
defer cleanup()
// Instantiate and start the invoice registry.
registry := NewRegistry(cdb, decodeExpiry)
err = registry.Start()
if err != nil {
t.Fatal(err)
}
defer registry.Stop()
allSubscriptions := registry.SubscribeNotifications(0, 0)
defer allSubscriptions.Cancel()
// Subscribe to the not yet existing invoice.
subscription := registry.SubscribeSingleInvoice(hash)
defer subscription.Cancel()
if subscription.hash != hash {
t.Fatalf("expected subscription for provided hash")
}
// Add the invoice.
invoice := &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: channeldb.UnknownPreimage,
Value: lnwire.MilliSatoshi(100000),
},
}
_, err = registry.AddInvoice(invoice, hash)
if err != nil {
t.Fatal(err)
}
// We expect the open state to be sent to the single invoice subscriber.
update := <-subscription.Updates
if update.Terms.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
update.Terms.State)
}
// We expect a new invoice notification to be sent out.
newInvoice := <-allSubscriptions.NewInvoices
if newInvoice.Terms.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
newInvoice.Terms.State)
}
// Use slightly higher amount for accept/settle.
amtPaid := lnwire.MilliSatoshi(100500)
hodlChan := make(chan interface{}, 1)
// NotifyExitHopHtlc without a preimage present in the invoice registry
// should be possible.
event, err := registry.NotifyExitHopHtlc(hash, amtPaid, hodlChan)
if err != nil {
t.Fatalf("expected settle to succeed but got %v", err)
}
if event != nil {
t.Fatalf("unexpect direct settle")
}
// Test idempotency.
event, err = registry.NotifyExitHopHtlc(hash, amtPaid, hodlChan)
if err != nil {
t.Fatalf("expected settle to succeed but got %v", err)
}
if event != nil {
t.Fatalf("unexpect direct settle")
}
// We expect the accepted state to be sent to the single invoice
// subscriber. For all invoice subscribers, we don't expect an update.
// Those only get notified on settle.
update = <-subscription.Updates
if update.Terms.State != channeldb.ContractAccepted {
t.Fatalf("expected state ContractAccepted, but got %v",
update.Terms.State)
}
if update.AmtPaid != amtPaid {
t.Fatal("invoice AmtPaid incorrect")
}
// Settling with preimage should succeed.
err = registry.SettleHodlInvoice(preimage)
if err != nil {
t.Fatal("expected set preimage to succeed")
}
hodlEvent := (<-hodlChan).(HodlEvent)
if *hodlEvent.Preimage != preimage {
t.Fatal("unexpected preimage in hodl event")
}
// We expect a settled notification to be sent out for both all and
// single invoice subscribers.
settledInvoice := <-allSubscriptions.SettledInvoices
if settledInvoice.Terms.State != channeldb.ContractSettled {
t.Fatalf("expected state ContractSettled, but got %v",
settledInvoice.Terms.State)
}
update = <-subscription.Updates
if update.Terms.State != channeldb.ContractSettled {
t.Fatalf("expected state ContractSettled, but got %v",
update.Terms.State)
}
// Idempotency.
err = registry.SettleHodlInvoice(preimage)
if err != channeldb.ErrInvoiceAlreadySettled {
t.Fatalf("expected ErrInvoiceAlreadySettled but got %v", err)
}
// Try to cancel.
err = registry.CancelInvoice(hash)
if err == nil {
t.Fatal("expected cancelation of a settled invoice to fail")
} }
} }

26
invoices/utils_test.go Normal file

@ -0,0 +1,26 @@
package invoices
import (
"os"
"runtime/pprof"
"testing"
"time"
)
// timeout implements a test level timeout.
func timeout(t *testing.T) func() {
done := make(chan struct{})
go func() {
select {
case <-time.After(5 * time.Second):
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
panic("test timeout")
case <-done:
}
}()
return func() {
close(done)
}
}

@ -0,0 +1,416 @@
package invoicesrpc
import (
"bytes"
"context"
"crypto/rand"
"errors"
"fmt"
"math"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/netann"
"github.com/lightningnetwork/lnd/zpay32"
)
// AddInvoiceConfig contains dependencies for invoice creation.
type AddInvoiceConfig struct {
// AddInvoice is called to add the invoice to the registry.
AddInvoice func(invoice *channeldb.Invoice, paymentHash lntypes.Hash) (
uint64, error)
// IsChannelActive is used to generate valid hop hints.
IsChannelActive func(chanID lnwire.ChannelID) bool
// ChainParams are required to properly decode invoice payment requests
// that are marshalled over rpc.
ChainParams *chaincfg.Params
// NodeSigner is an implementation of the MessageSigner implementation
// that's backed by the identity private key of the running lnd node.
NodeSigner *netann.NodeSigner
// MaxPaymentMSat is the maximum allowed payment.
MaxPaymentMSat lnwire.MilliSatoshi
// DefaultCLTVExpiry is the default invoice expiry if no values is
// specified.
DefaultCLTVExpiry uint32
// ChanDB is a global boltdb instance which is needed to access the
// channel graph.
ChanDB *channeldb.DB
}
// AddInvoiceData contains the required data to create a new invoice.
type AddInvoiceData struct {
// An optional memo to attach along with the invoice. Used for record
// keeping purposes for the invoice's creator, and will also be set in
// the description field of the encoded payment request if the
// description_hash field is not being used.
Memo string
// Deprecated. An optional cryptographic receipt of payment which is not
// implemented.
Receipt []byte
// The preimage which will allow settling an incoming HTLC payable to
// this preimage. If Preimage is set, Hash should be nil. If both
// Preimage and Hash are nil, a random preimage is generated.
Preimage *lntypes.Preimage
// The hash of the preimage. If Hash is set, Preimage should be nil.
// This condition indicates that we have a 'hold invoice' for which the
// htlc will be accepted and held until the preimage becomes known.
Hash *lntypes.Hash
// The value of this invoice in satoshis.
Value btcutil.Amount
// Hash (SHA-256) of a description of the payment. Used if the
// description of payment (memo) is too long to naturally fit within the
// description field of an encoded payment request.
DescriptionHash []byte
// Payment request expiry time in seconds. Default is 3600 (1 hour).
Expiry int64
// Fallback on-chain address.
FallbackAddr string
// Delta to use for the time-lock of the CLTV extended to the final hop.
CltvExpiry uint64
// Whether this invoice should include routing hints for private
// channels.
Private bool
}
// AddInvoice attempts to add a new invoice to the invoice database. Any
// duplicated invoices are rejected, therefore all invoices *must* have a
// unique payment preimage.
func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {
var (
paymentPreimage lntypes.Preimage
paymentHash lntypes.Hash
)
switch {
// Only either preimage or hash can be set.
case invoice.Preimage != nil && invoice.Hash != nil:
return nil, nil,
errors.New("preimage and hash both set")
// Prevent the unknown preimage magic value from being used for a
// regular invoice. This would cause the invoice the be handled as if it
// was a hold invoice.
case invoice.Preimage != nil &&
*invoice.Preimage == channeldb.UnknownPreimage:
return nil, nil,
fmt.Errorf("cannot use all zeroes as a preimage")
// Prevent the hash of the unknown preimage magic value to be used for a
// hold invoice. This would make it impossible to settle the invoice,
// because it would still be interpreted as not having a preimage.
case invoice.Hash != nil &&
*invoice.Hash == channeldb.UnknownPreimage.Hash():
return nil, nil,
fmt.Errorf("cannot use hash of all zeroes preimage")
// If no hash or preimage is given, generate a random preimage.
case invoice.Preimage == nil && invoice.Hash == nil:
if _, err := rand.Read(paymentPreimage[:]); err != nil {
return nil, nil, err
}
paymentHash = paymentPreimage.Hash()
// If just a hash is given, we create a hold invoice by setting the
// preimage to unknown.
case invoice.Preimage == nil && invoice.Hash != nil:
paymentPreimage = channeldb.UnknownPreimage
paymentHash = *invoice.Hash
// A specific preimage was supplied. Use that for the invoice.
case invoice.Preimage != nil && invoice.Hash == nil:
paymentPreimage = *invoice.Preimage
paymentHash = invoice.Preimage.Hash()
}
// The size of the memo, receipt and description hash attached must not
// exceed the maximum values for either of the fields.
if len(invoice.Memo) > channeldb.MaxMemoSize {
return nil, nil, fmt.Errorf("memo too large: %v bytes "+
"(maxsize=%v)", len(invoice.Memo), channeldb.MaxMemoSize)
}
if len(invoice.Receipt) > channeldb.MaxReceiptSize {
return nil, nil, fmt.Errorf("receipt too large: %v bytes "+
"(maxsize=%v)", len(invoice.Receipt), channeldb.MaxReceiptSize)
}
if len(invoice.DescriptionHash) > 0 && len(invoice.DescriptionHash) != 32 {
return nil, nil, fmt.Errorf("description hash is %v bytes, must be %v",
len(invoice.DescriptionHash), channeldb.MaxPaymentRequestSize)
}
// The value of the invoice must not be negative.
if invoice.Value < 0 {
return nil, nil, fmt.Errorf("payments of negative value "+
"are not allowed, value is %v", invoice.Value)
}
amtMSat := lnwire.NewMSatFromSatoshis(invoice.Value)
// The value of the invoice must also not exceed the current soft-limit
// on the largest payment within the network.
if amtMSat > cfg.MaxPaymentMSat {
return nil, nil, fmt.Errorf("payment of %v is too large, max "+
"payment allowed is %v", invoice.Value,
cfg.MaxPaymentMSat.ToSatoshis(),
)
}
// We also create an encoded payment request which allows the
// caller to compactly send the invoice to the payer. We'll create a
// list of options to be added to the encoded payment request. For now
// we only support the required fields description/description_hash,
// expiry, fallback address, and the amount field.
var options []func(*zpay32.Invoice)
// We only include the amount in the invoice if it is greater than 0.
// By not including the amount, we enable the creation of invoices that
// allow the payee to specify the amount of satoshis they wish to send.
if amtMSat > 0 {
options = append(options, zpay32.Amount(amtMSat))
}
// If specified, add a fallback address to the payment request.
if len(invoice.FallbackAddr) > 0 {
addr, err := btcutil.DecodeAddress(invoice.FallbackAddr,
cfg.ChainParams)
if err != nil {
return nil, nil, fmt.Errorf("invalid fallback address: %v",
err)
}
options = append(options, zpay32.FallbackAddr(addr))
}
// If expiry is set, specify it. If it is not provided, no expiry time
// will be explicitly added to this payment request, which will imply
// the default 3600 seconds.
if invoice.Expiry > 0 {
// We'll ensure that the specified expiry is restricted to sane
// number of seconds. As a result, we'll reject an invoice with
// an expiry greater than 1 year.
maxExpiry := time.Hour * 24 * 365
expSeconds := invoice.Expiry
if float64(expSeconds) > maxExpiry.Seconds() {
return nil, nil, fmt.Errorf("expiry of %v seconds "+
"greater than max expiry of %v seconds",
float64(expSeconds), maxExpiry.Seconds())
}
expiry := time.Duration(invoice.Expiry) * time.Second
options = append(options, zpay32.Expiry(expiry))
}
// If the description hash is set, then we add it do the list of options.
// If not, use the memo field as the payment request description.
if len(invoice.DescriptionHash) > 0 {
var descHash [32]byte
copy(descHash[:], invoice.DescriptionHash[:])
options = append(options, zpay32.DescriptionHash(descHash))
} else {
// Use the memo field as the description. If this is not set
// this will just be an empty string.
options = append(options, zpay32.Description(invoice.Memo))
}
// We'll use our current default CLTV value unless one was specified as
// an option on the command line when creating an invoice.
switch {
case invoice.CltvExpiry > math.MaxUint16:
return nil, nil, fmt.Errorf("CLTV delta of %v is too large, max "+
"accepted is: %v", invoice.CltvExpiry, math.MaxUint16)
case invoice.CltvExpiry != 0:
options = append(options,
zpay32.CLTVExpiry(invoice.CltvExpiry))
default:
// TODO(roasbeef): assumes set delta between versions
defaultDelta := cfg.DefaultCLTVExpiry
options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
}
// If we were requested to include routing hints in the invoice, then
// we'll fetch all of our available private channels and create routing
// hints for them.
if invoice.Private {
openChannels, err := cfg.ChanDB.FetchAllChannels()
if err != nil {
return nil, nil, fmt.Errorf("could not fetch all channels")
}
graph := cfg.ChanDB.ChannelGraph()
numHints := 0
for _, channel := range openChannels {
// We'll restrict the number of individual route hints
// to 20 to avoid creating overly large invoices.
if numHints > 20 {
break
}
// Since we're only interested in our private channels,
// we'll skip public ones.
isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
if isPublic {
continue
}
// Make sure the counterparty has enough balance in the
// channel for our amount. We do this in order to reduce
// payment errors when attempting to use this channel
// as a hint.
chanPoint := lnwire.NewChanIDFromOutPoint(
&channel.FundingOutpoint,
)
if amtMSat >= channel.LocalCommitment.RemoteBalance {
log.Debugf("Skipping channel %v due to "+
"not having enough remote balance",
chanPoint)
continue
}
// Make sure the channel is active.
if !cfg.IsChannelActive(chanPoint) {
log.Debugf("Skipping channel %v due to not "+
"being eligible to forward payments",
chanPoint)
continue
}
// To ensure we don't leak unadvertised nodes, we'll
// make sure our counterparty is publicly advertised
// within the network. Otherwise, we'll end up leaking
// information about nodes that intend to stay
// unadvertised, like in the case of a node only having
// private channels.
var remotePub [33]byte
copy(remotePub[:], channel.IdentityPub.SerializeCompressed())
isRemoteNodePublic, err := graph.IsPublicNode(remotePub)
if err != nil {
log.Errorf("Unable to determine if node %x "+
"is advertised: %v", remotePub, err)
continue
}
if !isRemoteNodePublic {
log.Debugf("Skipping channel %v due to "+
"counterparty %x being unadvertised",
chanPoint, remotePub)
continue
}
// Fetch the policies for each end of the channel.
chanID := channel.ShortChanID().ToUint64()
info, p1, p2, err := graph.FetchChannelEdgesByID(chanID)
if err != nil {
log.Errorf("Unable to fetch the routing "+
"policies for the edges of the channel "+
"%v: %v", chanPoint, err)
continue
}
// Now, we'll need to determine which is the correct
// policy for HTLCs being sent from the remote node.
var remotePolicy *channeldb.ChannelEdgePolicy
if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
remotePolicy = p1
} else {
remotePolicy = p2
}
// If for some reason we don't yet have the edge for
// the remote party, then we'll just skip adding this
// channel as a routing hint.
if remotePolicy == nil {
continue
}
// Finally, create the routing hint for this channel and
// add it to our list of route hints.
hint := zpay32.HopHint{
NodeID: channel.IdentityPub,
ChannelID: chanID,
FeeBaseMSat: uint32(remotePolicy.FeeBaseMSat),
FeeProportionalMillionths: uint32(
remotePolicy.FeeProportionalMillionths,
),
CLTVExpiryDelta: remotePolicy.TimeLockDelta,
}
// Include the route hint in our set of options that
// will be used when creating the invoice.
routeHint := []zpay32.HopHint{hint}
options = append(options, zpay32.RouteHint(routeHint))
numHints++
}
}
// Create and encode the payment request as a bech32 (zpay32) string.
creationDate := time.Now()
payReq, err := zpay32.NewInvoice(
cfg.ChainParams, paymentHash, creationDate, options...,
)
if err != nil {
return nil, nil, err
}
payReqString, err := payReq.Encode(
zpay32.MessageSigner{
SignCompact: cfg.NodeSigner.SignDigestCompact,
},
)
if err != nil {
return nil, nil, err
}
newInvoice := &channeldb.Invoice{
CreationDate: creationDate,
Memo: []byte(invoice.Memo),
Receipt: invoice.Receipt,
PaymentRequest: []byte(payReqString),
Terms: channeldb.ContractTerm{
Value: amtMSat,
PaymentPreimage: paymentPreimage,
},
}
log.Tracef("[addinvoice] adding new invoice %v",
newLogClosure(func() string {
return spew.Sdump(newInvoice)
}),
)
// With all sanity checks passed, write the invoice to the database.
_, err = cfg.AddInvoice(newInvoice, paymentHash)
if err != nil {
return nil, nil, err
}
return &paymentHash, newInvoice, nil
}

@ -4,8 +4,11 @@ package invoicesrpc
import ( import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/netann"
) )
// Config is the primary configuration struct for the invoices RPC server. It // Config is the primary configuration struct for the invoices RPC server. It
@ -26,7 +29,25 @@ type Config struct {
// created by the daemon. // created by the daemon.
InvoiceRegistry *invoices.InvoiceRegistry InvoiceRegistry *invoices.InvoiceRegistry
// IsChannelActive is used to generate valid hop hints.
IsChannelActive func(chanID lnwire.ChannelID) bool
// ChainParams are required to properly decode invoice payment requests // ChainParams are required to properly decode invoice payment requests
// that are marshalled over rpc. // that are marshalled over rpc.
ChainParams *chaincfg.Params ChainParams *chaincfg.Params
// NodeSigner is an implementation of the MessageSigner implementation
// that's backed by the identity private key of the running lnd node.
NodeSigner *netann.NodeSigner
// MaxPaymentMSat is the maximum allowed payment.
MaxPaymentMSat lnwire.MilliSatoshi
// DefaultCLTVExpiry is the default invoice expiry if no values is
// specified.
DefaultCLTVExpiry uint32
// ChanDB is a global boltdb instance which is needed to access the
// channel graph.
ChanDB *channeldb.DB
} }

@ -26,7 +26,7 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type CancelInvoiceMsg struct { type CancelInvoiceMsg struct {
// / Hash corresponding to the invoice to cancel. // / Hash corresponding to the (hold) invoice to cancel.
PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"` PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -37,7 +37,7 @@ func (m *CancelInvoiceMsg) Reset() { *m = CancelInvoiceMsg{} }
func (m *CancelInvoiceMsg) String() string { return proto.CompactTextString(m) } func (m *CancelInvoiceMsg) String() string { return proto.CompactTextString(m) }
func (*CancelInvoiceMsg) ProtoMessage() {} func (*CancelInvoiceMsg) ProtoMessage() {}
func (*CancelInvoiceMsg) Descriptor() ([]byte, []int) { func (*CancelInvoiceMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_1b708c9c030aea0e, []int{0} return fileDescriptor_invoices_faecc7e411e82f9d, []int{0}
} }
func (m *CancelInvoiceMsg) XXX_Unmarshal(b []byte) error { func (m *CancelInvoiceMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CancelInvoiceMsg.Unmarshal(m, b) return xxx_messageInfo_CancelInvoiceMsg.Unmarshal(m, b)
@ -74,7 +74,7 @@ func (m *CancelInvoiceResp) Reset() { *m = CancelInvoiceResp{} }
func (m *CancelInvoiceResp) String() string { return proto.CompactTextString(m) } func (m *CancelInvoiceResp) String() string { return proto.CompactTextString(m) }
func (*CancelInvoiceResp) ProtoMessage() {} func (*CancelInvoiceResp) ProtoMessage() {}
func (*CancelInvoiceResp) Descriptor() ([]byte, []int) { func (*CancelInvoiceResp) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_1b708c9c030aea0e, []int{1} return fileDescriptor_invoices_faecc7e411e82f9d, []int{1}
} }
func (m *CancelInvoiceResp) XXX_Unmarshal(b []byte) error { func (m *CancelInvoiceResp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CancelInvoiceResp.Unmarshal(m, b) return xxx_messageInfo_CancelInvoiceResp.Unmarshal(m, b)
@ -94,9 +94,244 @@ func (m *CancelInvoiceResp) XXX_DiscardUnknown() {
var xxx_messageInfo_CancelInvoiceResp proto.InternalMessageInfo var xxx_messageInfo_CancelInvoiceResp proto.InternalMessageInfo
type AddHoldInvoiceRequest struct {
// *
// An optional memo to attach along with the invoice. Used for record keeping
// purposes for the invoice's creator, and will also be set in the description
// field of the encoded payment request if the description_hash field is not
// being used.
Memo string `protobuf:"bytes,1,opt,name=memo,proto3" json:"memo,omitempty"`
// / The hash of the preimage
Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
// / The value of this invoice in satoshis
Value int64 `protobuf:"varint,3,opt,name=value,proto3" json:"value,omitempty"`
// *
// Hash (SHA-256) of a description of the payment. Used if the description of
// payment (memo) is too long to naturally fit within the description field
// of an encoded payment request.
DescriptionHash []byte `protobuf:"bytes,4,opt,name=description_hash,proto3" json:"description_hash,omitempty"`
// / Payment request expiry time in seconds. Default is 3600 (1 hour).
Expiry int64 `protobuf:"varint,5,opt,name=expiry,proto3" json:"expiry,omitempty"`
// / Fallback on-chain address.
FallbackAddr string `protobuf:"bytes,6,opt,name=fallback_addr,proto3" json:"fallback_addr,omitempty"`
// / Delta to use for the time-lock of the CLTV extended to the final hop.
CltvExpiry uint64 `protobuf:"varint,7,opt,name=cltv_expiry,proto3" json:"cltv_expiry,omitempty"`
// *
// Route hints that can each be individually used to assist in reaching the
// invoice's destination.
RouteHints []*lnrpc.RouteHint `protobuf:"bytes,8,rep,name=route_hints,proto3" json:"route_hints,omitempty"`
// / Whether this invoice should include routing hints for private channels.
Private bool `protobuf:"varint,9,opt,name=private,proto3" json:"private,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AddHoldInvoiceRequest) Reset() { *m = AddHoldInvoiceRequest{} }
func (m *AddHoldInvoiceRequest) String() string { return proto.CompactTextString(m) }
func (*AddHoldInvoiceRequest) ProtoMessage() {}
func (*AddHoldInvoiceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_faecc7e411e82f9d, []int{2}
}
func (m *AddHoldInvoiceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddHoldInvoiceRequest.Unmarshal(m, b)
}
func (m *AddHoldInvoiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AddHoldInvoiceRequest.Marshal(b, m, deterministic)
}
func (dst *AddHoldInvoiceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AddHoldInvoiceRequest.Merge(dst, src)
}
func (m *AddHoldInvoiceRequest) XXX_Size() int {
return xxx_messageInfo_AddHoldInvoiceRequest.Size(m)
}
func (m *AddHoldInvoiceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AddHoldInvoiceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AddHoldInvoiceRequest proto.InternalMessageInfo
func (m *AddHoldInvoiceRequest) GetMemo() string {
if m != nil {
return m.Memo
}
return ""
}
func (m *AddHoldInvoiceRequest) GetHash() []byte {
if m != nil {
return m.Hash
}
return nil
}
func (m *AddHoldInvoiceRequest) GetValue() int64 {
if m != nil {
return m.Value
}
return 0
}
func (m *AddHoldInvoiceRequest) GetDescriptionHash() []byte {
if m != nil {
return m.DescriptionHash
}
return nil
}
func (m *AddHoldInvoiceRequest) GetExpiry() int64 {
if m != nil {
return m.Expiry
}
return 0
}
func (m *AddHoldInvoiceRequest) GetFallbackAddr() string {
if m != nil {
return m.FallbackAddr
}
return ""
}
func (m *AddHoldInvoiceRequest) GetCltvExpiry() uint64 {
if m != nil {
return m.CltvExpiry
}
return 0
}
func (m *AddHoldInvoiceRequest) GetRouteHints() []*lnrpc.RouteHint {
if m != nil {
return m.RouteHints
}
return nil
}
func (m *AddHoldInvoiceRequest) GetPrivate() bool {
if m != nil {
return m.Private
}
return false
}
type AddHoldInvoiceResp struct {
// *
// A bare-bones invoice for a payment within the Lightning Network. With the
// details of the invoice, the sender has all the data necessary to send a
// payment to the recipient.
PaymentRequest string `protobuf:"bytes,1,opt,name=payment_request,proto3" json:"payment_request,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AddHoldInvoiceResp) Reset() { *m = AddHoldInvoiceResp{} }
func (m *AddHoldInvoiceResp) String() string { return proto.CompactTextString(m) }
func (*AddHoldInvoiceResp) ProtoMessage() {}
func (*AddHoldInvoiceResp) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_faecc7e411e82f9d, []int{3}
}
func (m *AddHoldInvoiceResp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddHoldInvoiceResp.Unmarshal(m, b)
}
func (m *AddHoldInvoiceResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AddHoldInvoiceResp.Marshal(b, m, deterministic)
}
func (dst *AddHoldInvoiceResp) XXX_Merge(src proto.Message) {
xxx_messageInfo_AddHoldInvoiceResp.Merge(dst, src)
}
func (m *AddHoldInvoiceResp) XXX_Size() int {
return xxx_messageInfo_AddHoldInvoiceResp.Size(m)
}
func (m *AddHoldInvoiceResp) XXX_DiscardUnknown() {
xxx_messageInfo_AddHoldInvoiceResp.DiscardUnknown(m)
}
var xxx_messageInfo_AddHoldInvoiceResp proto.InternalMessageInfo
func (m *AddHoldInvoiceResp) GetPaymentRequest() string {
if m != nil {
return m.PaymentRequest
}
return ""
}
type SettleInvoiceMsg struct {
// / Externally discovered pre-image that should be used to settle the hold invoice.
Preimage []byte `protobuf:"bytes,1,opt,name=preimage,proto3" json:"preimage,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SettleInvoiceMsg) Reset() { *m = SettleInvoiceMsg{} }
func (m *SettleInvoiceMsg) String() string { return proto.CompactTextString(m) }
func (*SettleInvoiceMsg) ProtoMessage() {}
func (*SettleInvoiceMsg) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_faecc7e411e82f9d, []int{4}
}
func (m *SettleInvoiceMsg) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SettleInvoiceMsg.Unmarshal(m, b)
}
func (m *SettleInvoiceMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SettleInvoiceMsg.Marshal(b, m, deterministic)
}
func (dst *SettleInvoiceMsg) XXX_Merge(src proto.Message) {
xxx_messageInfo_SettleInvoiceMsg.Merge(dst, src)
}
func (m *SettleInvoiceMsg) XXX_Size() int {
return xxx_messageInfo_SettleInvoiceMsg.Size(m)
}
func (m *SettleInvoiceMsg) XXX_DiscardUnknown() {
xxx_messageInfo_SettleInvoiceMsg.DiscardUnknown(m)
}
var xxx_messageInfo_SettleInvoiceMsg proto.InternalMessageInfo
func (m *SettleInvoiceMsg) GetPreimage() []byte {
if m != nil {
return m.Preimage
}
return nil
}
type SettleInvoiceResp struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SettleInvoiceResp) Reset() { *m = SettleInvoiceResp{} }
func (m *SettleInvoiceResp) String() string { return proto.CompactTextString(m) }
func (*SettleInvoiceResp) ProtoMessage() {}
func (*SettleInvoiceResp) Descriptor() ([]byte, []int) {
return fileDescriptor_invoices_faecc7e411e82f9d, []int{5}
}
func (m *SettleInvoiceResp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SettleInvoiceResp.Unmarshal(m, b)
}
func (m *SettleInvoiceResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SettleInvoiceResp.Marshal(b, m, deterministic)
}
func (dst *SettleInvoiceResp) XXX_Merge(src proto.Message) {
xxx_messageInfo_SettleInvoiceResp.Merge(dst, src)
}
func (m *SettleInvoiceResp) XXX_Size() int {
return xxx_messageInfo_SettleInvoiceResp.Size(m)
}
func (m *SettleInvoiceResp) XXX_DiscardUnknown() {
xxx_messageInfo_SettleInvoiceResp.DiscardUnknown(m)
}
var xxx_messageInfo_SettleInvoiceResp proto.InternalMessageInfo
func init() { func init() {
proto.RegisterType((*CancelInvoiceMsg)(nil), "invoicesrpc.CancelInvoiceMsg") proto.RegisterType((*CancelInvoiceMsg)(nil), "invoicesrpc.CancelInvoiceMsg")
proto.RegisterType((*CancelInvoiceResp)(nil), "invoicesrpc.CancelInvoiceResp") proto.RegisterType((*CancelInvoiceResp)(nil), "invoicesrpc.CancelInvoiceResp")
proto.RegisterType((*AddHoldInvoiceRequest)(nil), "invoicesrpc.AddHoldInvoiceRequest")
proto.RegisterType((*AddHoldInvoiceResp)(nil), "invoicesrpc.AddHoldInvoiceResp")
proto.RegisterType((*SettleInvoiceMsg)(nil), "invoicesrpc.SettleInvoiceMsg")
proto.RegisterType((*SettleInvoiceResp)(nil), "invoicesrpc.SettleInvoiceResp")
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -121,6 +356,14 @@ type InvoicesClient interface {
// canceled, this call will succeed. If the invoice is already settled, it will // canceled, this call will succeed. If the invoice is already settled, it will
// fail. // fail.
CancelInvoice(ctx context.Context, in *CancelInvoiceMsg, opts ...grpc.CallOption) (*CancelInvoiceResp, error) CancelInvoice(ctx context.Context, in *CancelInvoiceMsg, opts ...grpc.CallOption) (*CancelInvoiceResp, error)
// *
// AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
// supplied in the request.
AddHoldInvoice(ctx context.Context, in *AddHoldInvoiceRequest, opts ...grpc.CallOption) (*AddHoldInvoiceResp, error)
// *
// SettleInvoice settles an accepted invoice. If the invoice is already
// settled, this call will succeed.
SettleInvoice(ctx context.Context, in *SettleInvoiceMsg, opts ...grpc.CallOption) (*SettleInvoiceResp, error)
} }
type invoicesClient struct { type invoicesClient struct {
@ -172,6 +415,24 @@ func (c *invoicesClient) CancelInvoice(ctx context.Context, in *CancelInvoiceMsg
return out, nil return out, nil
} }
func (c *invoicesClient) AddHoldInvoice(ctx context.Context, in *AddHoldInvoiceRequest, opts ...grpc.CallOption) (*AddHoldInvoiceResp, error) {
out := new(AddHoldInvoiceResp)
err := c.cc.Invoke(ctx, "/invoicesrpc.Invoices/AddHoldInvoice", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *invoicesClient) SettleInvoice(ctx context.Context, in *SettleInvoiceMsg, opts ...grpc.CallOption) (*SettleInvoiceResp, error) {
out := new(SettleInvoiceResp)
err := c.cc.Invoke(ctx, "/invoicesrpc.Invoices/SettleInvoice", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// InvoicesServer is the server API for Invoices service. // InvoicesServer is the server API for Invoices service.
type InvoicesServer interface { type InvoicesServer interface {
// * // *
@ -184,6 +445,14 @@ type InvoicesServer interface {
// canceled, this call will succeed. If the invoice is already settled, it will // canceled, this call will succeed. If the invoice is already settled, it will
// fail. // fail.
CancelInvoice(context.Context, *CancelInvoiceMsg) (*CancelInvoiceResp, error) CancelInvoice(context.Context, *CancelInvoiceMsg) (*CancelInvoiceResp, error)
// *
// AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
// supplied in the request.
AddHoldInvoice(context.Context, *AddHoldInvoiceRequest) (*AddHoldInvoiceResp, error)
// *
// SettleInvoice settles an accepted invoice. If the invoice is already
// settled, this call will succeed.
SettleInvoice(context.Context, *SettleInvoiceMsg) (*SettleInvoiceResp, error)
} }
func RegisterInvoicesServer(s *grpc.Server, srv InvoicesServer) { func RegisterInvoicesServer(s *grpc.Server, srv InvoicesServer) {
@ -229,6 +498,42 @@ func _Invoices_CancelInvoice_Handler(srv interface{}, ctx context.Context, dec f
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Invoices_AddHoldInvoice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddHoldInvoiceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InvoicesServer).AddHoldInvoice(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/invoicesrpc.Invoices/AddHoldInvoice",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InvoicesServer).AddHoldInvoice(ctx, req.(*AddHoldInvoiceRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Invoices_SettleInvoice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SettleInvoiceMsg)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InvoicesServer).SettleInvoice(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/invoicesrpc.Invoices/SettleInvoice",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InvoicesServer).SettleInvoice(ctx, req.(*SettleInvoiceMsg))
}
return interceptor(ctx, in, info, handler)
}
var _Invoices_serviceDesc = grpc.ServiceDesc{ var _Invoices_serviceDesc = grpc.ServiceDesc{
ServiceName: "invoicesrpc.Invoices", ServiceName: "invoicesrpc.Invoices",
HandlerType: (*InvoicesServer)(nil), HandlerType: (*InvoicesServer)(nil),
@ -237,6 +542,14 @@ var _Invoices_serviceDesc = grpc.ServiceDesc{
MethodName: "CancelInvoice", MethodName: "CancelInvoice",
Handler: _Invoices_CancelInvoice_Handler, Handler: _Invoices_CancelInvoice_Handler,
}, },
{
MethodName: "AddHoldInvoice",
Handler: _Invoices_AddHoldInvoice_Handler,
},
{
MethodName: "SettleInvoice",
Handler: _Invoices_SettleInvoice_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
@ -249,25 +562,40 @@ var _Invoices_serviceDesc = grpc.ServiceDesc{
} }
func init() { func init() {
proto.RegisterFile("invoicesrpc/invoices.proto", fileDescriptor_invoices_1b708c9c030aea0e) proto.RegisterFile("invoicesrpc/invoices.proto", fileDescriptor_invoices_faecc7e411e82f9d)
} }
var fileDescriptor_invoices_1b708c9c030aea0e = []byte{ var fileDescriptor_invoices_faecc7e411e82f9d = []byte{
// 246 bytes of a gzipped FileDescriptorProto // 490 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xbf, 0x4b, 0x43, 0x31, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4f, 0x8f, 0xd3, 0x3c,
0x10, 0xc7, 0x79, 0x8b, 0x68, 0x5a, 0x45, 0x23, 0x88, 0x04, 0x15, 0xed, 0xe4, 0x94, 0xa8, 0xc5, 0x10, 0xc6, 0x95, 0xb6, 0xdb, 0x6d, 0xa7, 0xbb, 0xfb, 0xf6, 0x35, 0xb0, 0x8a, 0x22, 0xfe, 0x84,
0xd5, 0x41, 0x17, 0x1d, 0x14, 0x69, 0x37, 0x17, 0xc9, 0x8b, 0x21, 0x39, 0x4c, 0xef, 0x42, 0x92, 0x88, 0x43, 0xc4, 0x21, 0x81, 0xae, 0xb8, 0xae, 0x04, 0x5c, 0xca, 0x01, 0x84, 0x52, 0x71, 0xe1,
0x2a, 0xfe, 0x2b, 0xfe, 0xb5, 0xd2, 0x36, 0xe2, 0x7b, 0x42, 0xb7, 0xcb, 0x7d, 0x7f, 0xe4, 0x93, 0x52, 0xb9, 0x89, 0x49, 0xac, 0x75, 0x6d, 0x63, 0xbb, 0x85, 0xfd, 0x54, 0x7c, 0x06, 0xbe, 0x19,
0x30, 0x01, 0xf8, 0x41, 0x60, 0x6c, 0x4e, 0xd1, 0xa8, 0xdf, 0x59, 0xc6, 0x44, 0x85, 0xf8, 0xa0, 0x8a, 0xe3, 0x96, 0x24, 0x0b, 0xdc, 0x66, 0x1e, 0xcf, 0x3c, 0x19, 0xff, 0x3c, 0x81, 0x80, 0xf2,
0xa3, 0x89, 0x23, 0x47, 0xe4, 0x82, 0x55, 0x3a, 0x82, 0xd2, 0x88, 0x54, 0x74, 0x01, 0xc2, 0x6a, 0xbd, 0xa0, 0x39, 0xd1, 0x4a, 0xe6, 0xe9, 0x21, 0x4e, 0xa4, 0x12, 0x46, 0xa0, 0x59, 0xeb, 0x2c,
0x15, 0x5b, 0x29, 0x9a, 0xd5, 0x38, 0xba, 0x66, 0xbb, 0x77, 0x1a, 0x8d, 0x0d, 0x0f, 0xab, 0xf4, 0x78, 0x58, 0x0a, 0x51, 0x32, 0x92, 0x62, 0x49, 0x53, 0xcc, 0xb9, 0x30, 0xd8, 0x50, 0xc1, 0x5d,
0x63, 0x76, 0xfc, 0x8c, 0x0d, 0xa3, 0xfe, 0x9a, 0x59, 0x2c, 0xaf, 0x5e, 0x67, 0x7f, 0xd8, 0x9c, 0x69, 0x30, 0x55, 0x32, 0x6f, 0xc2, 0xe8, 0x15, 0xcc, 0xdf, 0x62, 0x9e, 0x13, 0xf6, 0xae, 0xe9,
0x36, 0xe7, 0xc3, 0xc9, 0xa0, 0xee, 0xee, 0x75, 0xf6, 0xa3, 0x7d, 0xb6, 0xd7, 0x8b, 0x4d, 0x6c, 0x7e, 0xaf, 0x4b, 0xf4, 0x14, 0xce, 0x24, 0xbe, 0xdd, 0x12, 0x6e, 0xd6, 0x15, 0xd6, 0x95, 0xef,
0x8e, 0x57, 0xdf, 0x0d, 0xdb, 0xac, 0xe7, 0xcc, 0x6f, 0xd8, 0xc1, 0x74, 0xde, 0x66, 0x93, 0xa0, 0x85, 0x5e, 0x7c, 0x96, 0xcd, 0x9c, 0xb6, 0xc4, 0xba, 0x8a, 0xee, 0xc1, 0xff, 0x9d, 0xb6, 0x8c,
0xb5, 0x53, 0x40, 0x17, 0x6c, 0x95, 0x38, 0x97, 0x01, 0x17, 0x00, 0xcf, 0x7f, 0x7d, 0x62, 0xa7, 0x68, 0x19, 0xfd, 0x18, 0xc0, 0x83, 0xd7, 0x45, 0xb1, 0x14, 0xac, 0x38, 0xca, 0x5f, 0x77, 0x44,
0xee, 0xaa, 0xe7, 0xa2, 0xe1, 0x4f, 0x6c, 0xbb, 0x77, 0x03, 0x3f, 0x96, 0x9d, 0x07, 0xca, 0xff, 0x1b, 0x84, 0x60, 0xb4, 0x25, 0x5b, 0x61, 0x9d, 0xa6, 0x99, 0x8d, 0x6b, 0xcd, 0xba, 0x0f, 0xac,
0xd0, 0xe2, 0x64, 0xbd, 0xbc, 0x80, 0xbb, 0x1d, 0xbf, 0x5c, 0x3a, 0x28, 0x7e, 0xde, 0x4a, 0x43, 0xbb, 0x8d, 0xd1, 0x7d, 0x38, 0xd9, 0x63, 0xb6, 0x23, 0xfe, 0x30, 0xf4, 0xe2, 0x61, 0xd6, 0x24,
0x33, 0x15, 0xc0, 0xf9, 0x82, 0x80, 0x0e, 0x6d, 0xf9, 0xa4, 0xf4, 0xae, 0x02, 0xbe, 0xa9, 0x25, 0xe8, 0x39, 0xcc, 0x0b, 0xa2, 0x73, 0x45, 0x65, 0x7d, 0x89, 0x66, 0xa6, 0x91, 0xed, 0xba, 0xa3,
0x82, 0xea, 0xd4, 0xb4, 0x1b, 0xcb, 0x4f, 0x1a, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xc3, 0xa3, 0x4b, 0x18, 0x93, 0xef, 0x92, 0xaa, 0x5b, 0xff, 0xc4, 0x5a, 0xb8, 0x0c, 0x3d, 0x83, 0xf3,
0x7e, 0x3a, 0x78, 0x01, 0x00, 0x00, 0x2f, 0x98, 0xb1, 0x0d, 0xce, 0x6f, 0xd6, 0xb8, 0x28, 0x94, 0x3f, 0xb6, 0xa3, 0x74, 0x45, 0x14,
0xc2, 0x2c, 0x67, 0x66, 0xbf, 0x76, 0x16, 0xa7, 0xa1, 0x17, 0x8f, 0xb2, 0xb6, 0x84, 0x16, 0x30,
0x53, 0x62, 0x67, 0xc8, 0xba, 0xa2, 0xdc, 0x68, 0x7f, 0x12, 0x0e, 0xe3, 0xd9, 0x62, 0x9e, 0x30,
0x5e, 0x23, 0xcd, 0xea, 0x93, 0x25, 0xe5, 0x26, 0x6b, 0x17, 0x21, 0x1f, 0x4e, 0xa5, 0xa2, 0x7b,
0x6c, 0x88, 0x3f, 0x0d, 0xbd, 0x78, 0x92, 0x1d, 0xd2, 0xe8, 0x1a, 0x50, 0x1f, 0x98, 0x96, 0x28,
0x86, 0xff, 0x0e, 0xfc, 0x55, 0x03, 0xd0, 0x81, 0xeb, 0xcb, 0x51, 0x02, 0xf3, 0x15, 0x31, 0x86,
0x91, 0xd6, 0xeb, 0x05, 0x30, 0x91, 0x8a, 0xd0, 0x2d, 0x2e, 0x89, 0x7b, 0xb9, 0x63, 0x5e, 0x3f,
0x5b, 0xa7, 0xbe, 0xfe, 0xdc, 0xe2, 0xe7, 0x00, 0x26, 0x2e, 0xd7, 0xe8, 0x1a, 0x2e, 0x57, 0xbb,
0x4d, 0x0d, 0x75, 0x43, 0x56, 0x94, 0x97, 0xc7, 0x52, 0x84, 0xdc, 0x25, 0x3f, 0xfe, 0x5e, 0x83,
0xe0, 0xc2, 0x69, 0xae, 0xe6, 0x85, 0x87, 0x3e, 0xc0, 0x79, 0x67, 0x31, 0xd0, 0xa3, 0xa4, 0xb5,
0x97, 0x49, 0x7f, 0xd7, 0x82, 0xc7, 0x7f, 0x3f, 0xb6, 0x2c, 0x3e, 0xc1, 0x45, 0x97, 0x10, 0x8a,
0x3a, 0x1d, 0x7f, 0xdc, 0xb7, 0xe0, 0xc9, 0x3f, 0x6b, 0xb4, 0xac, 0xc7, 0xec, 0x80, 0xe8, 0x8d,
0xd9, 0x87, 0xda, 0x1b, 0xf3, 0x0e, 0xc3, 0x37, 0x57, 0x9f, 0x5f, 0x96, 0xd4, 0x54, 0xbb, 0x4d,
0x92, 0x8b, 0x6d, 0xca, 0x68, 0x59, 0x19, 0x4e, 0x79, 0xc9, 0x89, 0xf9, 0x26, 0xd4, 0x4d, 0xca,
0x78, 0x91, 0x5a, 0x52, 0x69, 0xcb, 0x66, 0x33, 0xb6, 0xbf, 0xe0, 0xd5, 0xaf, 0x00, 0x00, 0x00,
0xff, 0xff, 0x89, 0x60, 0x5e, 0x11, 0xd6, 0x03, 0x00, 0x00,
} }

@ -23,10 +23,79 @@ service Invoices {
fail. fail.
*/ */
rpc CancelInvoice(CancelInvoiceMsg) returns (CancelInvoiceResp); rpc CancelInvoice(CancelInvoiceMsg) returns (CancelInvoiceResp);
/**
AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
supplied in the request.
*/
rpc AddHoldInvoice(AddHoldInvoiceRequest) returns (AddHoldInvoiceResp);
/**
SettleInvoice settles an accepted invoice. If the invoice is already
settled, this call will succeed.
*/
rpc SettleInvoice(SettleInvoiceMsg) returns (SettleInvoiceResp);
} }
message CancelInvoiceMsg { message CancelInvoiceMsg {
/// Hash corresponding to the invoice to cancel. /// Hash corresponding to the (hold) invoice to cancel.
bytes payment_hash = 1; bytes payment_hash = 1;
} }
message CancelInvoiceResp {} message CancelInvoiceResp {}
message AddHoldInvoiceRequest {
/**
An optional memo to attach along with the invoice. Used for record keeping
purposes for the invoice's creator, and will also be set in the description
field of the encoded payment request if the description_hash field is not
being used.
*/
string memo = 1 [json_name = "memo"];
/// The hash of the preimage
bytes hash = 2 [json_name = "hash"];
/// The value of this invoice in satoshis
int64 value = 3 [json_name = "value"];
/**
Hash (SHA-256) of a description of the payment. Used if the description of
payment (memo) is too long to naturally fit within the description field
of an encoded payment request.
*/
bytes description_hash = 4 [json_name = "description_hash"];
/// Payment request expiry time in seconds. Default is 3600 (1 hour).
int64 expiry = 5 [json_name = "expiry"];
/// Fallback on-chain address.
string fallback_addr = 6 [json_name = "fallback_addr"];
/// Delta to use for the time-lock of the CLTV extended to the final hop.
uint64 cltv_expiry = 7 [json_name = "cltv_expiry"];
/**
Route hints that can each be individually used to assist in reaching the
invoice's destination.
*/
repeated lnrpc.RouteHint route_hints = 8 [json_name = "route_hints"];
/// Whether this invoice should include routing hints for private channels.
bool private = 9 [json_name = "private"];
}
message AddHoldInvoiceResp {
/**
A bare-bones invoice for a payment within the Lightning Network. With the
details of the invoice, the sender has all the data necessary to send a
payment to the recipient.
*/
string payment_request = 1 [json_name = "payment_request"];
}
message SettleInvoiceMsg {
/// Externally discovered pre-image that should be used to settle the hold invoice.
bytes preimage = 1;
}
message SettleInvoiceResp {}

@ -11,6 +11,8 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
) )
@ -43,10 +45,18 @@ var (
Entity: "invoices", Entity: "invoices",
Action: "read", Action: "read",
}}, }},
"/invoicesrpc.Invoices/SettleInvoice": {{
Entity: "invoices",
Action: "write",
}},
"/invoicesrpc.Invoices/CancelInvoice": {{ "/invoicesrpc.Invoices/CancelInvoice": {{
Entity: "invoices", Entity: "invoices",
Action: "write", Action: "write",
}}, }},
"/invoicesrpc.Invoices/AddHoldInvoice": {{
Entity: "invoices",
Action: "write",
}},
} }
// DefaultInvoicesMacFilename is the default name of the invoices // DefaultInvoicesMacFilename is the default name of the invoices
@ -163,12 +173,12 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error {
func (s *Server) SubscribeSingleInvoice(req *lnrpc.PaymentHash, func (s *Server) SubscribeSingleInvoice(req *lnrpc.PaymentHash,
updateStream Invoices_SubscribeSingleInvoiceServer) error { updateStream Invoices_SubscribeSingleInvoiceServer) error {
hash, err := lntypes.NewHash(req.RHash) hash, err := lntypes.MakeHash(req.RHash)
if err != nil { if err != nil {
return err return err
} }
invoiceClient := s.cfg.InvoiceRegistry.SubscribeSingleInvoice(*hash) invoiceClient := s.cfg.InvoiceRegistry.SubscribeSingleInvoice(hash)
defer invoiceClient.Cancel() defer invoiceClient.Cancel()
for { for {
@ -191,18 +201,36 @@ func (s *Server) SubscribeSingleInvoice(req *lnrpc.PaymentHash,
} }
} }
// SettleInvoice settles an accepted invoice. If the invoice is already settled,
// this call will succeed.
func (s *Server) SettleInvoice(ctx context.Context,
in *SettleInvoiceMsg) (*SettleInvoiceResp, error) {
preimage, err := lntypes.MakePreimage(in.Preimage)
if err != nil {
return nil, err
}
err = s.cfg.InvoiceRegistry.SettleHodlInvoice(preimage)
if err != nil && err != channeldb.ErrInvoiceAlreadySettled {
return nil, err
}
return &SettleInvoiceResp{}, nil
}
// CancelInvoice cancels a currently open invoice. If the invoice is already // CancelInvoice cancels a currently open invoice. If the invoice is already
// canceled, this call will succeed. If the invoice is already settled, it will // canceled, this call will succeed. If the invoice is already settled, it will
// fail. // fail.
func (s *Server) CancelInvoice(ctx context.Context, func (s *Server) CancelInvoice(ctx context.Context,
in *CancelInvoiceMsg) (*CancelInvoiceResp, error) { in *CancelInvoiceMsg) (*CancelInvoiceResp, error) {
paymentHash, err := lntypes.NewHash(in.PaymentHash) paymentHash, err := lntypes.MakeHash(in.PaymentHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.cfg.InvoiceRegistry.CancelInvoice(*paymentHash) err = s.cfg.InvoiceRegistry.CancelInvoice(paymentHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -211,3 +239,45 @@ func (s *Server) CancelInvoice(ctx context.Context,
return &CancelInvoiceResp{}, nil return &CancelInvoiceResp{}, nil
} }
// AddHoldInvoice attempts to add a new hold invoice to the invoice database.
// Any duplicated invoices are rejected, therefore all invoices *must* have a
// unique payment hash.
func (s *Server) AddHoldInvoice(ctx context.Context,
invoice *AddHoldInvoiceRequest) (*AddHoldInvoiceResp, error) {
addInvoiceCfg := &AddInvoiceConfig{
AddInvoice: s.cfg.InvoiceRegistry.AddInvoice,
IsChannelActive: s.cfg.IsChannelActive,
ChainParams: s.cfg.ChainParams,
NodeSigner: s.cfg.NodeSigner,
MaxPaymentMSat: s.cfg.MaxPaymentMSat,
DefaultCLTVExpiry: s.cfg.DefaultCLTVExpiry,
ChanDB: s.cfg.ChanDB,
}
hash, err := lntypes.MakeHash(invoice.Hash)
if err != nil {
return nil, err
}
addInvoiceData := &AddInvoiceData{
Memo: invoice.Memo,
Hash: &hash,
Value: btcutil.Amount(invoice.Value),
DescriptionHash: invoice.DescriptionHash,
Expiry: invoice.Expiry,
FallbackAddr: invoice.FallbackAddr,
CltvExpiry: invoice.CltvExpiry,
Private: invoice.Private,
}
_, dbInvoice, err := AddInvoice(ctx, addInvoiceCfg, addInvoiceData)
if err != nil {
return nil, err
}
return &AddHoldInvoiceResp{
PaymentRequest: string(dbInvoice.PaymentRequest),
}, nil
}

@ -7,7 +7,6 @@ import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -61,16 +60,17 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
state = lnrpc.Invoice_SETTLED state = lnrpc.Invoice_SETTLED
case channeldb.ContractCanceled: case channeldb.ContractCanceled:
state = lnrpc.Invoice_CANCELED state = lnrpc.Invoice_CANCELED
case channeldb.ContractAccepted:
state = lnrpc.Invoice_ACCEPTED
default: default:
return nil, fmt.Errorf("unknown invoice state %v", return nil, fmt.Errorf("unknown invoice state %v",
invoice.Terms.State) invoice.Terms.State)
} }
return &lnrpc.Invoice{ rpcInvoice := &lnrpc.Invoice{
Memo: string(invoice.Memo[:]), Memo: string(invoice.Memo[:]),
Receipt: invoice.Receipt[:], Receipt: invoice.Receipt[:],
RHash: decoded.PaymentHash[:], RHash: decoded.PaymentHash[:],
RPreimage: preimage[:],
Value: int64(satAmt), Value: int64(satAmt),
CreationDate: invoice.CreationDate.Unix(), CreationDate: invoice.CreationDate.Unix(),
SettleDate: settleDate, SettleDate: settleDate,
@ -88,12 +88,18 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
AmtPaidMsat: int64(invoice.AmtPaid), AmtPaidMsat: int64(invoice.AmtPaid),
AmtPaid: int64(invoice.AmtPaid), AmtPaid: int64(invoice.AmtPaid),
State: state, State: state,
}, nil }
if preimage != channeldb.UnknownPreimage {
rpcInvoice.RPreimage = preimage[:]
}
return rpcInvoice, nil
} }
// CreateRPCRouteHints takes in the decoded form of an invoice's route hints // CreateRPCRouteHints takes in the decoded form of an invoice's route hints
// and converts them into the lnrpc type. // and converts them into the lnrpc type.
func CreateRPCRouteHints(routeHints [][]routing.HopHint) []*lnrpc.RouteHint { func CreateRPCRouteHints(routeHints [][]zpay32.HopHint) []*lnrpc.RouteHint {
var res []*lnrpc.RouteHint var res []*lnrpc.RouteHint
for _, route := range routeHints { for _, route := range routeHints {

@ -55,7 +55,7 @@ func (x AddressType) String() string {
return proto.EnumName(AddressType_name, int32(x)) return proto.EnumName(AddressType_name, int32(x))
} }
func (AddressType) EnumDescriptor() ([]byte, []int) { func (AddressType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{0}
} }
type ChannelCloseSummary_ClosureType int32 type ChannelCloseSummary_ClosureType int32
@ -90,7 +90,7 @@ func (x ChannelCloseSummary_ClosureType) String() string {
return proto.EnumName(ChannelCloseSummary_ClosureType_name, int32(x)) return proto.EnumName(ChannelCloseSummary_ClosureType_name, int32(x))
} }
func (ChannelCloseSummary_ClosureType) EnumDescriptor() ([]byte, []int) { func (ChannelCloseSummary_ClosureType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{39, 0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{39, 0}
} }
type ChannelEventUpdate_UpdateType int32 type ChannelEventUpdate_UpdateType int32
@ -119,7 +119,7 @@ func (x ChannelEventUpdate_UpdateType) String() string {
return proto.EnumName(ChannelEventUpdate_UpdateType_name, int32(x)) return proto.EnumName(ChannelEventUpdate_UpdateType_name, int32(x))
} }
func (ChannelEventUpdate_UpdateType) EnumDescriptor() ([]byte, []int) { func (ChannelEventUpdate_UpdateType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{60, 0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{60, 0}
} }
type Invoice_InvoiceState int32 type Invoice_InvoiceState int32
@ -128,24 +128,27 @@ const (
Invoice_OPEN Invoice_InvoiceState = 0 Invoice_OPEN Invoice_InvoiceState = 0
Invoice_SETTLED Invoice_InvoiceState = 1 Invoice_SETTLED Invoice_InvoiceState = 1
Invoice_CANCELED Invoice_InvoiceState = 2 Invoice_CANCELED Invoice_InvoiceState = 2
Invoice_ACCEPTED Invoice_InvoiceState = 3
) )
var Invoice_InvoiceState_name = map[int32]string{ var Invoice_InvoiceState_name = map[int32]string{
0: "OPEN", 0: "OPEN",
1: "SETTLED", 1: "SETTLED",
2: "CANCELED", 2: "CANCELED",
3: "ACCEPTED",
} }
var Invoice_InvoiceState_value = map[string]int32{ var Invoice_InvoiceState_value = map[string]int32{
"OPEN": 0, "OPEN": 0,
"SETTLED": 1, "SETTLED": 1,
"CANCELED": 2, "CANCELED": 2,
"ACCEPTED": 3,
} }
func (x Invoice_InvoiceState) String() string { func (x Invoice_InvoiceState) String() string {
return proto.EnumName(Invoice_InvoiceState_name, int32(x)) return proto.EnumName(Invoice_InvoiceState_name, int32(x))
} }
func (Invoice_InvoiceState) EnumDescriptor() ([]byte, []int) { func (Invoice_InvoiceState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{90, 0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{90, 0}
} }
type GenSeedRequest struct { type GenSeedRequest struct {
@ -166,7 +169,7 @@ func (m *GenSeedRequest) Reset() { *m = GenSeedRequest{} }
func (m *GenSeedRequest) String() string { return proto.CompactTextString(m) } func (m *GenSeedRequest) String() string { return proto.CompactTextString(m) }
func (*GenSeedRequest) ProtoMessage() {} func (*GenSeedRequest) ProtoMessage() {}
func (*GenSeedRequest) Descriptor() ([]byte, []int) { func (*GenSeedRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{0}
} }
func (m *GenSeedRequest) XXX_Unmarshal(b []byte) error { func (m *GenSeedRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenSeedRequest.Unmarshal(m, b) return xxx_messageInfo_GenSeedRequest.Unmarshal(m, b)
@ -221,7 +224,7 @@ func (m *GenSeedResponse) Reset() { *m = GenSeedResponse{} }
func (m *GenSeedResponse) String() string { return proto.CompactTextString(m) } func (m *GenSeedResponse) String() string { return proto.CompactTextString(m) }
func (*GenSeedResponse) ProtoMessage() {} func (*GenSeedResponse) ProtoMessage() {}
func (*GenSeedResponse) Descriptor() ([]byte, []int) { func (*GenSeedResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{1} return fileDescriptor_rpc_1d2b968b4af573ab, []int{1}
} }
func (m *GenSeedResponse) XXX_Unmarshal(b []byte) error { func (m *GenSeedResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenSeedResponse.Unmarshal(m, b) return xxx_messageInfo_GenSeedResponse.Unmarshal(m, b)
@ -286,7 +289,7 @@ func (m *InitWalletRequest) Reset() { *m = InitWalletRequest{} }
func (m *InitWalletRequest) String() string { return proto.CompactTextString(m) } func (m *InitWalletRequest) String() string { return proto.CompactTextString(m) }
func (*InitWalletRequest) ProtoMessage() {} func (*InitWalletRequest) ProtoMessage() {}
func (*InitWalletRequest) Descriptor() ([]byte, []int) { func (*InitWalletRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{2} return fileDescriptor_rpc_1d2b968b4af573ab, []int{2}
} }
func (m *InitWalletRequest) XXX_Unmarshal(b []byte) error { func (m *InitWalletRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InitWalletRequest.Unmarshal(m, b) return xxx_messageInfo_InitWalletRequest.Unmarshal(m, b)
@ -344,7 +347,7 @@ func (m *InitWalletResponse) Reset() { *m = InitWalletResponse{} }
func (m *InitWalletResponse) String() string { return proto.CompactTextString(m) } func (m *InitWalletResponse) String() string { return proto.CompactTextString(m) }
func (*InitWalletResponse) ProtoMessage() {} func (*InitWalletResponse) ProtoMessage() {}
func (*InitWalletResponse) Descriptor() ([]byte, []int) { func (*InitWalletResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{3} return fileDescriptor_rpc_1d2b968b4af573ab, []int{3}
} }
func (m *InitWalletResponse) XXX_Unmarshal(b []byte) error { func (m *InitWalletResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InitWalletResponse.Unmarshal(m, b) return xxx_messageInfo_InitWalletResponse.Unmarshal(m, b)
@ -386,7 +389,7 @@ func (m *UnlockWalletRequest) Reset() { *m = UnlockWalletRequest{} }
func (m *UnlockWalletRequest) String() string { return proto.CompactTextString(m) } func (m *UnlockWalletRequest) String() string { return proto.CompactTextString(m) }
func (*UnlockWalletRequest) ProtoMessage() {} func (*UnlockWalletRequest) ProtoMessage() {}
func (*UnlockWalletRequest) Descriptor() ([]byte, []int) { func (*UnlockWalletRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{4} return fileDescriptor_rpc_1d2b968b4af573ab, []int{4}
} }
func (m *UnlockWalletRequest) XXX_Unmarshal(b []byte) error { func (m *UnlockWalletRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UnlockWalletRequest.Unmarshal(m, b) return xxx_messageInfo_UnlockWalletRequest.Unmarshal(m, b)
@ -430,7 +433,7 @@ func (m *UnlockWalletResponse) Reset() { *m = UnlockWalletResponse{} }
func (m *UnlockWalletResponse) String() string { return proto.CompactTextString(m) } func (m *UnlockWalletResponse) String() string { return proto.CompactTextString(m) }
func (*UnlockWalletResponse) ProtoMessage() {} func (*UnlockWalletResponse) ProtoMessage() {}
func (*UnlockWalletResponse) Descriptor() ([]byte, []int) { func (*UnlockWalletResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{5} return fileDescriptor_rpc_1d2b968b4af573ab, []int{5}
} }
func (m *UnlockWalletResponse) XXX_Unmarshal(b []byte) error { func (m *UnlockWalletResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UnlockWalletResponse.Unmarshal(m, b) return xxx_messageInfo_UnlockWalletResponse.Unmarshal(m, b)
@ -468,7 +471,7 @@ func (m *ChangePasswordRequest) Reset() { *m = ChangePasswordRequest{} }
func (m *ChangePasswordRequest) String() string { return proto.CompactTextString(m) } func (m *ChangePasswordRequest) String() string { return proto.CompactTextString(m) }
func (*ChangePasswordRequest) ProtoMessage() {} func (*ChangePasswordRequest) ProtoMessage() {}
func (*ChangePasswordRequest) Descriptor() ([]byte, []int) { func (*ChangePasswordRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{6} return fileDescriptor_rpc_1d2b968b4af573ab, []int{6}
} }
func (m *ChangePasswordRequest) XXX_Unmarshal(b []byte) error { func (m *ChangePasswordRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChangePasswordRequest.Unmarshal(m, b) return xxx_messageInfo_ChangePasswordRequest.Unmarshal(m, b)
@ -512,7 +515,7 @@ func (m *ChangePasswordResponse) Reset() { *m = ChangePasswordResponse{}
func (m *ChangePasswordResponse) String() string { return proto.CompactTextString(m) } func (m *ChangePasswordResponse) String() string { return proto.CompactTextString(m) }
func (*ChangePasswordResponse) ProtoMessage() {} func (*ChangePasswordResponse) ProtoMessage() {}
func (*ChangePasswordResponse) Descriptor() ([]byte, []int) { func (*ChangePasswordResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{7} return fileDescriptor_rpc_1d2b968b4af573ab, []int{7}
} }
func (m *ChangePasswordResponse) XXX_Unmarshal(b []byte) error { func (m *ChangePasswordResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChangePasswordResponse.Unmarshal(m, b) return xxx_messageInfo_ChangePasswordResponse.Unmarshal(m, b)
@ -554,7 +557,7 @@ func (m *Utxo) Reset() { *m = Utxo{} }
func (m *Utxo) String() string { return proto.CompactTextString(m) } func (m *Utxo) String() string { return proto.CompactTextString(m) }
func (*Utxo) ProtoMessage() {} func (*Utxo) ProtoMessage() {}
func (*Utxo) Descriptor() ([]byte, []int) { func (*Utxo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{8} return fileDescriptor_rpc_1d2b968b4af573ab, []int{8}
} }
func (m *Utxo) XXX_Unmarshal(b []byte) error { func (m *Utxo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Utxo.Unmarshal(m, b) return xxx_messageInfo_Utxo.Unmarshal(m, b)
@ -642,7 +645,7 @@ func (m *Transaction) Reset() { *m = Transaction{} }
func (m *Transaction) String() string { return proto.CompactTextString(m) } func (m *Transaction) String() string { return proto.CompactTextString(m) }
func (*Transaction) ProtoMessage() {} func (*Transaction) ProtoMessage() {}
func (*Transaction) Descriptor() ([]byte, []int) { func (*Transaction) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{9} return fileDescriptor_rpc_1d2b968b4af573ab, []int{9}
} }
func (m *Transaction) XXX_Unmarshal(b []byte) error { func (m *Transaction) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Transaction.Unmarshal(m, b) return xxx_messageInfo_Transaction.Unmarshal(m, b)
@ -728,7 +731,7 @@ func (m *GetTransactionsRequest) Reset() { *m = GetTransactionsRequest{}
func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) } func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) }
func (*GetTransactionsRequest) ProtoMessage() {} func (*GetTransactionsRequest) ProtoMessage() {}
func (*GetTransactionsRequest) Descriptor() ([]byte, []int) { func (*GetTransactionsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{10} return fileDescriptor_rpc_1d2b968b4af573ab, []int{10}
} }
func (m *GetTransactionsRequest) XXX_Unmarshal(b []byte) error { func (m *GetTransactionsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetTransactionsRequest.Unmarshal(m, b) return xxx_messageInfo_GetTransactionsRequest.Unmarshal(m, b)
@ -760,7 +763,7 @@ func (m *TransactionDetails) Reset() { *m = TransactionDetails{} }
func (m *TransactionDetails) String() string { return proto.CompactTextString(m) } func (m *TransactionDetails) String() string { return proto.CompactTextString(m) }
func (*TransactionDetails) ProtoMessage() {} func (*TransactionDetails) ProtoMessage() {}
func (*TransactionDetails) Descriptor() ([]byte, []int) { func (*TransactionDetails) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{11} return fileDescriptor_rpc_1d2b968b4af573ab, []int{11}
} }
func (m *TransactionDetails) XXX_Unmarshal(b []byte) error { func (m *TransactionDetails) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TransactionDetails.Unmarshal(m, b) return xxx_messageInfo_TransactionDetails.Unmarshal(m, b)
@ -801,7 +804,7 @@ func (m *FeeLimit) Reset() { *m = FeeLimit{} }
func (m *FeeLimit) String() string { return proto.CompactTextString(m) } func (m *FeeLimit) String() string { return proto.CompactTextString(m) }
func (*FeeLimit) ProtoMessage() {} func (*FeeLimit) ProtoMessage() {}
func (*FeeLimit) Descriptor() ([]byte, []int) { func (*FeeLimit) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{12} return fileDescriptor_rpc_1d2b968b4af573ab, []int{12}
} }
func (m *FeeLimit) XXX_Unmarshal(b []byte) error { func (m *FeeLimit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeLimit.Unmarshal(m, b) return xxx_messageInfo_FeeLimit.Unmarshal(m, b)
@ -961,7 +964,7 @@ func (m *SendRequest) Reset() { *m = SendRequest{} }
func (m *SendRequest) String() string { return proto.CompactTextString(m) } func (m *SendRequest) String() string { return proto.CompactTextString(m) }
func (*SendRequest) ProtoMessage() {} func (*SendRequest) ProtoMessage() {}
func (*SendRequest) Descriptor() ([]byte, []int) { func (*SendRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{13} return fileDescriptor_rpc_1d2b968b4af573ab, []int{13}
} }
func (m *SendRequest) XXX_Unmarshal(b []byte) error { func (m *SendRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendRequest.Unmarshal(m, b) return xxx_messageInfo_SendRequest.Unmarshal(m, b)
@ -1058,7 +1061,7 @@ func (m *SendResponse) Reset() { *m = SendResponse{} }
func (m *SendResponse) String() string { return proto.CompactTextString(m) } func (m *SendResponse) String() string { return proto.CompactTextString(m) }
func (*SendResponse) ProtoMessage() {} func (*SendResponse) ProtoMessage() {}
func (*SendResponse) Descriptor() ([]byte, []int) { func (*SendResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{14} return fileDescriptor_rpc_1d2b968b4af573ab, []int{14}
} }
func (m *SendResponse) XXX_Unmarshal(b []byte) error { func (m *SendResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendResponse.Unmarshal(m, b) return xxx_messageInfo_SendResponse.Unmarshal(m, b)
@ -1128,7 +1131,7 @@ func (m *SendToRouteRequest) Reset() { *m = SendToRouteRequest{} }
func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) } func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) }
func (*SendToRouteRequest) ProtoMessage() {} func (*SendToRouteRequest) ProtoMessage() {}
func (*SendToRouteRequest) Descriptor() ([]byte, []int) { func (*SendToRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{15} return fileDescriptor_rpc_1d2b968b4af573ab, []int{15}
} }
func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error { func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendToRouteRequest.Unmarshal(m, b) return xxx_messageInfo_SendToRouteRequest.Unmarshal(m, b)
@ -1193,7 +1196,7 @@ func (m *ChannelPoint) Reset() { *m = ChannelPoint{} }
func (m *ChannelPoint) String() string { return proto.CompactTextString(m) } func (m *ChannelPoint) String() string { return proto.CompactTextString(m) }
func (*ChannelPoint) ProtoMessage() {} func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) { func (*ChannelPoint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{16} return fileDescriptor_rpc_1d2b968b4af573ab, []int{16}
} }
func (m *ChannelPoint) XXX_Unmarshal(b []byte) error { func (m *ChannelPoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelPoint.Unmarshal(m, b) return xxx_messageInfo_ChannelPoint.Unmarshal(m, b)
@ -1339,7 +1342,7 @@ func (m *OutPoint) Reset() { *m = OutPoint{} }
func (m *OutPoint) String() string { return proto.CompactTextString(m) } func (m *OutPoint) String() string { return proto.CompactTextString(m) }
func (*OutPoint) ProtoMessage() {} func (*OutPoint) ProtoMessage() {}
func (*OutPoint) Descriptor() ([]byte, []int) { func (*OutPoint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{17} return fileDescriptor_rpc_1d2b968b4af573ab, []int{17}
} }
func (m *OutPoint) XXX_Unmarshal(b []byte) error { func (m *OutPoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OutPoint.Unmarshal(m, b) return xxx_messageInfo_OutPoint.Unmarshal(m, b)
@ -1394,7 +1397,7 @@ func (m *LightningAddress) Reset() { *m = LightningAddress{} }
func (m *LightningAddress) String() string { return proto.CompactTextString(m) } func (m *LightningAddress) String() string { return proto.CompactTextString(m) }
func (*LightningAddress) ProtoMessage() {} func (*LightningAddress) ProtoMessage() {}
func (*LightningAddress) Descriptor() ([]byte, []int) { func (*LightningAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{18} return fileDescriptor_rpc_1d2b968b4af573ab, []int{18}
} }
func (m *LightningAddress) XXX_Unmarshal(b []byte) error { func (m *LightningAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LightningAddress.Unmarshal(m, b) return xxx_messageInfo_LightningAddress.Unmarshal(m, b)
@ -1444,7 +1447,7 @@ func (m *SendManyRequest) Reset() { *m = SendManyRequest{} }
func (m *SendManyRequest) String() string { return proto.CompactTextString(m) } func (m *SendManyRequest) String() string { return proto.CompactTextString(m) }
func (*SendManyRequest) ProtoMessage() {} func (*SendManyRequest) ProtoMessage() {}
func (*SendManyRequest) Descriptor() ([]byte, []int) { func (*SendManyRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{19} return fileDescriptor_rpc_1d2b968b4af573ab, []int{19}
} }
func (m *SendManyRequest) XXX_Unmarshal(b []byte) error { func (m *SendManyRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendManyRequest.Unmarshal(m, b) return xxx_messageInfo_SendManyRequest.Unmarshal(m, b)
@ -1497,7 +1500,7 @@ func (m *SendManyResponse) Reset() { *m = SendManyResponse{} }
func (m *SendManyResponse) String() string { return proto.CompactTextString(m) } func (m *SendManyResponse) String() string { return proto.CompactTextString(m) }
func (*SendManyResponse) ProtoMessage() {} func (*SendManyResponse) ProtoMessage() {}
func (*SendManyResponse) Descriptor() ([]byte, []int) { func (*SendManyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{20} return fileDescriptor_rpc_1d2b968b4af573ab, []int{20}
} }
func (m *SendManyResponse) XXX_Unmarshal(b []byte) error { func (m *SendManyResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendManyResponse.Unmarshal(m, b) return xxx_messageInfo_SendManyResponse.Unmarshal(m, b)
@ -1547,7 +1550,7 @@ func (m *SendCoinsRequest) Reset() { *m = SendCoinsRequest{} }
func (m *SendCoinsRequest) String() string { return proto.CompactTextString(m) } func (m *SendCoinsRequest) String() string { return proto.CompactTextString(m) }
func (*SendCoinsRequest) ProtoMessage() {} func (*SendCoinsRequest) ProtoMessage() {}
func (*SendCoinsRequest) Descriptor() ([]byte, []int) { func (*SendCoinsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{21} return fileDescriptor_rpc_1d2b968b4af573ab, []int{21}
} }
func (m *SendCoinsRequest) XXX_Unmarshal(b []byte) error { func (m *SendCoinsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendCoinsRequest.Unmarshal(m, b) return xxx_messageInfo_SendCoinsRequest.Unmarshal(m, b)
@ -1614,7 +1617,7 @@ func (m *SendCoinsResponse) Reset() { *m = SendCoinsResponse{} }
func (m *SendCoinsResponse) String() string { return proto.CompactTextString(m) } func (m *SendCoinsResponse) String() string { return proto.CompactTextString(m) }
func (*SendCoinsResponse) ProtoMessage() {} func (*SendCoinsResponse) ProtoMessage() {}
func (*SendCoinsResponse) Descriptor() ([]byte, []int) { func (*SendCoinsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{22} return fileDescriptor_rpc_1d2b968b4af573ab, []int{22}
} }
func (m *SendCoinsResponse) XXX_Unmarshal(b []byte) error { func (m *SendCoinsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendCoinsResponse.Unmarshal(m, b) return xxx_messageInfo_SendCoinsResponse.Unmarshal(m, b)
@ -1655,7 +1658,7 @@ func (m *ListUnspentRequest) Reset() { *m = ListUnspentRequest{} }
func (m *ListUnspentRequest) String() string { return proto.CompactTextString(m) } func (m *ListUnspentRequest) String() string { return proto.CompactTextString(m) }
func (*ListUnspentRequest) ProtoMessage() {} func (*ListUnspentRequest) ProtoMessage() {}
func (*ListUnspentRequest) Descriptor() ([]byte, []int) { func (*ListUnspentRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{23} return fileDescriptor_rpc_1d2b968b4af573ab, []int{23}
} }
func (m *ListUnspentRequest) XXX_Unmarshal(b []byte) error { func (m *ListUnspentRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUnspentRequest.Unmarshal(m, b) return xxx_messageInfo_ListUnspentRequest.Unmarshal(m, b)
@ -1701,7 +1704,7 @@ func (m *ListUnspentResponse) Reset() { *m = ListUnspentResponse{} }
func (m *ListUnspentResponse) String() string { return proto.CompactTextString(m) } func (m *ListUnspentResponse) String() string { return proto.CompactTextString(m) }
func (*ListUnspentResponse) ProtoMessage() {} func (*ListUnspentResponse) ProtoMessage() {}
func (*ListUnspentResponse) Descriptor() ([]byte, []int) { func (*ListUnspentResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{24} return fileDescriptor_rpc_1d2b968b4af573ab, []int{24}
} }
func (m *ListUnspentResponse) XXX_Unmarshal(b []byte) error { func (m *ListUnspentResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUnspentResponse.Unmarshal(m, b) return xxx_messageInfo_ListUnspentResponse.Unmarshal(m, b)
@ -1740,7 +1743,7 @@ func (m *NewAddressRequest) Reset() { *m = NewAddressRequest{} }
func (m *NewAddressRequest) String() string { return proto.CompactTextString(m) } func (m *NewAddressRequest) String() string { return proto.CompactTextString(m) }
func (*NewAddressRequest) ProtoMessage() {} func (*NewAddressRequest) ProtoMessage() {}
func (*NewAddressRequest) Descriptor() ([]byte, []int) { func (*NewAddressRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{25} return fileDescriptor_rpc_1d2b968b4af573ab, []int{25}
} }
func (m *NewAddressRequest) XXX_Unmarshal(b []byte) error { func (m *NewAddressRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewAddressRequest.Unmarshal(m, b) return xxx_messageInfo_NewAddressRequest.Unmarshal(m, b)
@ -1779,7 +1782,7 @@ func (m *NewAddressResponse) Reset() { *m = NewAddressResponse{} }
func (m *NewAddressResponse) String() string { return proto.CompactTextString(m) } func (m *NewAddressResponse) String() string { return proto.CompactTextString(m) }
func (*NewAddressResponse) ProtoMessage() {} func (*NewAddressResponse) ProtoMessage() {}
func (*NewAddressResponse) Descriptor() ([]byte, []int) { func (*NewAddressResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{26} return fileDescriptor_rpc_1d2b968b4af573ab, []int{26}
} }
func (m *NewAddressResponse) XXX_Unmarshal(b []byte) error { func (m *NewAddressResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewAddressResponse.Unmarshal(m, b) return xxx_messageInfo_NewAddressResponse.Unmarshal(m, b)
@ -1818,7 +1821,7 @@ func (m *SignMessageRequest) Reset() { *m = SignMessageRequest{} }
func (m *SignMessageRequest) String() string { return proto.CompactTextString(m) } func (m *SignMessageRequest) String() string { return proto.CompactTextString(m) }
func (*SignMessageRequest) ProtoMessage() {} func (*SignMessageRequest) ProtoMessage() {}
func (*SignMessageRequest) Descriptor() ([]byte, []int) { func (*SignMessageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{27} return fileDescriptor_rpc_1d2b968b4af573ab, []int{27}
} }
func (m *SignMessageRequest) XXX_Unmarshal(b []byte) error { func (m *SignMessageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignMessageRequest.Unmarshal(m, b) return xxx_messageInfo_SignMessageRequest.Unmarshal(m, b)
@ -1857,7 +1860,7 @@ func (m *SignMessageResponse) Reset() { *m = SignMessageResponse{} }
func (m *SignMessageResponse) String() string { return proto.CompactTextString(m) } func (m *SignMessageResponse) String() string { return proto.CompactTextString(m) }
func (*SignMessageResponse) ProtoMessage() {} func (*SignMessageResponse) ProtoMessage() {}
func (*SignMessageResponse) Descriptor() ([]byte, []int) { func (*SignMessageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{28} return fileDescriptor_rpc_1d2b968b4af573ab, []int{28}
} }
func (m *SignMessageResponse) XXX_Unmarshal(b []byte) error { func (m *SignMessageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignMessageResponse.Unmarshal(m, b) return xxx_messageInfo_SignMessageResponse.Unmarshal(m, b)
@ -1898,7 +1901,7 @@ func (m *VerifyMessageRequest) Reset() { *m = VerifyMessageRequest{} }
func (m *VerifyMessageRequest) String() string { return proto.CompactTextString(m) } func (m *VerifyMessageRequest) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageRequest) ProtoMessage() {} func (*VerifyMessageRequest) ProtoMessage() {}
func (*VerifyMessageRequest) Descriptor() ([]byte, []int) { func (*VerifyMessageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{29} return fileDescriptor_rpc_1d2b968b4af573ab, []int{29}
} }
func (m *VerifyMessageRequest) XXX_Unmarshal(b []byte) error { func (m *VerifyMessageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VerifyMessageRequest.Unmarshal(m, b) return xxx_messageInfo_VerifyMessageRequest.Unmarshal(m, b)
@ -1946,7 +1949,7 @@ func (m *VerifyMessageResponse) Reset() { *m = VerifyMessageResponse{} }
func (m *VerifyMessageResponse) String() string { return proto.CompactTextString(m) } func (m *VerifyMessageResponse) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageResponse) ProtoMessage() {} func (*VerifyMessageResponse) ProtoMessage() {}
func (*VerifyMessageResponse) Descriptor() ([]byte, []int) { func (*VerifyMessageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{30} return fileDescriptor_rpc_1d2b968b4af573ab, []int{30}
} }
func (m *VerifyMessageResponse) XXX_Unmarshal(b []byte) error { func (m *VerifyMessageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VerifyMessageResponse.Unmarshal(m, b) return xxx_messageInfo_VerifyMessageResponse.Unmarshal(m, b)
@ -1995,7 +1998,7 @@ func (m *ConnectPeerRequest) Reset() { *m = ConnectPeerRequest{} }
func (m *ConnectPeerRequest) String() string { return proto.CompactTextString(m) } func (m *ConnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerRequest) ProtoMessage() {} func (*ConnectPeerRequest) ProtoMessage() {}
func (*ConnectPeerRequest) Descriptor() ([]byte, []int) { func (*ConnectPeerRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{31} return fileDescriptor_rpc_1d2b968b4af573ab, []int{31}
} }
func (m *ConnectPeerRequest) XXX_Unmarshal(b []byte) error { func (m *ConnectPeerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectPeerRequest.Unmarshal(m, b) return xxx_messageInfo_ConnectPeerRequest.Unmarshal(m, b)
@ -2039,7 +2042,7 @@ func (m *ConnectPeerResponse) Reset() { *m = ConnectPeerResponse{} }
func (m *ConnectPeerResponse) String() string { return proto.CompactTextString(m) } func (m *ConnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerResponse) ProtoMessage() {} func (*ConnectPeerResponse) ProtoMessage() {}
func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { func (*ConnectPeerResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{32} return fileDescriptor_rpc_1d2b968b4af573ab, []int{32}
} }
func (m *ConnectPeerResponse) XXX_Unmarshal(b []byte) error { func (m *ConnectPeerResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectPeerResponse.Unmarshal(m, b) return xxx_messageInfo_ConnectPeerResponse.Unmarshal(m, b)
@ -2071,7 +2074,7 @@ func (m *DisconnectPeerRequest) Reset() { *m = DisconnectPeerRequest{} }
func (m *DisconnectPeerRequest) String() string { return proto.CompactTextString(m) } func (m *DisconnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerRequest) ProtoMessage() {} func (*DisconnectPeerRequest) ProtoMessage() {}
func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) { func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{33} return fileDescriptor_rpc_1d2b968b4af573ab, []int{33}
} }
func (m *DisconnectPeerRequest) XXX_Unmarshal(b []byte) error { func (m *DisconnectPeerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectPeerRequest.Unmarshal(m, b) return xxx_messageInfo_DisconnectPeerRequest.Unmarshal(m, b)
@ -2108,7 +2111,7 @@ func (m *DisconnectPeerResponse) Reset() { *m = DisconnectPeerResponse{}
func (m *DisconnectPeerResponse) String() string { return proto.CompactTextString(m) } func (m *DisconnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerResponse) ProtoMessage() {} func (*DisconnectPeerResponse) ProtoMessage() {}
func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) { func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{34} return fileDescriptor_rpc_1d2b968b4af573ab, []int{34}
} }
func (m *DisconnectPeerResponse) XXX_Unmarshal(b []byte) error { func (m *DisconnectPeerResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectPeerResponse.Unmarshal(m, b) return xxx_messageInfo_DisconnectPeerResponse.Unmarshal(m, b)
@ -2142,7 +2145,7 @@ func (m *HTLC) Reset() { *m = HTLC{} }
func (m *HTLC) String() string { return proto.CompactTextString(m) } func (m *HTLC) String() string { return proto.CompactTextString(m) }
func (*HTLC) ProtoMessage() {} func (*HTLC) ProtoMessage() {}
func (*HTLC) Descriptor() ([]byte, []int) { func (*HTLC) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{35} return fileDescriptor_rpc_1d2b968b4af573ab, []int{35}
} }
func (m *HTLC) XXX_Unmarshal(b []byte) error { func (m *HTLC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HTLC.Unmarshal(m, b) return xxx_messageInfo_HTLC.Unmarshal(m, b)
@ -2256,7 +2259,7 @@ func (m *Channel) Reset() { *m = Channel{} }
func (m *Channel) String() string { return proto.CompactTextString(m) } func (m *Channel) String() string { return proto.CompactTextString(m) }
func (*Channel) ProtoMessage() {} func (*Channel) ProtoMessage() {}
func (*Channel) Descriptor() ([]byte, []int) { func (*Channel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{36} return fileDescriptor_rpc_1d2b968b4af573ab, []int{36}
} }
func (m *Channel) XXX_Unmarshal(b []byte) error { func (m *Channel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Channel.Unmarshal(m, b) return xxx_messageInfo_Channel.Unmarshal(m, b)
@ -2416,7 +2419,7 @@ func (m *ListChannelsRequest) Reset() { *m = ListChannelsRequest{} }
func (m *ListChannelsRequest) String() string { return proto.CompactTextString(m) } func (m *ListChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*ListChannelsRequest) ProtoMessage() {} func (*ListChannelsRequest) ProtoMessage() {}
func (*ListChannelsRequest) Descriptor() ([]byte, []int) { func (*ListChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{37} return fileDescriptor_rpc_1d2b968b4af573ab, []int{37}
} }
func (m *ListChannelsRequest) XXX_Unmarshal(b []byte) error { func (m *ListChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListChannelsRequest.Unmarshal(m, b) return xxx_messageInfo_ListChannelsRequest.Unmarshal(m, b)
@ -2476,7 +2479,7 @@ func (m *ListChannelsResponse) Reset() { *m = ListChannelsResponse{} }
func (m *ListChannelsResponse) String() string { return proto.CompactTextString(m) } func (m *ListChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*ListChannelsResponse) ProtoMessage() {} func (*ListChannelsResponse) ProtoMessage() {}
func (*ListChannelsResponse) Descriptor() ([]byte, []int) { func (*ListChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{38} return fileDescriptor_rpc_1d2b968b4af573ab, []int{38}
} }
func (m *ListChannelsResponse) XXX_Unmarshal(b []byte) error { func (m *ListChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListChannelsResponse.Unmarshal(m, b) return xxx_messageInfo_ListChannelsResponse.Unmarshal(m, b)
@ -2533,7 +2536,7 @@ func (m *ChannelCloseSummary) Reset() { *m = ChannelCloseSummary{} }
func (m *ChannelCloseSummary) String() string { return proto.CompactTextString(m) } func (m *ChannelCloseSummary) String() string { return proto.CompactTextString(m) }
func (*ChannelCloseSummary) ProtoMessage() {} func (*ChannelCloseSummary) ProtoMessage() {}
func (*ChannelCloseSummary) Descriptor() ([]byte, []int) { func (*ChannelCloseSummary) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{39} return fileDescriptor_rpc_1d2b968b4af573ab, []int{39}
} }
func (m *ChannelCloseSummary) XXX_Unmarshal(b []byte) error { func (m *ChannelCloseSummary) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelCloseSummary.Unmarshal(m, b) return xxx_messageInfo_ChannelCloseSummary.Unmarshal(m, b)
@ -2639,7 +2642,7 @@ func (m *ClosedChannelsRequest) Reset() { *m = ClosedChannelsRequest{} }
func (m *ClosedChannelsRequest) String() string { return proto.CompactTextString(m) } func (m *ClosedChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelsRequest) ProtoMessage() {} func (*ClosedChannelsRequest) ProtoMessage() {}
func (*ClosedChannelsRequest) Descriptor() ([]byte, []int) { func (*ClosedChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{40} return fileDescriptor_rpc_1d2b968b4af573ab, []int{40}
} }
func (m *ClosedChannelsRequest) XXX_Unmarshal(b []byte) error { func (m *ClosedChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelsRequest.Unmarshal(m, b) return xxx_messageInfo_ClosedChannelsRequest.Unmarshal(m, b)
@ -2712,7 +2715,7 @@ func (m *ClosedChannelsResponse) Reset() { *m = ClosedChannelsResponse{}
func (m *ClosedChannelsResponse) String() string { return proto.CompactTextString(m) } func (m *ClosedChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelsResponse) ProtoMessage() {} func (*ClosedChannelsResponse) ProtoMessage() {}
func (*ClosedChannelsResponse) Descriptor() ([]byte, []int) { func (*ClosedChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{41} return fileDescriptor_rpc_1d2b968b4af573ab, []int{41}
} }
func (m *ClosedChannelsResponse) XXX_Unmarshal(b []byte) error { func (m *ClosedChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelsResponse.Unmarshal(m, b) return xxx_messageInfo_ClosedChannelsResponse.Unmarshal(m, b)
@ -2765,7 +2768,7 @@ func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) } func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {} func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) { func (*Peer) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{42} return fileDescriptor_rpc_1d2b968b4af573ab, []int{42}
} }
func (m *Peer) XXX_Unmarshal(b []byte) error { func (m *Peer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Peer.Unmarshal(m, b) return xxx_messageInfo_Peer.Unmarshal(m, b)
@ -2851,7 +2854,7 @@ func (m *ListPeersRequest) Reset() { *m = ListPeersRequest{} }
func (m *ListPeersRequest) String() string { return proto.CompactTextString(m) } func (m *ListPeersRequest) String() string { return proto.CompactTextString(m) }
func (*ListPeersRequest) ProtoMessage() {} func (*ListPeersRequest) ProtoMessage() {}
func (*ListPeersRequest) Descriptor() ([]byte, []int) { func (*ListPeersRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{43} return fileDescriptor_rpc_1d2b968b4af573ab, []int{43}
} }
func (m *ListPeersRequest) XXX_Unmarshal(b []byte) error { func (m *ListPeersRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPeersRequest.Unmarshal(m, b) return xxx_messageInfo_ListPeersRequest.Unmarshal(m, b)
@ -2883,7 +2886,7 @@ func (m *ListPeersResponse) Reset() { *m = ListPeersResponse{} }
func (m *ListPeersResponse) String() string { return proto.CompactTextString(m) } func (m *ListPeersResponse) String() string { return proto.CompactTextString(m) }
func (*ListPeersResponse) ProtoMessage() {} func (*ListPeersResponse) ProtoMessage() {}
func (*ListPeersResponse) Descriptor() ([]byte, []int) { func (*ListPeersResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{44} return fileDescriptor_rpc_1d2b968b4af573ab, []int{44}
} }
func (m *ListPeersResponse) XXX_Unmarshal(b []byte) error { func (m *ListPeersResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPeersResponse.Unmarshal(m, b) return xxx_messageInfo_ListPeersResponse.Unmarshal(m, b)
@ -2920,7 +2923,7 @@ func (m *GetInfoRequest) Reset() { *m = GetInfoRequest{} }
func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) } func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) }
func (*GetInfoRequest) ProtoMessage() {} func (*GetInfoRequest) ProtoMessage() {}
func (*GetInfoRequest) Descriptor() ([]byte, []int) { func (*GetInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{45} return fileDescriptor_rpc_1d2b968b4af573ab, []int{45}
} }
func (m *GetInfoRequest) XXX_Unmarshal(b []byte) error { func (m *GetInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetInfoRequest.Unmarshal(m, b) return xxx_messageInfo_GetInfoRequest.Unmarshal(m, b)
@ -2980,7 +2983,7 @@ func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} }
func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) } func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) }
func (*GetInfoResponse) ProtoMessage() {} func (*GetInfoResponse) ProtoMessage() {}
func (*GetInfoResponse) Descriptor() ([]byte, []int) { func (*GetInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{46} return fileDescriptor_rpc_1d2b968b4af573ab, []int{46}
} }
func (m *GetInfoResponse) XXX_Unmarshal(b []byte) error { func (m *GetInfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetInfoResponse.Unmarshal(m, b) return xxx_messageInfo_GetInfoResponse.Unmarshal(m, b)
@ -3113,7 +3116,7 @@ func (m *Chain) Reset() { *m = Chain{} }
func (m *Chain) String() string { return proto.CompactTextString(m) } func (m *Chain) String() string { return proto.CompactTextString(m) }
func (*Chain) ProtoMessage() {} func (*Chain) ProtoMessage() {}
func (*Chain) Descriptor() ([]byte, []int) { func (*Chain) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{47} return fileDescriptor_rpc_1d2b968b4af573ab, []int{47}
} }
func (m *Chain) XXX_Unmarshal(b []byte) error { func (m *Chain) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Chain.Unmarshal(m, b) return xxx_messageInfo_Chain.Unmarshal(m, b)
@ -3160,7 +3163,7 @@ func (m *ConfirmationUpdate) Reset() { *m = ConfirmationUpdate{} }
func (m *ConfirmationUpdate) String() string { return proto.CompactTextString(m) } func (m *ConfirmationUpdate) String() string { return proto.CompactTextString(m) }
func (*ConfirmationUpdate) ProtoMessage() {} func (*ConfirmationUpdate) ProtoMessage() {}
func (*ConfirmationUpdate) Descriptor() ([]byte, []int) { func (*ConfirmationUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{48} return fileDescriptor_rpc_1d2b968b4af573ab, []int{48}
} }
func (m *ConfirmationUpdate) XXX_Unmarshal(b []byte) error { func (m *ConfirmationUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfirmationUpdate.Unmarshal(m, b) return xxx_messageInfo_ConfirmationUpdate.Unmarshal(m, b)
@ -3212,7 +3215,7 @@ func (m *ChannelOpenUpdate) Reset() { *m = ChannelOpenUpdate{} }
func (m *ChannelOpenUpdate) String() string { return proto.CompactTextString(m) } func (m *ChannelOpenUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelOpenUpdate) ProtoMessage() {} func (*ChannelOpenUpdate) ProtoMessage() {}
func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) { func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{49} return fileDescriptor_rpc_1d2b968b4af573ab, []int{49}
} }
func (m *ChannelOpenUpdate) XXX_Unmarshal(b []byte) error { func (m *ChannelOpenUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelOpenUpdate.Unmarshal(m, b) return xxx_messageInfo_ChannelOpenUpdate.Unmarshal(m, b)
@ -3251,7 +3254,7 @@ func (m *ChannelCloseUpdate) Reset() { *m = ChannelCloseUpdate{} }
func (m *ChannelCloseUpdate) String() string { return proto.CompactTextString(m) } func (m *ChannelCloseUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelCloseUpdate) ProtoMessage() {} func (*ChannelCloseUpdate) ProtoMessage() {}
func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{50} return fileDescriptor_rpc_1d2b968b4af573ab, []int{50}
} }
func (m *ChannelCloseUpdate) XXX_Unmarshal(b []byte) error { func (m *ChannelCloseUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelCloseUpdate.Unmarshal(m, b) return xxx_messageInfo_ChannelCloseUpdate.Unmarshal(m, b)
@ -3306,7 +3309,7 @@ func (m *CloseChannelRequest) Reset() { *m = CloseChannelRequest{} }
func (m *CloseChannelRequest) String() string { return proto.CompactTextString(m) } func (m *CloseChannelRequest) String() string { return proto.CompactTextString(m) }
func (*CloseChannelRequest) ProtoMessage() {} func (*CloseChannelRequest) ProtoMessage() {}
func (*CloseChannelRequest) Descriptor() ([]byte, []int) { func (*CloseChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{51} return fileDescriptor_rpc_1d2b968b4af573ab, []int{51}
} }
func (m *CloseChannelRequest) XXX_Unmarshal(b []byte) error { func (m *CloseChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseChannelRequest.Unmarshal(m, b) return xxx_messageInfo_CloseChannelRequest.Unmarshal(m, b)
@ -3368,7 +3371,7 @@ func (m *CloseStatusUpdate) Reset() { *m = CloseStatusUpdate{} }
func (m *CloseStatusUpdate) String() string { return proto.CompactTextString(m) } func (m *CloseStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*CloseStatusUpdate) ProtoMessage() {} func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { func (*CloseStatusUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{52} return fileDescriptor_rpc_1d2b968b4af573ab, []int{52}
} }
func (m *CloseStatusUpdate) XXX_Unmarshal(b []byte) error { func (m *CloseStatusUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseStatusUpdate.Unmarshal(m, b) return xxx_messageInfo_CloseStatusUpdate.Unmarshal(m, b)
@ -3511,7 +3514,7 @@ func (m *PendingUpdate) Reset() { *m = PendingUpdate{} }
func (m *PendingUpdate) String() string { return proto.CompactTextString(m) } func (m *PendingUpdate) String() string { return proto.CompactTextString(m) }
func (*PendingUpdate) ProtoMessage() {} func (*PendingUpdate) ProtoMessage() {}
func (*PendingUpdate) Descriptor() ([]byte, []int) { func (*PendingUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{53} return fileDescriptor_rpc_1d2b968b4af573ab, []int{53}
} }
func (m *PendingUpdate) XXX_Unmarshal(b []byte) error { func (m *PendingUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingUpdate.Unmarshal(m, b) return xxx_messageInfo_PendingUpdate.Unmarshal(m, b)
@ -3577,7 +3580,7 @@ func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} }
func (m *OpenChannelRequest) String() string { return proto.CompactTextString(m) } func (m *OpenChannelRequest) String() string { return proto.CompactTextString(m) }
func (*OpenChannelRequest) ProtoMessage() {} func (*OpenChannelRequest) ProtoMessage() {}
func (*OpenChannelRequest) Descriptor() ([]byte, []int) { func (*OpenChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{54} return fileDescriptor_rpc_1d2b968b4af573ab, []int{54}
} }
func (m *OpenChannelRequest) XXX_Unmarshal(b []byte) error { func (m *OpenChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenChannelRequest.Unmarshal(m, b) return xxx_messageInfo_OpenChannelRequest.Unmarshal(m, b)
@ -3688,7 +3691,7 @@ func (m *OpenStatusUpdate) Reset() { *m = OpenStatusUpdate{} }
func (m *OpenStatusUpdate) String() string { return proto.CompactTextString(m) } func (m *OpenStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*OpenStatusUpdate) ProtoMessage() {} func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { func (*OpenStatusUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{55} return fileDescriptor_rpc_1d2b968b4af573ab, []int{55}
} }
func (m *OpenStatusUpdate) XXX_Unmarshal(b []byte) error { func (m *OpenStatusUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenStatusUpdate.Unmarshal(m, b) return xxx_messageInfo_OpenStatusUpdate.Unmarshal(m, b)
@ -3844,7 +3847,7 @@ func (m *PendingHTLC) Reset() { *m = PendingHTLC{} }
func (m *PendingHTLC) String() string { return proto.CompactTextString(m) } func (m *PendingHTLC) String() string { return proto.CompactTextString(m) }
func (*PendingHTLC) ProtoMessage() {} func (*PendingHTLC) ProtoMessage() {}
func (*PendingHTLC) Descriptor() ([]byte, []int) { func (*PendingHTLC) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{56} return fileDescriptor_rpc_1d2b968b4af573ab, []int{56}
} }
func (m *PendingHTLC) XXX_Unmarshal(b []byte) error { func (m *PendingHTLC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingHTLC.Unmarshal(m, b) return xxx_messageInfo_PendingHTLC.Unmarshal(m, b)
@ -3916,7 +3919,7 @@ func (m *PendingChannelsRequest) Reset() { *m = PendingChannelsRequest{}
func (m *PendingChannelsRequest) String() string { return proto.CompactTextString(m) } func (m *PendingChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsRequest) ProtoMessage() {} func (*PendingChannelsRequest) ProtoMessage() {}
func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { func (*PendingChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{57} return fileDescriptor_rpc_1d2b968b4af573ab, []int{57}
} }
func (m *PendingChannelsRequest) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsRequest.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsRequest.Unmarshal(m, b)
@ -3956,7 +3959,7 @@ func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse
func (m *PendingChannelsResponse) String() string { return proto.CompactTextString(m) } func (m *PendingChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse) ProtoMessage() {} func (*PendingChannelsResponse) ProtoMessage() {}
func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58}
} }
func (m *PendingChannelsResponse) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse.Unmarshal(m, b)
@ -4028,7 +4031,7 @@ func (m *PendingChannelsResponse_PendingChannel) Reset() {
func (m *PendingChannelsResponse_PendingChannel) String() string { return proto.CompactTextString(m) } func (m *PendingChannelsResponse_PendingChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {} func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58, 0} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58, 0}
} }
func (m *PendingChannelsResponse_PendingChannel) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse_PendingChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_PendingChannel.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse_PendingChannel.Unmarshal(m, b)
@ -4115,7 +4118,7 @@ func (m *PendingChannelsResponse_PendingOpenChannel) String() string {
} }
func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {} func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58, 1} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58, 1}
} }
func (m *PendingChannelsResponse_PendingOpenChannel) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse_PendingOpenChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_PendingOpenChannel.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse_PendingOpenChannel.Unmarshal(m, b)
@ -4188,7 +4191,7 @@ func (m *PendingChannelsResponse_WaitingCloseChannel) String() string {
} }
func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {} func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {}
func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58, 2} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58, 2}
} }
func (m *PendingChannelsResponse_WaitingCloseChannel) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse_WaitingCloseChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_WaitingCloseChannel.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse_WaitingCloseChannel.Unmarshal(m, b)
@ -4236,7 +4239,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) } func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58, 3} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58, 3}
} }
func (m *PendingChannelsResponse_ClosedChannel) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse_ClosedChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_ClosedChannel.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse_ClosedChannel.Unmarshal(m, b)
@ -4300,7 +4303,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string {
} }
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{58, 4} return fileDescriptor_rpc_1d2b968b4af573ab, []int{58, 4}
} }
func (m *PendingChannelsResponse_ForceClosedChannel) XXX_Unmarshal(b []byte) error { func (m *PendingChannelsResponse_ForceClosedChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_ForceClosedChannel.Unmarshal(m, b) return xxx_messageInfo_PendingChannelsResponse_ForceClosedChannel.Unmarshal(m, b)
@ -4379,7 +4382,7 @@ func (m *ChannelEventSubscription) Reset() { *m = ChannelEventSubscripti
func (m *ChannelEventSubscription) String() string { return proto.CompactTextString(m) } func (m *ChannelEventSubscription) String() string { return proto.CompactTextString(m) }
func (*ChannelEventSubscription) ProtoMessage() {} func (*ChannelEventSubscription) ProtoMessage() {}
func (*ChannelEventSubscription) Descriptor() ([]byte, []int) { func (*ChannelEventSubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{59} return fileDescriptor_rpc_1d2b968b4af573ab, []int{59}
} }
func (m *ChannelEventSubscription) XXX_Unmarshal(b []byte) error { func (m *ChannelEventSubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEventSubscription.Unmarshal(m, b) return xxx_messageInfo_ChannelEventSubscription.Unmarshal(m, b)
@ -4416,7 +4419,7 @@ func (m *ChannelEventUpdate) Reset() { *m = ChannelEventUpdate{} }
func (m *ChannelEventUpdate) String() string { return proto.CompactTextString(m) } func (m *ChannelEventUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelEventUpdate) ProtoMessage() {} func (*ChannelEventUpdate) ProtoMessage() {}
func (*ChannelEventUpdate) Descriptor() ([]byte, []int) { func (*ChannelEventUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{60} return fileDescriptor_rpc_1d2b968b4af573ab, []int{60}
} }
func (m *ChannelEventUpdate) XXX_Unmarshal(b []byte) error { func (m *ChannelEventUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEventUpdate.Unmarshal(m, b) return xxx_messageInfo_ChannelEventUpdate.Unmarshal(m, b)
@ -4628,7 +4631,7 @@ func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} }
func (m *WalletBalanceRequest) String() string { return proto.CompactTextString(m) } func (m *WalletBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceRequest) ProtoMessage() {} func (*WalletBalanceRequest) ProtoMessage() {}
func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { func (*WalletBalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{61} return fileDescriptor_rpc_1d2b968b4af573ab, []int{61}
} }
func (m *WalletBalanceRequest) XXX_Unmarshal(b []byte) error { func (m *WalletBalanceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WalletBalanceRequest.Unmarshal(m, b) return xxx_messageInfo_WalletBalanceRequest.Unmarshal(m, b)
@ -4664,7 +4667,7 @@ func (m *WalletBalanceResponse) Reset() { *m = WalletBalanceResponse{} }
func (m *WalletBalanceResponse) String() string { return proto.CompactTextString(m) } func (m *WalletBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceResponse) ProtoMessage() {} func (*WalletBalanceResponse) ProtoMessage() {}
func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { func (*WalletBalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{62} return fileDescriptor_rpc_1d2b968b4af573ab, []int{62}
} }
func (m *WalletBalanceResponse) XXX_Unmarshal(b []byte) error { func (m *WalletBalanceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WalletBalanceResponse.Unmarshal(m, b) return xxx_messageInfo_WalletBalanceResponse.Unmarshal(m, b)
@ -4715,7 +4718,7 @@ func (m *ChannelBalanceRequest) Reset() { *m = ChannelBalanceRequest{} }
func (m *ChannelBalanceRequest) String() string { return proto.CompactTextString(m) } func (m *ChannelBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceRequest) ProtoMessage() {} func (*ChannelBalanceRequest) ProtoMessage() {}
func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{63} return fileDescriptor_rpc_1d2b968b4af573ab, []int{63}
} }
func (m *ChannelBalanceRequest) XXX_Unmarshal(b []byte) error { func (m *ChannelBalanceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBalanceRequest.Unmarshal(m, b) return xxx_messageInfo_ChannelBalanceRequest.Unmarshal(m, b)
@ -4749,7 +4752,7 @@ func (m *ChannelBalanceResponse) Reset() { *m = ChannelBalanceResponse{}
func (m *ChannelBalanceResponse) String() string { return proto.CompactTextString(m) } func (m *ChannelBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceResponse) ProtoMessage() {} func (*ChannelBalanceResponse) ProtoMessage() {}
func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{64} return fileDescriptor_rpc_1d2b968b4af573ab, []int{64}
} }
func (m *ChannelBalanceResponse) XXX_Unmarshal(b []byte) error { func (m *ChannelBalanceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBalanceResponse.Unmarshal(m, b) return xxx_messageInfo_ChannelBalanceResponse.Unmarshal(m, b)
@ -4819,7 +4822,7 @@ func (m *QueryRoutesRequest) Reset() { *m = QueryRoutesRequest{} }
func (m *QueryRoutesRequest) String() string { return proto.CompactTextString(m) } func (m *QueryRoutesRequest) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesRequest) ProtoMessage() {} func (*QueryRoutesRequest) ProtoMessage() {}
func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { func (*QueryRoutesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{65} return fileDescriptor_rpc_1d2b968b4af573ab, []int{65}
} }
func (m *QueryRoutesRequest) XXX_Unmarshal(b []byte) error { func (m *QueryRoutesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryRoutesRequest.Unmarshal(m, b) return xxx_messageInfo_QueryRoutesRequest.Unmarshal(m, b)
@ -4914,7 +4917,7 @@ func (m *EdgeLocator) Reset() { *m = EdgeLocator{} }
func (m *EdgeLocator) String() string { return proto.CompactTextString(m) } func (m *EdgeLocator) String() string { return proto.CompactTextString(m) }
func (*EdgeLocator) ProtoMessage() {} func (*EdgeLocator) ProtoMessage() {}
func (*EdgeLocator) Descriptor() ([]byte, []int) { func (*EdgeLocator) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{66} return fileDescriptor_rpc_1d2b968b4af573ab, []int{66}
} }
func (m *EdgeLocator) XXX_Unmarshal(b []byte) error { func (m *EdgeLocator) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EdgeLocator.Unmarshal(m, b) return xxx_messageInfo_EdgeLocator.Unmarshal(m, b)
@ -4959,7 +4962,7 @@ func (m *QueryRoutesResponse) Reset() { *m = QueryRoutesResponse{} }
func (m *QueryRoutesResponse) String() string { return proto.CompactTextString(m) } func (m *QueryRoutesResponse) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesResponse) ProtoMessage() {} func (*QueryRoutesResponse) ProtoMessage() {}
func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { func (*QueryRoutesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{67} return fileDescriptor_rpc_1d2b968b4af573ab, []int{67}
} }
func (m *QueryRoutesResponse) XXX_Unmarshal(b []byte) error { func (m *QueryRoutesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryRoutesResponse.Unmarshal(m, b) return xxx_messageInfo_QueryRoutesResponse.Unmarshal(m, b)
@ -5011,7 +5014,7 @@ func (m *Hop) Reset() { *m = Hop{} }
func (m *Hop) String() string { return proto.CompactTextString(m) } func (m *Hop) String() string { return proto.CompactTextString(m) }
func (*Hop) ProtoMessage() {} func (*Hop) ProtoMessage() {}
func (*Hop) Descriptor() ([]byte, []int) { func (*Hop) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{68} return fileDescriptor_rpc_1d2b968b4af573ab, []int{68}
} }
func (m *Hop) XXX_Unmarshal(b []byte) error { func (m *Hop) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Hop.Unmarshal(m, b) return xxx_messageInfo_Hop.Unmarshal(m, b)
@ -5132,7 +5135,7 @@ func (m *Route) Reset() { *m = Route{} }
func (m *Route) String() string { return proto.CompactTextString(m) } func (m *Route) String() string { return proto.CompactTextString(m) }
func (*Route) ProtoMessage() {} func (*Route) ProtoMessage() {}
func (*Route) Descriptor() ([]byte, []int) { func (*Route) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{69} return fileDescriptor_rpc_1d2b968b4af573ab, []int{69}
} }
func (m *Route) XXX_Unmarshal(b []byte) error { func (m *Route) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Route.Unmarshal(m, b) return xxx_messageInfo_Route.Unmarshal(m, b)
@ -5208,7 +5211,7 @@ func (m *NodeInfoRequest) Reset() { *m = NodeInfoRequest{} }
func (m *NodeInfoRequest) String() string { return proto.CompactTextString(m) } func (m *NodeInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NodeInfoRequest) ProtoMessage() {} func (*NodeInfoRequest) ProtoMessage() {}
func (*NodeInfoRequest) Descriptor() ([]byte, []int) { func (*NodeInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{70} return fileDescriptor_rpc_1d2b968b4af573ab, []int{70}
} }
func (m *NodeInfoRequest) XXX_Unmarshal(b []byte) error { func (m *NodeInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeInfoRequest.Unmarshal(m, b) return xxx_messageInfo_NodeInfoRequest.Unmarshal(m, b)
@ -5253,7 +5256,7 @@ func (m *NodeInfo) Reset() { *m = NodeInfo{} }
func (m *NodeInfo) String() string { return proto.CompactTextString(m) } func (m *NodeInfo) String() string { return proto.CompactTextString(m) }
func (*NodeInfo) ProtoMessage() {} func (*NodeInfo) ProtoMessage() {}
func (*NodeInfo) Descriptor() ([]byte, []int) { func (*NodeInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{71} return fileDescriptor_rpc_1d2b968b4af573ab, []int{71}
} }
func (m *NodeInfo) XXX_Unmarshal(b []byte) error { func (m *NodeInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeInfo.Unmarshal(m, b) return xxx_messageInfo_NodeInfo.Unmarshal(m, b)
@ -5314,7 +5317,7 @@ func (m *LightningNode) Reset() { *m = LightningNode{} }
func (m *LightningNode) String() string { return proto.CompactTextString(m) } func (m *LightningNode) String() string { return proto.CompactTextString(m) }
func (*LightningNode) ProtoMessage() {} func (*LightningNode) ProtoMessage() {}
func (*LightningNode) Descriptor() ([]byte, []int) { func (*LightningNode) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{72} return fileDescriptor_rpc_1d2b968b4af573ab, []int{72}
} }
func (m *LightningNode) XXX_Unmarshal(b []byte) error { func (m *LightningNode) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LightningNode.Unmarshal(m, b) return xxx_messageInfo_LightningNode.Unmarshal(m, b)
@ -5381,7 +5384,7 @@ func (m *NodeAddress) Reset() { *m = NodeAddress{} }
func (m *NodeAddress) String() string { return proto.CompactTextString(m) } func (m *NodeAddress) String() string { return proto.CompactTextString(m) }
func (*NodeAddress) ProtoMessage() {} func (*NodeAddress) ProtoMessage() {}
func (*NodeAddress) Descriptor() ([]byte, []int) { func (*NodeAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{73} return fileDescriptor_rpc_1d2b968b4af573ab, []int{73}
} }
func (m *NodeAddress) XXX_Unmarshal(b []byte) error { func (m *NodeAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeAddress.Unmarshal(m, b) return xxx_messageInfo_NodeAddress.Unmarshal(m, b)
@ -5431,7 +5434,7 @@ func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} }
func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) } func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) }
func (*RoutingPolicy) ProtoMessage() {} func (*RoutingPolicy) ProtoMessage() {}
func (*RoutingPolicy) Descriptor() ([]byte, []int) { func (*RoutingPolicy) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{74} return fileDescriptor_rpc_1d2b968b4af573ab, []int{74}
} }
func (m *RoutingPolicy) XXX_Unmarshal(b []byte) error { func (m *RoutingPolicy) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutingPolicy.Unmarshal(m, b) return xxx_messageInfo_RoutingPolicy.Unmarshal(m, b)
@ -5521,7 +5524,7 @@ func (m *ChannelEdge) Reset() { *m = ChannelEdge{} }
func (m *ChannelEdge) String() string { return proto.CompactTextString(m) } func (m *ChannelEdge) String() string { return proto.CompactTextString(m) }
func (*ChannelEdge) ProtoMessage() {} func (*ChannelEdge) ProtoMessage() {}
func (*ChannelEdge) Descriptor() ([]byte, []int) { func (*ChannelEdge) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{75} return fileDescriptor_rpc_1d2b968b4af573ab, []int{75}
} }
func (m *ChannelEdge) XXX_Unmarshal(b []byte) error { func (m *ChannelEdge) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEdge.Unmarshal(m, b) return xxx_messageInfo_ChannelEdge.Unmarshal(m, b)
@ -5612,7 +5615,7 @@ func (m *ChannelGraphRequest) Reset() { *m = ChannelGraphRequest{} }
func (m *ChannelGraphRequest) String() string { return proto.CompactTextString(m) } func (m *ChannelGraphRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelGraphRequest) ProtoMessage() {} func (*ChannelGraphRequest) ProtoMessage() {}
func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { func (*ChannelGraphRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{76} return fileDescriptor_rpc_1d2b968b4af573ab, []int{76}
} }
func (m *ChannelGraphRequest) XXX_Unmarshal(b []byte) error { func (m *ChannelGraphRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelGraphRequest.Unmarshal(m, b) return xxx_messageInfo_ChannelGraphRequest.Unmarshal(m, b)
@ -5654,7 +5657,7 @@ func (m *ChannelGraph) Reset() { *m = ChannelGraph{} }
func (m *ChannelGraph) String() string { return proto.CompactTextString(m) } func (m *ChannelGraph) String() string { return proto.CompactTextString(m) }
func (*ChannelGraph) ProtoMessage() {} func (*ChannelGraph) ProtoMessage() {}
func (*ChannelGraph) Descriptor() ([]byte, []int) { func (*ChannelGraph) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{77} return fileDescriptor_rpc_1d2b968b4af573ab, []int{77}
} }
func (m *ChannelGraph) XXX_Unmarshal(b []byte) error { func (m *ChannelGraph) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelGraph.Unmarshal(m, b) return xxx_messageInfo_ChannelGraph.Unmarshal(m, b)
@ -5703,7 +5706,7 @@ func (m *ChanInfoRequest) Reset() { *m = ChanInfoRequest{} }
func (m *ChanInfoRequest) String() string { return proto.CompactTextString(m) } func (m *ChanInfoRequest) String() string { return proto.CompactTextString(m) }
func (*ChanInfoRequest) ProtoMessage() {} func (*ChanInfoRequest) ProtoMessage() {}
func (*ChanInfoRequest) Descriptor() ([]byte, []int) { func (*ChanInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{78} return fileDescriptor_rpc_1d2b968b4af573ab, []int{78}
} }
func (m *ChanInfoRequest) XXX_Unmarshal(b []byte) error { func (m *ChanInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChanInfoRequest.Unmarshal(m, b) return xxx_messageInfo_ChanInfoRequest.Unmarshal(m, b)
@ -5740,7 +5743,7 @@ func (m *NetworkInfoRequest) Reset() { *m = NetworkInfoRequest{} }
func (m *NetworkInfoRequest) String() string { return proto.CompactTextString(m) } func (m *NetworkInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NetworkInfoRequest) ProtoMessage() {} func (*NetworkInfoRequest) ProtoMessage() {}
func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { func (*NetworkInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{79} return fileDescriptor_rpc_1d2b968b4af573ab, []int{79}
} }
func (m *NetworkInfoRequest) XXX_Unmarshal(b []byte) error { func (m *NetworkInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkInfoRequest.Unmarshal(m, b) return xxx_messageInfo_NetworkInfoRequest.Unmarshal(m, b)
@ -5779,7 +5782,7 @@ func (m *NetworkInfo) Reset() { *m = NetworkInfo{} }
func (m *NetworkInfo) String() string { return proto.CompactTextString(m) } func (m *NetworkInfo) String() string { return proto.CompactTextString(m) }
func (*NetworkInfo) ProtoMessage() {} func (*NetworkInfo) ProtoMessage() {}
func (*NetworkInfo) Descriptor() ([]byte, []int) { func (*NetworkInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{80} return fileDescriptor_rpc_1d2b968b4af573ab, []int{80}
} }
func (m *NetworkInfo) XXX_Unmarshal(b []byte) error { func (m *NetworkInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkInfo.Unmarshal(m, b) return xxx_messageInfo_NetworkInfo.Unmarshal(m, b)
@ -5872,7 +5875,7 @@ func (m *StopRequest) Reset() { *m = StopRequest{} }
func (m *StopRequest) String() string { return proto.CompactTextString(m) } func (m *StopRequest) String() string { return proto.CompactTextString(m) }
func (*StopRequest) ProtoMessage() {} func (*StopRequest) ProtoMessage() {}
func (*StopRequest) Descriptor() ([]byte, []int) { func (*StopRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{81} return fileDescriptor_rpc_1d2b968b4af573ab, []int{81}
} }
func (m *StopRequest) XXX_Unmarshal(b []byte) error { func (m *StopRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopRequest.Unmarshal(m, b) return xxx_messageInfo_StopRequest.Unmarshal(m, b)
@ -5902,7 +5905,7 @@ func (m *StopResponse) Reset() { *m = StopResponse{} }
func (m *StopResponse) String() string { return proto.CompactTextString(m) } func (m *StopResponse) String() string { return proto.CompactTextString(m) }
func (*StopResponse) ProtoMessage() {} func (*StopResponse) ProtoMessage() {}
func (*StopResponse) Descriptor() ([]byte, []int) { func (*StopResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{82} return fileDescriptor_rpc_1d2b968b4af573ab, []int{82}
} }
func (m *StopResponse) XXX_Unmarshal(b []byte) error { func (m *StopResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopResponse.Unmarshal(m, b) return xxx_messageInfo_StopResponse.Unmarshal(m, b)
@ -5932,7 +5935,7 @@ func (m *GraphTopologySubscription) Reset() { *m = GraphTopologySubscrip
func (m *GraphTopologySubscription) String() string { return proto.CompactTextString(m) } func (m *GraphTopologySubscription) String() string { return proto.CompactTextString(m) }
func (*GraphTopologySubscription) ProtoMessage() {} func (*GraphTopologySubscription) ProtoMessage() {}
func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { func (*GraphTopologySubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{83} return fileDescriptor_rpc_1d2b968b4af573ab, []int{83}
} }
func (m *GraphTopologySubscription) XXX_Unmarshal(b []byte) error { func (m *GraphTopologySubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphTopologySubscription.Unmarshal(m, b) return xxx_messageInfo_GraphTopologySubscription.Unmarshal(m, b)
@ -5965,7 +5968,7 @@ func (m *GraphTopologyUpdate) Reset() { *m = GraphTopologyUpdate{} }
func (m *GraphTopologyUpdate) String() string { return proto.CompactTextString(m) } func (m *GraphTopologyUpdate) String() string { return proto.CompactTextString(m) }
func (*GraphTopologyUpdate) ProtoMessage() {} func (*GraphTopologyUpdate) ProtoMessage() {}
func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{84} return fileDescriptor_rpc_1d2b968b4af573ab, []int{84}
} }
func (m *GraphTopologyUpdate) XXX_Unmarshal(b []byte) error { func (m *GraphTopologyUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphTopologyUpdate.Unmarshal(m, b) return xxx_messageInfo_GraphTopologyUpdate.Unmarshal(m, b)
@ -6020,7 +6023,7 @@ func (m *NodeUpdate) Reset() { *m = NodeUpdate{} }
func (m *NodeUpdate) String() string { return proto.CompactTextString(m) } func (m *NodeUpdate) String() string { return proto.CompactTextString(m) }
func (*NodeUpdate) ProtoMessage() {} func (*NodeUpdate) ProtoMessage() {}
func (*NodeUpdate) Descriptor() ([]byte, []int) { func (*NodeUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{85} return fileDescriptor_rpc_1d2b968b4af573ab, []int{85}
} }
func (m *NodeUpdate) XXX_Unmarshal(b []byte) error { func (m *NodeUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeUpdate.Unmarshal(m, b) return xxx_messageInfo_NodeUpdate.Unmarshal(m, b)
@ -6088,7 +6091,7 @@ func (m *ChannelEdgeUpdate) Reset() { *m = ChannelEdgeUpdate{} }
func (m *ChannelEdgeUpdate) String() string { return proto.CompactTextString(m) } func (m *ChannelEdgeUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelEdgeUpdate) ProtoMessage() {} func (*ChannelEdgeUpdate) ProtoMessage() {}
func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{86} return fileDescriptor_rpc_1d2b968b4af573ab, []int{86}
} }
func (m *ChannelEdgeUpdate) XXX_Unmarshal(b []byte) error { func (m *ChannelEdgeUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEdgeUpdate.Unmarshal(m, b) return xxx_messageInfo_ChannelEdgeUpdate.Unmarshal(m, b)
@ -6168,7 +6171,7 @@ func (m *ClosedChannelUpdate) Reset() { *m = ClosedChannelUpdate{} }
func (m *ClosedChannelUpdate) String() string { return proto.CompactTextString(m) } func (m *ClosedChannelUpdate) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelUpdate) ProtoMessage() {} func (*ClosedChannelUpdate) ProtoMessage() {}
func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{87} return fileDescriptor_rpc_1d2b968b4af573ab, []int{87}
} }
func (m *ClosedChannelUpdate) XXX_Unmarshal(b []byte) error { func (m *ClosedChannelUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelUpdate.Unmarshal(m, b) return xxx_messageInfo_ClosedChannelUpdate.Unmarshal(m, b)
@ -6238,7 +6241,7 @@ func (m *HopHint) Reset() { *m = HopHint{} }
func (m *HopHint) String() string { return proto.CompactTextString(m) } func (m *HopHint) String() string { return proto.CompactTextString(m) }
func (*HopHint) ProtoMessage() {} func (*HopHint) ProtoMessage() {}
func (*HopHint) Descriptor() ([]byte, []int) { func (*HopHint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{88} return fileDescriptor_rpc_1d2b968b4af573ab, []int{88}
} }
func (m *HopHint) XXX_Unmarshal(b []byte) error { func (m *HopHint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HopHint.Unmarshal(m, b) return xxx_messageInfo_HopHint.Unmarshal(m, b)
@ -6307,7 +6310,7 @@ func (m *RouteHint) Reset() { *m = RouteHint{} }
func (m *RouteHint) String() string { return proto.CompactTextString(m) } func (m *RouteHint) String() string { return proto.CompactTextString(m) }
func (*RouteHint) ProtoMessage() {} func (*RouteHint) ProtoMessage() {}
func (*RouteHint) Descriptor() ([]byte, []int) { func (*RouteHint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{89} return fileDescriptor_rpc_1d2b968b4af573ab, []int{89}
} }
func (m *RouteHint) XXX_Unmarshal(b []byte) error { func (m *RouteHint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RouteHint.Unmarshal(m, b) return xxx_messageInfo_RouteHint.Unmarshal(m, b)
@ -6422,7 +6425,7 @@ func (m *Invoice) Reset() { *m = Invoice{} }
func (m *Invoice) String() string { return proto.CompactTextString(m) } func (m *Invoice) String() string { return proto.CompactTextString(m) }
func (*Invoice) ProtoMessage() {} func (*Invoice) ProtoMessage() {}
func (*Invoice) Descriptor() ([]byte, []int) { func (*Invoice) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{90} return fileDescriptor_rpc_1d2b968b4af573ab, []int{90}
} }
func (m *Invoice) XXX_Unmarshal(b []byte) error { func (m *Invoice) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Invoice.Unmarshal(m, b) return xxx_messageInfo_Invoice.Unmarshal(m, b)
@ -6614,7 +6617,7 @@ func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} }
func (m *AddInvoiceResponse) String() string { return proto.CompactTextString(m) } func (m *AddInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*AddInvoiceResponse) ProtoMessage() {} func (*AddInvoiceResponse) ProtoMessage() {}
func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { func (*AddInvoiceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{91} return fileDescriptor_rpc_1d2b968b4af573ab, []int{91}
} }
func (m *AddInvoiceResponse) XXX_Unmarshal(b []byte) error { func (m *AddInvoiceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddInvoiceResponse.Unmarshal(m, b) return xxx_messageInfo_AddInvoiceResponse.Unmarshal(m, b)
@ -6671,7 +6674,7 @@ func (m *PaymentHash) Reset() { *m = PaymentHash{} }
func (m *PaymentHash) String() string { return proto.CompactTextString(m) } func (m *PaymentHash) String() string { return proto.CompactTextString(m) }
func (*PaymentHash) ProtoMessage() {} func (*PaymentHash) ProtoMessage() {}
func (*PaymentHash) Descriptor() ([]byte, []int) { func (*PaymentHash) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{92} return fileDescriptor_rpc_1d2b968b4af573ab, []int{92}
} }
func (m *PaymentHash) XXX_Unmarshal(b []byte) error { func (m *PaymentHash) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PaymentHash.Unmarshal(m, b) return xxx_messageInfo_PaymentHash.Unmarshal(m, b)
@ -6727,7 +6730,7 @@ func (m *ListInvoiceRequest) Reset() { *m = ListInvoiceRequest{} }
func (m *ListInvoiceRequest) String() string { return proto.CompactTextString(m) } func (m *ListInvoiceRequest) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceRequest) ProtoMessage() {} func (*ListInvoiceRequest) ProtoMessage() {}
func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { func (*ListInvoiceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{93} return fileDescriptor_rpc_1d2b968b4af573ab, []int{93}
} }
func (m *ListInvoiceRequest) XXX_Unmarshal(b []byte) error { func (m *ListInvoiceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListInvoiceRequest.Unmarshal(m, b) return xxx_messageInfo_ListInvoiceRequest.Unmarshal(m, b)
@ -6797,7 +6800,7 @@ func (m *ListInvoiceResponse) Reset() { *m = ListInvoiceResponse{} }
func (m *ListInvoiceResponse) String() string { return proto.CompactTextString(m) } func (m *ListInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceResponse) ProtoMessage() {} func (*ListInvoiceResponse) ProtoMessage() {}
func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { func (*ListInvoiceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{94} return fileDescriptor_rpc_1d2b968b4af573ab, []int{94}
} }
func (m *ListInvoiceResponse) XXX_Unmarshal(b []byte) error { func (m *ListInvoiceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListInvoiceResponse.Unmarshal(m, b) return xxx_messageInfo_ListInvoiceResponse.Unmarshal(m, b)
@ -6860,7 +6863,7 @@ func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} }
func (m *InvoiceSubscription) String() string { return proto.CompactTextString(m) } func (m *InvoiceSubscription) String() string { return proto.CompactTextString(m) }
func (*InvoiceSubscription) ProtoMessage() {} func (*InvoiceSubscription) ProtoMessage() {}
func (*InvoiceSubscription) Descriptor() ([]byte, []int) { func (*InvoiceSubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{95} return fileDescriptor_rpc_1d2b968b4af573ab, []int{95}
} }
func (m *InvoiceSubscription) XXX_Unmarshal(b []byte) error { func (m *InvoiceSubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InvoiceSubscription.Unmarshal(m, b) return xxx_messageInfo_InvoiceSubscription.Unmarshal(m, b)
@ -6920,7 +6923,7 @@ func (m *Payment) Reset() { *m = Payment{} }
func (m *Payment) String() string { return proto.CompactTextString(m) } func (m *Payment) String() string { return proto.CompactTextString(m) }
func (*Payment) ProtoMessage() {} func (*Payment) ProtoMessage() {}
func (*Payment) Descriptor() ([]byte, []int) { func (*Payment) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{96} return fileDescriptor_rpc_1d2b968b4af573ab, []int{96}
} }
func (m *Payment) XXX_Unmarshal(b []byte) error { func (m *Payment) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Payment.Unmarshal(m, b) return xxx_messageInfo_Payment.Unmarshal(m, b)
@ -7007,7 +7010,7 @@ func (m *ListPaymentsRequest) Reset() { *m = ListPaymentsRequest{} }
func (m *ListPaymentsRequest) String() string { return proto.CompactTextString(m) } func (m *ListPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsRequest) ProtoMessage() {} func (*ListPaymentsRequest) ProtoMessage() {}
func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { func (*ListPaymentsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{97} return fileDescriptor_rpc_1d2b968b4af573ab, []int{97}
} }
func (m *ListPaymentsRequest) XXX_Unmarshal(b []byte) error { func (m *ListPaymentsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPaymentsRequest.Unmarshal(m, b) return xxx_messageInfo_ListPaymentsRequest.Unmarshal(m, b)
@ -7039,7 +7042,7 @@ func (m *ListPaymentsResponse) Reset() { *m = ListPaymentsResponse{} }
func (m *ListPaymentsResponse) String() string { return proto.CompactTextString(m) } func (m *ListPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsResponse) ProtoMessage() {} func (*ListPaymentsResponse) ProtoMessage() {}
func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { func (*ListPaymentsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{98} return fileDescriptor_rpc_1d2b968b4af573ab, []int{98}
} }
func (m *ListPaymentsResponse) XXX_Unmarshal(b []byte) error { func (m *ListPaymentsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPaymentsResponse.Unmarshal(m, b) return xxx_messageInfo_ListPaymentsResponse.Unmarshal(m, b)
@ -7076,7 +7079,7 @@ func (m *DeleteAllPaymentsRequest) Reset() { *m = DeleteAllPaymentsReque
func (m *DeleteAllPaymentsRequest) String() string { return proto.CompactTextString(m) } func (m *DeleteAllPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsRequest) ProtoMessage() {} func (*DeleteAllPaymentsRequest) ProtoMessage() {}
func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{99} return fileDescriptor_rpc_1d2b968b4af573ab, []int{99}
} }
func (m *DeleteAllPaymentsRequest) XXX_Unmarshal(b []byte) error { func (m *DeleteAllPaymentsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteAllPaymentsRequest.Unmarshal(m, b) return xxx_messageInfo_DeleteAllPaymentsRequest.Unmarshal(m, b)
@ -7106,7 +7109,7 @@ func (m *DeleteAllPaymentsResponse) Reset() { *m = DeleteAllPaymentsResp
func (m *DeleteAllPaymentsResponse) String() string { return proto.CompactTextString(m) } func (m *DeleteAllPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsResponse) ProtoMessage() {} func (*DeleteAllPaymentsResponse) ProtoMessage() {}
func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{100} return fileDescriptor_rpc_1d2b968b4af573ab, []int{100}
} }
func (m *DeleteAllPaymentsResponse) XXX_Unmarshal(b []byte) error { func (m *DeleteAllPaymentsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteAllPaymentsResponse.Unmarshal(m, b) return xxx_messageInfo_DeleteAllPaymentsResponse.Unmarshal(m, b)
@ -7137,7 +7140,7 @@ func (m *AbandonChannelRequest) Reset() { *m = AbandonChannelRequest{} }
func (m *AbandonChannelRequest) String() string { return proto.CompactTextString(m) } func (m *AbandonChannelRequest) String() string { return proto.CompactTextString(m) }
func (*AbandonChannelRequest) ProtoMessage() {} func (*AbandonChannelRequest) ProtoMessage() {}
func (*AbandonChannelRequest) Descriptor() ([]byte, []int) { func (*AbandonChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{101} return fileDescriptor_rpc_1d2b968b4af573ab, []int{101}
} }
func (m *AbandonChannelRequest) XXX_Unmarshal(b []byte) error { func (m *AbandonChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AbandonChannelRequest.Unmarshal(m, b) return xxx_messageInfo_AbandonChannelRequest.Unmarshal(m, b)
@ -7174,7 +7177,7 @@ func (m *AbandonChannelResponse) Reset() { *m = AbandonChannelResponse{}
func (m *AbandonChannelResponse) String() string { return proto.CompactTextString(m) } func (m *AbandonChannelResponse) String() string { return proto.CompactTextString(m) }
func (*AbandonChannelResponse) ProtoMessage() {} func (*AbandonChannelResponse) ProtoMessage() {}
func (*AbandonChannelResponse) Descriptor() ([]byte, []int) { func (*AbandonChannelResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{102} return fileDescriptor_rpc_1d2b968b4af573ab, []int{102}
} }
func (m *AbandonChannelResponse) XXX_Unmarshal(b []byte) error { func (m *AbandonChannelResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AbandonChannelResponse.Unmarshal(m, b) return xxx_messageInfo_AbandonChannelResponse.Unmarshal(m, b)
@ -7206,7 +7209,7 @@ func (m *DebugLevelRequest) Reset() { *m = DebugLevelRequest{} }
func (m *DebugLevelRequest) String() string { return proto.CompactTextString(m) } func (m *DebugLevelRequest) String() string { return proto.CompactTextString(m) }
func (*DebugLevelRequest) ProtoMessage() {} func (*DebugLevelRequest) ProtoMessage() {}
func (*DebugLevelRequest) Descriptor() ([]byte, []int) { func (*DebugLevelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{103} return fileDescriptor_rpc_1d2b968b4af573ab, []int{103}
} }
func (m *DebugLevelRequest) XXX_Unmarshal(b []byte) error { func (m *DebugLevelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DebugLevelRequest.Unmarshal(m, b) return xxx_messageInfo_DebugLevelRequest.Unmarshal(m, b)
@ -7251,7 +7254,7 @@ func (m *DebugLevelResponse) Reset() { *m = DebugLevelResponse{} }
func (m *DebugLevelResponse) String() string { return proto.CompactTextString(m) } func (m *DebugLevelResponse) String() string { return proto.CompactTextString(m) }
func (*DebugLevelResponse) ProtoMessage() {} func (*DebugLevelResponse) ProtoMessage() {}
func (*DebugLevelResponse) Descriptor() ([]byte, []int) { func (*DebugLevelResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{104} return fileDescriptor_rpc_1d2b968b4af573ab, []int{104}
} }
func (m *DebugLevelResponse) XXX_Unmarshal(b []byte) error { func (m *DebugLevelResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DebugLevelResponse.Unmarshal(m, b) return xxx_messageInfo_DebugLevelResponse.Unmarshal(m, b)
@ -7290,7 +7293,7 @@ func (m *PayReqString) Reset() { *m = PayReqString{} }
func (m *PayReqString) String() string { return proto.CompactTextString(m) } func (m *PayReqString) String() string { return proto.CompactTextString(m) }
func (*PayReqString) ProtoMessage() {} func (*PayReqString) ProtoMessage() {}
func (*PayReqString) Descriptor() ([]byte, []int) { func (*PayReqString) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{105} return fileDescriptor_rpc_1d2b968b4af573ab, []int{105}
} }
func (m *PayReqString) XXX_Unmarshal(b []byte) error { func (m *PayReqString) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PayReqString.Unmarshal(m, b) return xxx_messageInfo_PayReqString.Unmarshal(m, b)
@ -7337,7 +7340,7 @@ func (m *PayReq) Reset() { *m = PayReq{} }
func (m *PayReq) String() string { return proto.CompactTextString(m) } func (m *PayReq) String() string { return proto.CompactTextString(m) }
func (*PayReq) ProtoMessage() {} func (*PayReq) ProtoMessage() {}
func (*PayReq) Descriptor() ([]byte, []int) { func (*PayReq) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{106} return fileDescriptor_rpc_1d2b968b4af573ab, []int{106}
} }
func (m *PayReq) XXX_Unmarshal(b []byte) error { func (m *PayReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PayReq.Unmarshal(m, b) return xxx_messageInfo_PayReq.Unmarshal(m, b)
@ -7437,7 +7440,7 @@ func (m *FeeReportRequest) Reset() { *m = FeeReportRequest{} }
func (m *FeeReportRequest) String() string { return proto.CompactTextString(m) } func (m *FeeReportRequest) String() string { return proto.CompactTextString(m) }
func (*FeeReportRequest) ProtoMessage() {} func (*FeeReportRequest) ProtoMessage() {}
func (*FeeReportRequest) Descriptor() ([]byte, []int) { func (*FeeReportRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{107} return fileDescriptor_rpc_1d2b968b4af573ab, []int{107}
} }
func (m *FeeReportRequest) XXX_Unmarshal(b []byte) error { func (m *FeeReportRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeReportRequest.Unmarshal(m, b) return xxx_messageInfo_FeeReportRequest.Unmarshal(m, b)
@ -7475,7 +7478,7 @@ func (m *ChannelFeeReport) Reset() { *m = ChannelFeeReport{} }
func (m *ChannelFeeReport) String() string { return proto.CompactTextString(m) } func (m *ChannelFeeReport) String() string { return proto.CompactTextString(m) }
func (*ChannelFeeReport) ProtoMessage() {} func (*ChannelFeeReport) ProtoMessage() {}
func (*ChannelFeeReport) Descriptor() ([]byte, []int) { func (*ChannelFeeReport) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{108} return fileDescriptor_rpc_1d2b968b4af573ab, []int{108}
} }
func (m *ChannelFeeReport) XXX_Unmarshal(b []byte) error { func (m *ChannelFeeReport) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelFeeReport.Unmarshal(m, b) return xxx_messageInfo_ChannelFeeReport.Unmarshal(m, b)
@ -7541,7 +7544,7 @@ func (m *FeeReportResponse) Reset() { *m = FeeReportResponse{} }
func (m *FeeReportResponse) String() string { return proto.CompactTextString(m) } func (m *FeeReportResponse) String() string { return proto.CompactTextString(m) }
func (*FeeReportResponse) ProtoMessage() {} func (*FeeReportResponse) ProtoMessage() {}
func (*FeeReportResponse) Descriptor() ([]byte, []int) { func (*FeeReportResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{109} return fileDescriptor_rpc_1d2b968b4af573ab, []int{109}
} }
func (m *FeeReportResponse) XXX_Unmarshal(b []byte) error { func (m *FeeReportResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeReportResponse.Unmarshal(m, b) return xxx_messageInfo_FeeReportResponse.Unmarshal(m, b)
@ -7609,7 +7612,7 @@ func (m *PolicyUpdateRequest) Reset() { *m = PolicyUpdateRequest{} }
func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) } func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateRequest) ProtoMessage() {} func (*PolicyUpdateRequest) ProtoMessage() {}
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{110} return fileDescriptor_rpc_1d2b968b4af573ab, []int{110}
} }
func (m *PolicyUpdateRequest) XXX_Unmarshal(b []byte) error { func (m *PolicyUpdateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PolicyUpdateRequest.Unmarshal(m, b) return xxx_messageInfo_PolicyUpdateRequest.Unmarshal(m, b)
@ -7770,7 +7773,7 @@ func (m *PolicyUpdateResponse) Reset() { *m = PolicyUpdateResponse{} }
func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) } func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateResponse) ProtoMessage() {} func (*PolicyUpdateResponse) ProtoMessage() {}
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{111} return fileDescriptor_rpc_1d2b968b4af573ab, []int{111}
} }
func (m *PolicyUpdateResponse) XXX_Unmarshal(b []byte) error { func (m *PolicyUpdateResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PolicyUpdateResponse.Unmarshal(m, b) return xxx_messageInfo_PolicyUpdateResponse.Unmarshal(m, b)
@ -7808,7 +7811,7 @@ func (m *ForwardingHistoryRequest) Reset() { *m = ForwardingHistoryReque
func (m *ForwardingHistoryRequest) String() string { return proto.CompactTextString(m) } func (m *ForwardingHistoryRequest) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryRequest) ProtoMessage() {} func (*ForwardingHistoryRequest) ProtoMessage() {}
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{112} return fileDescriptor_rpc_1d2b968b4af573ab, []int{112}
} }
func (m *ForwardingHistoryRequest) XXX_Unmarshal(b []byte) error { func (m *ForwardingHistoryRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingHistoryRequest.Unmarshal(m, b) return xxx_messageInfo_ForwardingHistoryRequest.Unmarshal(m, b)
@ -7880,7 +7883,7 @@ func (m *ForwardingEvent) Reset() { *m = ForwardingEvent{} }
func (m *ForwardingEvent) String() string { return proto.CompactTextString(m) } func (m *ForwardingEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardingEvent) ProtoMessage() {} func (*ForwardingEvent) ProtoMessage() {}
func (*ForwardingEvent) Descriptor() ([]byte, []int) { func (*ForwardingEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{113} return fileDescriptor_rpc_1d2b968b4af573ab, []int{113}
} }
func (m *ForwardingEvent) XXX_Unmarshal(b []byte) error { func (m *ForwardingEvent) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingEvent.Unmarshal(m, b) return xxx_messageInfo_ForwardingEvent.Unmarshal(m, b)
@ -7963,7 +7966,7 @@ func (m *ForwardingHistoryResponse) Reset() { *m = ForwardingHistoryResp
func (m *ForwardingHistoryResponse) String() string { return proto.CompactTextString(m) } func (m *ForwardingHistoryResponse) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryResponse) ProtoMessage() {} func (*ForwardingHistoryResponse) ProtoMessage() {}
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_3ef4d8a7aac1a994, []int{114} return fileDescriptor_rpc_1d2b968b4af573ab, []int{114}
} }
func (m *ForwardingHistoryResponse) XXX_Unmarshal(b []byte) error { func (m *ForwardingHistoryResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingHistoryResponse.Unmarshal(m, b) return xxx_messageInfo_ForwardingHistoryResponse.Unmarshal(m, b)
@ -10464,455 +10467,456 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
Metadata: "rpc.proto", Metadata: "rpc.proto",
} }
func init() { proto.RegisterFile("rpc.proto", fileDescriptor_rpc_3ef4d8a7aac1a994) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor_rpc_1d2b968b4af573ab) }
var fileDescriptor_rpc_3ef4d8a7aac1a994 = []byte{ var fileDescriptor_rpc_1d2b968b4af573ab = []byte{
// 7142 bytes of a gzipped FileDescriptorProto // 7154 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x5d, 0x6c, 0x24, 0xd9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x5d, 0x6c, 0x24, 0xd9,
0x5d, 0xef, 0x54, 0x7f, 0xd8, 0xdd, 0xff, 0x6e, 0xb7, 0xdb, 0xc7, 0x5f, 0x3d, 0xbd, 0xb3, 0xb3, 0x5d, 0xef, 0x54, 0x7f, 0xd8, 0xdd, 0xff, 0x6e, 0xb7, 0xdb, 0xc7, 0x5f, 0x3d, 0xbd, 0xb3, 0xb3,
0xde, 0xca, 0xdc, 0x1d, 0xc7, 0xd9, 0x3b, 0x9e, 0x9d, 0x24, 0x9b, 0xcd, 0xee, 0xcd, 0xbd, 0xd7, 0xde, 0xca, 0xdc, 0x1d, 0xc7, 0xd9, 0x3b, 0x9e, 0x9d, 0x24, 0x9b, 0xcd, 0xee, 0xcd, 0xbd, 0xd7,
0x63, 0x7b, 0xc6, 0x93, 0x78, 0x3d, 0x4e, 0x79, 0x26, 0x73, 0xb3, 0xc9, 0x55, 0xa7, 0xdc, 0x7d, 0x63, 0x7b, 0xc6, 0x93, 0x78, 0x3d, 0x4e, 0x79, 0x26, 0x73, 0xb3, 0xc9, 0x55, 0xa7, 0xdc, 0x7d,
0xdc, 0xae, 0x9d, 0xea, 0xaa, 0x4e, 0x55, 0xb5, 0x3d, 0xce, 0x32, 0x12, 0x02, 0x44, 0x24, 0x04, 0xdc, 0xae, 0x9d, 0xea, 0xaa, 0x4e, 0x55, 0xb5, 0x3d, 0xce, 0x32, 0x12, 0x02, 0x04, 0x12, 0x02,
0x42, 0xc0, 0x0b, 0x41, 0x41, 0x48, 0x01, 0x09, 0xf2, 0xc8, 0x43, 0x10, 0x12, 0x1f, 0x4f, 0x88, 0x21, 0x40, 0x48, 0x04, 0x05, 0x21, 0x05, 0x24, 0xc8, 0x23, 0x0f, 0x41, 0x48, 0x7c, 0x3c, 0x21,
0x07, 0x24, 0x84, 0x20, 0x8f, 0x48, 0x48, 0x08, 0x5e, 0x80, 0x07, 0x24, 0x24, 0x1e, 0x91, 0xd0, 0x1e, 0x90, 0x10, 0x82, 0x3c, 0x22, 0x21, 0x21, 0x78, 0x01, 0x1e, 0x90, 0x90, 0x78, 0x44, 0x42,
0xf9, 0x9f, 0x8f, 0x3a, 0xa7, 0xaa, 0x7a, 0x3c, 0x9b, 0x04, 0x9e, 0xec, 0xf3, 0x3b, 0xff, 0x3a, 0xe7, 0x7f, 0x3e, 0xea, 0x9c, 0xaa, 0xea, 0xf1, 0x6c, 0x12, 0x78, 0xb2, 0xcf, 0xef, 0xfc, 0xeb,
0x9f, 0xff, 0xaf, 0xf3, 0x3f, 0xff, 0xd3, 0x50, 0x8f, 0xc6, 0xfd, 0x5b, 0xe3, 0x28, 0x4c, 0x42, 0x7c, 0xfe, 0xbf, 0xce, 0xff, 0xfc, 0x4f, 0x43, 0x3d, 0x1a, 0xf7, 0x6f, 0x8d, 0xa3, 0x30, 0x09,
0x52, 0xf5, 0x83, 0x68, 0xdc, 0xef, 0x5e, 0x1b, 0x86, 0xe1, 0xd0, 0xa7, 0x9b, 0xee, 0xd8, 0xdb, 0x49, 0xd5, 0x0f, 0xa2, 0x71, 0xbf, 0x7b, 0x6d, 0x18, 0x86, 0x43, 0x9f, 0x6e, 0xba, 0x63, 0x6f,
0x74, 0x83, 0x20, 0x4c, 0xdc, 0xc4, 0x0b, 0x83, 0x98, 0x13, 0xd9, 0xdf, 0x80, 0xd6, 0x7d, 0x1a, 0xd3, 0x0d, 0x82, 0x30, 0x71, 0x13, 0x2f, 0x0c, 0x62, 0x4e, 0x64, 0x7f, 0x03, 0x5a, 0xf7, 0x69,
0x1c, 0x51, 0x3a, 0x70, 0xe8, 0x37, 0x27, 0x34, 0x4e, 0xc8, 0xa7, 0x60, 0xc1, 0xa5, 0xdf, 0xa2, 0x70, 0x44, 0xe9, 0xc0, 0xa1, 0xdf, 0x9c, 0xd0, 0x38, 0x21, 0x9f, 0x82, 0x05, 0x97, 0x7e, 0x8b,
0x74, 0xd0, 0x1b, 0xbb, 0x71, 0x3c, 0x3e, 0x8d, 0xdc, 0x98, 0x76, 0xac, 0x35, 0x6b, 0xbd, 0xe9, 0xd2, 0x41, 0x6f, 0xec, 0xc6, 0xf1, 0xf8, 0x34, 0x72, 0x63, 0xda, 0xb1, 0xd6, 0xac, 0xf5, 0xa6,
0xb4, 0x79, 0xc5, 0xa1, 0xc2, 0xc9, 0xeb, 0xd0, 0x8c, 0x19, 0x29, 0x0d, 0x92, 0x28, 0x1c, 0x5f, 0xd3, 0xe6, 0x15, 0x87, 0x0a, 0x27, 0xaf, 0x43, 0x33, 0x66, 0xa4, 0x34, 0x48, 0xa2, 0x70, 0x7c,
0x74, 0x4a, 0x48, 0xd7, 0x60, 0xd8, 0x2e, 0x87, 0x6c, 0x1f, 0xe6, 0x55, 0x0f, 0xf1, 0x38, 0x0c, 0xd1, 0x29, 0x21, 0x5d, 0x83, 0x61, 0xbb, 0x1c, 0xb2, 0x7d, 0x98, 0x57, 0x3d, 0xc4, 0xe3, 0x30,
0x62, 0x4a, 0x6e, 0xc3, 0x52, 0xdf, 0x1b, 0x9f, 0xd2, 0xa8, 0x87, 0x1f, 0x8f, 0x02, 0x3a, 0x0a, 0x88, 0x29, 0xb9, 0x0d, 0x4b, 0x7d, 0x6f, 0x7c, 0x4a, 0xa3, 0x1e, 0x7e, 0x3c, 0x0a, 0xe8, 0x28,
0x03, 0xaf, 0xdf, 0xb1, 0xd6, 0xca, 0xeb, 0x75, 0x87, 0xf0, 0x3a, 0xf6, 0xc5, 0xfb, 0xa2, 0x86, 0x0c, 0xbc, 0x7e, 0xc7, 0x5a, 0x2b, 0xaf, 0xd7, 0x1d, 0xc2, 0xeb, 0xd8, 0x17, 0xef, 0x8b, 0x1a,
0xdc, 0x84, 0x79, 0x1a, 0x70, 0x9c, 0x0e, 0xf0, 0x2b, 0xd1, 0x55, 0x2b, 0x85, 0xd9, 0x07, 0xf6, 0x72, 0x13, 0xe6, 0x69, 0xc0, 0x71, 0x3a, 0xc0, 0xaf, 0x44, 0x57, 0xad, 0x14, 0x66, 0x1f, 0xd8,
0x9f, 0x59, 0xb0, 0xf0, 0x20, 0xf0, 0x92, 0x27, 0xae, 0xef, 0xd3, 0x44, 0xce, 0xe9, 0x26, 0xcc, 0x7f, 0x66, 0xc1, 0xc2, 0x83, 0xc0, 0x4b, 0x9e, 0xb8, 0xbe, 0x4f, 0x13, 0x39, 0xa7, 0x9b, 0x30,
0x9f, 0x23, 0x80, 0x73, 0x3a, 0x0f, 0xa3, 0x81, 0x98, 0x51, 0x8b, 0xc3, 0x87, 0x02, 0x9d, 0x3a, 0x7f, 0x8e, 0x00, 0xce, 0xe9, 0x3c, 0x8c, 0x06, 0x62, 0x46, 0x2d, 0x0e, 0x1f, 0x0a, 0x74, 0xea,
0xb2, 0xd2, 0xd4, 0x91, 0x15, 0x2e, 0x57, 0x79, 0xca, 0x72, 0xdd, 0x84, 0xf9, 0x88, 0xf6, 0xc3, 0xc8, 0x4a, 0x53, 0x47, 0x56, 0xb8, 0x5c, 0xe5, 0x29, 0xcb, 0x75, 0x13, 0xe6, 0x23, 0xda, 0x0f,
0x33, 0x1a, 0x5d, 0xf4, 0xce, 0xbd, 0x60, 0x10, 0x9e, 0x77, 0x2a, 0x6b, 0xd6, 0x7a, 0xd5, 0x69, 0xcf, 0x68, 0x74, 0xd1, 0x3b, 0xf7, 0x82, 0x41, 0x78, 0xde, 0xa9, 0xac, 0x59, 0xeb, 0x55, 0xa7,
0x49, 0xf8, 0x09, 0xa2, 0xf6, 0x12, 0x10, 0x7d, 0x16, 0x7c, 0xdd, 0xec, 0x21, 0x2c, 0x3e, 0x0e, 0x25, 0xe1, 0x27, 0x88, 0xda, 0x4b, 0x40, 0xf4, 0x59, 0xf0, 0x75, 0xb3, 0x87, 0xb0, 0xf8, 0x38,
0xfc, 0xb0, 0xff, 0xf4, 0x47, 0x9c, 0x5d, 0x41, 0xf7, 0xa5, 0xc2, 0xee, 0x57, 0x60, 0xc9, 0xec, 0xf0, 0xc3, 0xfe, 0xd3, 0x1f, 0x72, 0x76, 0x05, 0xdd, 0x97, 0x0a, 0xbb, 0x5f, 0x81, 0x25, 0xb3,
0x48, 0x0c, 0x80, 0xc2, 0xf2, 0xf6, 0xa9, 0x1b, 0x0c, 0xa9, 0x6c, 0x52, 0x0e, 0xe1, 0x93, 0xd0, 0x23, 0x31, 0x00, 0x0a, 0xcb, 0xdb, 0xa7, 0x6e, 0x30, 0xa4, 0xb2, 0x49, 0x39, 0x84, 0x4f, 0x42,
0xee, 0x4f, 0xa2, 0x88, 0x06, 0xb9, 0x31, 0xcc, 0x0b, 0x5c, 0x0d, 0xe2, 0x75, 0x68, 0x06, 0xf4, 0xbb, 0x3f, 0x89, 0x22, 0x1a, 0xe4, 0xc6, 0x30, 0x2f, 0x70, 0x35, 0x88, 0xd7, 0xa1, 0x19, 0xd0,
0x3c, 0x25, 0x13, 0x2c, 0x13, 0xd0, 0x73, 0x49, 0x62, 0x77, 0x60, 0x25, 0xdb, 0x8d, 0x18, 0xc0, 0xf3, 0x94, 0x4c, 0xb0, 0x4c, 0x40, 0xcf, 0x25, 0x89, 0xdd, 0x81, 0x95, 0x6c, 0x37, 0x62, 0x00,
0xdf, 0x5b, 0x50, 0x79, 0x9c, 0x3c, 0x0b, 0xc9, 0x2d, 0xa8, 0x24, 0x17, 0x63, 0xce, 0x98, 0xad, 0x7f, 0x6f, 0x41, 0xe5, 0x71, 0xf2, 0x2c, 0x24, 0xb7, 0xa0, 0x92, 0x5c, 0x8c, 0x39, 0x63, 0xb6,
0x3b, 0xe4, 0x16, 0xf2, 0xfa, 0xad, 0xad, 0xc1, 0x20, 0xa2, 0x71, 0xfc, 0xe8, 0x62, 0x4c, 0x9d, 0xee, 0x90, 0x5b, 0xc8, 0xeb, 0xb7, 0xb6, 0x06, 0x83, 0x88, 0xc6, 0xf1, 0xa3, 0x8b, 0x31, 0x75,
0xa6, 0xcb, 0x0b, 0x3d, 0x46, 0x47, 0x3a, 0x30, 0x2b, 0xca, 0xd8, 0x61, 0xdd, 0x91, 0x45, 0x72, 0x9a, 0x2e, 0x2f, 0xf4, 0x18, 0x1d, 0xe9, 0xc0, 0xac, 0x28, 0x63, 0x87, 0x75, 0x47, 0x16, 0xc9,
0x1d, 0xc0, 0x1d, 0x85, 0x93, 0x20, 0xe9, 0xc5, 0x6e, 0x82, 0x3b, 0x57, 0x76, 0x34, 0x84, 0x5c, 0x75, 0x00, 0x77, 0x14, 0x4e, 0x82, 0xa4, 0x17, 0xbb, 0x09, 0xee, 0x5c, 0xd9, 0xd1, 0x10, 0x72,
0x83, 0xfa, 0xf8, 0x69, 0x2f, 0xee, 0x47, 0xde, 0x38, 0xc1, 0xdd, 0xaa, 0x3b, 0x29, 0x40, 0x3e, 0x0d, 0xea, 0xe3, 0xa7, 0xbd, 0xb8, 0x1f, 0x79, 0xe3, 0x04, 0x77, 0xab, 0xee, 0xa4, 0x00, 0xf9,
0x05, 0xb5, 0x70, 0x92, 0x8c, 0x43, 0x2f, 0x48, 0x3a, 0xd5, 0x35, 0x6b, 0xbd, 0x71, 0x67, 0x5e, 0x14, 0xd4, 0xc2, 0x49, 0x32, 0x0e, 0xbd, 0x20, 0xe9, 0x54, 0xd7, 0xac, 0xf5, 0xc6, 0x9d, 0x79,
0x8c, 0xe5, 0xe1, 0x24, 0x39, 0x64, 0xb0, 0xa3, 0x08, 0xc8, 0x0d, 0x98, 0xeb, 0x87, 0xc1, 0x89, 0x31, 0x96, 0x87, 0x93, 0xe4, 0x90, 0xc1, 0x8e, 0x22, 0x20, 0x37, 0x60, 0xae, 0x1f, 0x06, 0x27,
0x17, 0x8d, 0xb8, 0x0c, 0x76, 0x66, 0xb0, 0x37, 0x13, 0xb4, 0xbf, 0x53, 0x82, 0xc6, 0xa3, 0xc8, 0x5e, 0x34, 0xe2, 0x32, 0xd8, 0x99, 0xc1, 0xde, 0x4c, 0xd0, 0xfe, 0x76, 0x09, 0x1a, 0x8f, 0x22,
0x0d, 0x62, 0xb7, 0xcf, 0x00, 0x36, 0xf4, 0xe4, 0x59, 0xef, 0xd4, 0x8d, 0x4f, 0x71, 0xb6, 0x75, 0x37, 0x88, 0xdd, 0x3e, 0x03, 0xd8, 0xd0, 0x93, 0x67, 0xbd, 0x53, 0x37, 0x3e, 0xc5, 0xd9, 0xd6,
0x47, 0x16, 0xc9, 0x0a, 0xcc, 0xf0, 0x81, 0xe2, 0x9c, 0xca, 0x8e, 0x28, 0x91, 0x37, 0x61, 0x21, 0x1d, 0x59, 0x24, 0x2b, 0x30, 0xc3, 0x07, 0x8a, 0x73, 0x2a, 0x3b, 0xa2, 0x44, 0xde, 0x84, 0x85,
0x98, 0x8c, 0x7a, 0x66, 0x5f, 0x65, 0xdc, 0xe9, 0x7c, 0x05, 0x5b, 0x80, 0x63, 0xb6, 0xd7, 0xbc, 0x60, 0x32, 0xea, 0x99, 0x7d, 0x95, 0x71, 0xa7, 0xf3, 0x15, 0x6c, 0x01, 0x8e, 0xd9, 0x5e, 0xf3,
0x0b, 0x3e, 0x43, 0x0d, 0x21, 0x36, 0x34, 0x45, 0x89, 0x7a, 0xc3, 0x53, 0x3e, 0xcd, 0xaa, 0x63, 0x2e, 0xf8, 0x0c, 0x35, 0x84, 0xd8, 0xd0, 0x14, 0x25, 0xea, 0x0d, 0x4f, 0xf9, 0x34, 0xab, 0x8e,
0x60, 0xac, 0x8d, 0xc4, 0x1b, 0xd1, 0x5e, 0x9c, 0xb8, 0xa3, 0xb1, 0x98, 0x96, 0x86, 0x60, 0x7d, 0x81, 0xb1, 0x36, 0x12, 0x6f, 0x44, 0x7b, 0x71, 0xe2, 0x8e, 0xc6, 0x62, 0x5a, 0x1a, 0x82, 0xf5,
0x98, 0xb8, 0x7e, 0xef, 0x84, 0xd2, 0xb8, 0x33, 0x2b, 0xea, 0x15, 0x42, 0xde, 0x80, 0xd6, 0x80, 0x61, 0xe2, 0xfa, 0xbd, 0x13, 0x4a, 0xe3, 0xce, 0xac, 0xa8, 0x57, 0x08, 0x79, 0x03, 0x5a, 0x03,
0xc6, 0x49, 0x4f, 0x6c, 0x0a, 0x8d, 0x3b, 0x35, 0x94, 0xb8, 0x0c, 0xca, 0x38, 0xe3, 0x3e, 0x4d, 0x1a, 0x27, 0x3d, 0xb1, 0x29, 0x34, 0xee, 0xd4, 0x50, 0xe2, 0x32, 0x28, 0xe3, 0x8c, 0xfb, 0x34,
0xb4, 0xd5, 0x89, 0x05, 0x07, 0xda, 0xfb, 0x40, 0x34, 0x78, 0x87, 0x26, 0xae, 0xe7, 0xc7, 0xe4, 0xd1, 0x56, 0x27, 0x16, 0x1c, 0x68, 0xef, 0x03, 0xd1, 0xe0, 0x1d, 0x9a, 0xb8, 0x9e, 0x1f, 0x93,
0x6d, 0x68, 0x26, 0x1a, 0x31, 0x6a, 0x98, 0x86, 0x62, 0x17, 0xed, 0x03, 0xc7, 0xa0, 0xb3, 0xef, 0xb7, 0xa1, 0x99, 0x68, 0xc4, 0xa8, 0x61, 0x1a, 0x8a, 0x5d, 0xb4, 0x0f, 0x1c, 0x83, 0xce, 0xbe,
0x43, 0xed, 0x1e, 0xa5, 0xfb, 0xde, 0xc8, 0x4b, 0xc8, 0x0a, 0x54, 0x4f, 0xbc, 0x67, 0x94, 0x33, 0x0f, 0xb5, 0x7b, 0x94, 0xee, 0x7b, 0x23, 0x2f, 0x21, 0x2b, 0x50, 0x3d, 0xf1, 0x9e, 0x51, 0xce,
0x74, 0x79, 0xef, 0x8a, 0xc3, 0x8b, 0xa4, 0x0b, 0xb3, 0x63, 0x1a, 0xf5, 0xa9, 0x5c, 0xfe, 0xbd, 0xd0, 0xe5, 0xbd, 0x2b, 0x0e, 0x2f, 0x92, 0x2e, 0xcc, 0x8e, 0x69, 0xd4, 0xa7, 0x72, 0xf9, 0xf7,
0x2b, 0x8e, 0x04, 0xee, 0xce, 0x42, 0xd5, 0x67, 0x1f, 0xdb, 0x7f, 0x53, 0x82, 0xc6, 0x11, 0x0d, 0xae, 0x38, 0x12, 0xb8, 0x3b, 0x0b, 0x55, 0x9f, 0x7d, 0x6c, 0xff, 0x4d, 0x09, 0x1a, 0x47, 0x34,
0x94, 0xa0, 0x10, 0xa8, 0xb0, 0x29, 0x09, 0xe1, 0xc0, 0xff, 0xc9, 0x6b, 0xd0, 0xc0, 0x69, 0xc6, 0x50, 0x82, 0x42, 0xa0, 0xc2, 0xa6, 0x24, 0x84, 0x03, 0xff, 0x27, 0xaf, 0x41, 0x03, 0xa7, 0x19,
0x49, 0xe4, 0x05, 0x43, 0xc1, 0x9f, 0xc0, 0xa0, 0x23, 0x44, 0x48, 0x1b, 0xca, 0xee, 0x48, 0xf2, 0x27, 0x91, 0x17, 0x0c, 0x05, 0x7f, 0x02, 0x83, 0x8e, 0x10, 0x21, 0x6d, 0x28, 0xbb, 0x23, 0xc9,
0x26, 0xfb, 0x97, 0x09, 0xd1, 0xd8, 0xbd, 0x18, 0x31, 0x79, 0x53, 0xbb, 0xd6, 0x74, 0x1a, 0x02, 0x9b, 0xec, 0x5f, 0x26, 0x44, 0x63, 0xf7, 0x62, 0xc4, 0xe4, 0x4d, 0xed, 0x5a, 0xd3, 0x69, 0x08,
0xdb, 0x63, 0xdb, 0x76, 0x0b, 0x16, 0x75, 0x12, 0xd9, 0x7a, 0x15, 0x5b, 0x5f, 0xd0, 0x28, 0x45, 0x6c, 0x8f, 0x6d, 0xdb, 0x2d, 0x58, 0xd4, 0x49, 0x64, 0xeb, 0x55, 0x6c, 0x7d, 0x41, 0xa3, 0x14,
0x27, 0x37, 0x61, 0x5e, 0xd2, 0x47, 0x7c, 0xb0, 0xb8, 0x8f, 0x75, 0xa7, 0x25, 0x60, 0x39, 0x85, 0x9d, 0xdc, 0x84, 0x79, 0x49, 0x1f, 0xf1, 0xc1, 0xe2, 0x3e, 0xd6, 0x9d, 0x96, 0x80, 0xe5, 0x14,
0x75, 0x68, 0x9f, 0x78, 0x81, 0xeb, 0xf7, 0xfa, 0x7e, 0x72, 0xd6, 0x1b, 0x50, 0x3f, 0x71, 0x71, 0xd6, 0xa1, 0x7d, 0xe2, 0x05, 0xae, 0xdf, 0xeb, 0xfb, 0xc9, 0x59, 0x6f, 0x40, 0xfd, 0xc4, 0xc5,
0x47, 0xab, 0x4e, 0x0b, 0xf1, 0x6d, 0x3f, 0x39, 0xdb, 0x61, 0x28, 0x79, 0x13, 0xea, 0x27, 0x94, 0x1d, 0xad, 0x3a, 0x2d, 0xc4, 0xb7, 0xfd, 0xe4, 0x6c, 0x87, 0xa1, 0xe4, 0x4d, 0xa8, 0x9f, 0x50,
0xf6, 0x70, 0x25, 0x3a, 0x35, 0x43, 0x3a, 0xe4, 0xea, 0x3a, 0xb5, 0x13, 0xb9, 0xce, 0xeb, 0xd0, 0xda, 0xc3, 0x95, 0xe8, 0xd4, 0x0c, 0xe9, 0x90, 0xab, 0xeb, 0xd4, 0x4e, 0xe4, 0x3a, 0xaf, 0x43,
0x0e, 0x27, 0xc9, 0x30, 0xf4, 0x82, 0x61, 0xaf, 0x7f, 0xea, 0x06, 0x3d, 0x6f, 0xd0, 0xa9, 0xaf, 0x3b, 0x9c, 0x24, 0xc3, 0xd0, 0x0b, 0x86, 0xbd, 0xfe, 0xa9, 0x1b, 0xf4, 0xbc, 0x41, 0xa7, 0xbe,
0x59, 0xeb, 0x15, 0xa7, 0x25, 0x71, 0xa6, 0x15, 0x1e, 0x0c, 0xec, 0x3f, 0xb4, 0xa0, 0xc9, 0x17, 0x66, 0xad, 0x57, 0x9c, 0x96, 0xc4, 0x99, 0x56, 0x78, 0x30, 0xb0, 0xff, 0xd0, 0x82, 0x26, 0x5f,
0x55, 0x18, 0x94, 0x1b, 0x30, 0x27, 0xc7, 0x4e, 0xa3, 0x28, 0x8c, 0x84, 0xa0, 0x98, 0x20, 0xd9, 0x54, 0x61, 0x50, 0x6e, 0xc0, 0x9c, 0x1c, 0x3b, 0x8d, 0xa2, 0x30, 0x12, 0x82, 0x62, 0x82, 0x64,
0x80, 0xb6, 0x04, 0xc6, 0x11, 0xf5, 0x46, 0xee, 0x90, 0x0a, 0xed, 0x93, 0xc3, 0xc9, 0x9d, 0xb4, 0x03, 0xda, 0x12, 0x18, 0x47, 0xd4, 0x1b, 0xb9, 0x43, 0x2a, 0xb4, 0x4f, 0x0e, 0x27, 0x77, 0xd2,
0xc5, 0x28, 0x9c, 0x24, 0x5c, 0xa5, 0x37, 0xee, 0x34, 0xc5, 0xf0, 0x1d, 0x86, 0x39, 0x26, 0x09, 0x16, 0xa3, 0x70, 0x92, 0x70, 0x95, 0xde, 0xb8, 0xd3, 0x14, 0xc3, 0x77, 0x18, 0xe6, 0x98, 0x24,
0x13, 0x94, 0x82, 0x4d, 0x31, 0x30, 0xfb, 0x07, 0x16, 0x10, 0x36, 0xf4, 0x47, 0x21, 0x6f, 0x42, 0x4c, 0x50, 0x0a, 0x36, 0xc5, 0xc0, 0xec, 0xef, 0x5b, 0x40, 0xd8, 0xd0, 0x1f, 0x85, 0xbc, 0x09,
0xac, 0x69, 0x76, 0x3f, 0xad, 0x97, 0xde, 0xcf, 0xd2, 0xb4, 0xfd, 0x5c, 0x87, 0x19, 0x1c, 0x16, 0xb1, 0xa6, 0xd9, 0xfd, 0xb4, 0x5e, 0x7a, 0x3f, 0x4b, 0xd3, 0xf6, 0x73, 0x1d, 0x66, 0x70, 0x58,
0x93, 0xfc, 0x72, 0x76, 0xe8, 0x77, 0x4b, 0x1d, 0xcb, 0x11, 0xf5, 0xc4, 0x86, 0x2a, 0x9f, 0x63, 0x4c, 0xf2, 0xcb, 0xd9, 0xa1, 0xdf, 0x2d, 0x75, 0x2c, 0x47, 0xd4, 0x13, 0x1b, 0xaa, 0x7c, 0x8e,
0xa5, 0x60, 0x8e, 0xbc, 0xca, 0xfe, 0x9e, 0x05, 0x4d, 0xb6, 0xfa, 0x01, 0xf5, 0x51, 0xab, 0x91, 0x95, 0x82, 0x39, 0xf2, 0x2a, 0xfb, 0xbb, 0x16, 0x34, 0xd9, 0xea, 0x07, 0xd4, 0x47, 0xad, 0x46,
0xdb, 0x40, 0x4e, 0x26, 0xc1, 0x80, 0x6d, 0x56, 0xf2, 0xcc, 0x1b, 0xf4, 0x8e, 0x2f, 0x58, 0x57, 0x6e, 0x03, 0x39, 0x99, 0x04, 0x03, 0xb6, 0x59, 0xc9, 0x33, 0x6f, 0xd0, 0x3b, 0xbe, 0x60, 0x5d,
0x38, 0xee, 0xbd, 0x2b, 0x4e, 0x41, 0x1d, 0x79, 0x13, 0xda, 0x06, 0x1a, 0x27, 0x11, 0x1f, 0xfd, 0xe1, 0xb8, 0xf7, 0xae, 0x38, 0x05, 0x75, 0xe4, 0x4d, 0x68, 0x1b, 0x68, 0x9c, 0x44, 0x7c, 0xf4,
0xde, 0x15, 0x27, 0x57, 0xc3, 0x16, 0x93, 0xe9, 0xcd, 0x49, 0xd2, 0xf3, 0x82, 0x01, 0x7d, 0x86, 0x7b, 0x57, 0x9c, 0x5c, 0x0d, 0x5b, 0x4c, 0xa6, 0x37, 0x27, 0x49, 0xcf, 0x0b, 0x06, 0xf4, 0x19,
0xeb, 0x3f, 0xe7, 0x18, 0xd8, 0xdd, 0x16, 0x34, 0xf5, 0xef, 0xec, 0x0f, 0xa1, 0x26, 0xb5, 0x2e, 0xae, 0xff, 0x9c, 0x63, 0x60, 0x77, 0x5b, 0xd0, 0xd4, 0xbf, 0xb3, 0x3f, 0x84, 0x9a, 0xd4, 0xba,
0x6a, 0x9c, 0xcc, 0xb8, 0x1c, 0x0d, 0x21, 0x5d, 0xa8, 0x99, 0xa3, 0x70, 0x6a, 0x1f, 0xa7, 0x6f, 0xa8, 0x71, 0x32, 0xe3, 0x72, 0x34, 0x84, 0x74, 0xa1, 0x66, 0x8e, 0xc2, 0xa9, 0x7d, 0x9c, 0xbe,
0xfb, 0x7f, 0x43, 0x7b, 0x9f, 0xa9, 0xbe, 0xc0, 0x0b, 0x86, 0xc2, 0xec, 0x30, 0x7d, 0x3c, 0x9e, 0xed, 0xff, 0x0d, 0xed, 0x7d, 0xa6, 0xfa, 0x02, 0x2f, 0x18, 0x0a, 0xb3, 0xc3, 0xf4, 0xf1, 0x78,
0x1c, 0x3f, 0xa5, 0x17, 0x82, 0xff, 0x44, 0x89, 0x09, 0xfd, 0x69, 0x18, 0x27, 0xa2, 0x1f, 0xfc, 0x72, 0xfc, 0x94, 0x5e, 0x08, 0xfe, 0x13, 0x25, 0x26, 0xf4, 0xa7, 0x61, 0x9c, 0x88, 0x7e, 0xf0,
0xdf, 0xfe, 0x07, 0x0b, 0xe6, 0x19, 0x23, 0xbc, 0xef, 0x06, 0x17, 0x92, 0x0b, 0xf6, 0xa1, 0xc9, 0x7f, 0xfb, 0x1f, 0x2c, 0x98, 0x67, 0x8c, 0xf0, 0xbe, 0x1b, 0x5c, 0x48, 0x2e, 0xd8, 0x87, 0x26,
0x9a, 0x7a, 0x14, 0x6e, 0x71, 0xad, 0xce, 0xb5, 0xd5, 0xba, 0xd8, 0x8f, 0x0c, 0xf5, 0x2d, 0x9d, 0x6b, 0xea, 0x51, 0xb8, 0xc5, 0xb5, 0x3a, 0xd7, 0x56, 0xeb, 0x62, 0x3f, 0x32, 0xd4, 0xb7, 0x74,
0x94, 0x39, 0x5b, 0x17, 0x8e, 0xf1, 0x35, 0x53, 0x2b, 0x89, 0x1b, 0x0d, 0x69, 0x82, 0xfa, 0x5e, 0x52, 0xe6, 0x6c, 0x5d, 0x38, 0xc6, 0xd7, 0x4c, 0xad, 0x24, 0x6e, 0x34, 0xa4, 0x09, 0xea, 0x7b,
0xe8, 0x7f, 0xe0, 0xd0, 0x76, 0x18, 0x9c, 0x90, 0x35, 0x68, 0xc6, 0x6e, 0xd2, 0x1b, 0xd3, 0x08, 0xa1, 0xff, 0x81, 0x43, 0xdb, 0x61, 0x70, 0x42, 0xd6, 0xa0, 0x19, 0xbb, 0x49, 0x6f, 0x4c, 0x23,
0xd7, 0x04, 0x55, 0x43, 0xd9, 0x81, 0xd8, 0x4d, 0x0e, 0x69, 0x74, 0xf7, 0x22, 0xa1, 0xdd, 0xff, 0x5c, 0x13, 0x54, 0x0d, 0x65, 0x07, 0x62, 0x37, 0x39, 0xa4, 0xd1, 0xdd, 0x8b, 0x84, 0x76, 0xff,
0x03, 0x0b, 0xb9, 0x5e, 0x98, 0x36, 0x4a, 0xa7, 0xc8, 0xfe, 0x25, 0x4b, 0x50, 0x3d, 0x73, 0xfd, 0x0f, 0x2c, 0xe4, 0x7a, 0x61, 0xda, 0x28, 0x9d, 0x22, 0xfb, 0x97, 0x2c, 0x41, 0xf5, 0xcc, 0xf5,
0x09, 0x15, 0x66, 0x88, 0x17, 0xde, 0x2d, 0xbd, 0x63, 0xd9, 0x6f, 0x40, 0x3b, 0x1d, 0xb6, 0x10, 0x27, 0x54, 0x98, 0x21, 0x5e, 0x78, 0xb7, 0xf4, 0x8e, 0x65, 0xbf, 0x01, 0xed, 0x74, 0xd8, 0x42,
0x56, 0x02, 0x15, 0xb6, 0xd2, 0xa2, 0x01, 0xfc, 0xdf, 0xfe, 0xae, 0xc5, 0x09, 0xb7, 0x43, 0x4f, 0x58, 0x09, 0x54, 0xd8, 0x4a, 0x8b, 0x06, 0xf0, 0x7f, 0xfb, 0x3b, 0x16, 0x27, 0xdc, 0x0e, 0x3d,
0xa9, 0x74, 0x46, 0xc8, 0x34, 0xbf, 0x24, 0x64, 0xff, 0x4f, 0x35, 0x79, 0x3f, 0xfe, 0x64, 0xc9, 0xa5, 0xd2, 0x19, 0x21, 0xd3, 0xfc, 0x92, 0x90, 0xfd, 0x3f, 0xd5, 0xe4, 0xfd, 0xe8, 0x93, 0x25,
0x55, 0xa8, 0xc5, 0x34, 0x18, 0xf4, 0x5c, 0xdf, 0x47, 0xcd, 0x57, 0x73, 0x66, 0x59, 0x79, 0xcb, 0x57, 0xa1, 0x16, 0xd3, 0x60, 0xd0, 0x73, 0x7d, 0x1f, 0x35, 0x5f, 0xcd, 0x99, 0x65, 0xe5, 0x2d,
0xf7, 0xed, 0x9b, 0xb0, 0xa0, 0x8d, 0xee, 0x05, 0xf3, 0x38, 0x00, 0xb2, 0xef, 0xc5, 0xc9, 0xe3, 0xdf, 0xb7, 0x6f, 0xc2, 0x82, 0x36, 0xba, 0x17, 0xcc, 0xe3, 0x00, 0xc8, 0xbe, 0x17, 0x27, 0x8f,
0x20, 0x1e, 0x6b, 0x1a, 0xf3, 0x15, 0xa8, 0x8f, 0xbc, 0x00, 0x47, 0xc6, 0x59, 0xb1, 0xea, 0xd4, 0x83, 0x78, 0xac, 0x69, 0xcc, 0x57, 0xa0, 0x3e, 0xf2, 0x02, 0x1c, 0x19, 0x67, 0xc5, 0xaa, 0x53,
0x46, 0x5e, 0xc0, 0xc6, 0x15, 0x63, 0xa5, 0xfb, 0x4c, 0x54, 0x96, 0x44, 0xa5, 0xfb, 0x0c, 0x2b, 0x1b, 0x79, 0x01, 0x1b, 0x57, 0x8c, 0x95, 0xee, 0x33, 0x51, 0x59, 0x12, 0x95, 0xee, 0x33, 0xac,
0xed, 0x77, 0x60, 0xd1, 0x68, 0x4f, 0x74, 0xfd, 0x3a, 0x54, 0x27, 0xc9, 0xb3, 0x50, 0xda, 0xb3, 0xb4, 0xdf, 0x81, 0x45, 0xa3, 0x3d, 0xd1, 0xf5, 0xeb, 0x50, 0x9d, 0x24, 0xcf, 0x42, 0x69, 0xcf,
0x86, 0xe0, 0x10, 0xe6, 0x19, 0x39, 0xbc, 0xc6, 0x7e, 0x0f, 0x16, 0x0e, 0xe8, 0xb9, 0xe0, 0x4c, 0x1a, 0x82, 0x43, 0x98, 0x67, 0xe4, 0xf0, 0x1a, 0xfb, 0x3d, 0x58, 0x38, 0xa0, 0xe7, 0x82, 0x33,
0x39, 0x90, 0x37, 0x2e, 0xf5, 0x9a, 0xb0, 0xde, 0xbe, 0x05, 0x44, 0xff, 0x58, 0xf4, 0xaa, 0xf9, 0xe5, 0x40, 0xde, 0xb8, 0xd4, 0x6b, 0xc2, 0x7a, 0xfb, 0x16, 0x10, 0xfd, 0x63, 0xd1, 0xab, 0xe6,
0x50, 0x96, 0xe1, 0x43, 0xd9, 0x6f, 0x00, 0x39, 0xf2, 0x86, 0xc1, 0xfb, 0x34, 0x8e, 0xdd, 0xa1, 0x43, 0x59, 0x86, 0x0f, 0x65, 0xbf, 0x01, 0xe4, 0xc8, 0x1b, 0x06, 0xef, 0xd3, 0x38, 0x76, 0x87,
0x52, 0x6a, 0x6d, 0x28, 0x8f, 0xe2, 0xa1, 0x90, 0x3d, 0xf6, 0xaf, 0xfd, 0x69, 0x58, 0x34, 0xe8, 0x4a, 0xa9, 0xb5, 0xa1, 0x3c, 0x8a, 0x87, 0x42, 0xf6, 0xd8, 0xbf, 0xf6, 0xa7, 0x61, 0xd1, 0xa0,
0x44, 0xc3, 0xd7, 0xa0, 0x1e, 0x7b, 0xc3, 0xc0, 0x4d, 0x26, 0x11, 0x15, 0x4d, 0xa7, 0x80, 0x7d, 0x13, 0x0d, 0x5f, 0x83, 0x7a, 0xec, 0x0d, 0x03, 0x37, 0x99, 0x44, 0x54, 0x34, 0x9d, 0x02, 0xf6,
0x0f, 0x96, 0xbe, 0x42, 0x23, 0xef, 0xe4, 0xe2, 0xb2, 0xe6, 0xcd, 0x76, 0x4a, 0xd9, 0x76, 0x76, 0x3d, 0x58, 0xfa, 0x0a, 0x8d, 0xbc, 0x93, 0x8b, 0xcb, 0x9a, 0x37, 0xdb, 0x29, 0x65, 0xdb, 0xd9,
0x61, 0x39, 0xd3, 0x8e, 0xe8, 0x9e, 0xb3, 0xaf, 0xd8, 0xc9, 0x9a, 0xc3, 0x0b, 0x9a, 0x30, 0x97, 0x85, 0xe5, 0x4c, 0x3b, 0xa2, 0x7b, 0xce, 0xbe, 0x62, 0x27, 0x6b, 0x0e, 0x2f, 0x68, 0xc2, 0x5c,
0x74, 0x61, 0xb6, 0x1f, 0x03, 0xd9, 0x0e, 0x83, 0x80, 0xf6, 0x93, 0x43, 0x4a, 0xa3, 0xf4, 0xd4, 0xd2, 0x85, 0xd9, 0x7e, 0x0c, 0x64, 0x3b, 0x0c, 0x02, 0xda, 0x4f, 0x0e, 0x29, 0x8d, 0xd2, 0x53,
0x94, 0xf2, 0x6a, 0xe3, 0xce, 0xaa, 0x58, 0xd9, 0xac, 0x86, 0x10, 0x4c, 0x4c, 0xa0, 0x32, 0xa6, 0x53, 0xca, 0xab, 0x8d, 0x3b, 0xab, 0x62, 0x65, 0xb3, 0x1a, 0x42, 0x30, 0x31, 0x81, 0xca, 0x98,
0xd1, 0x08, 0x1b, 0xae, 0x39, 0xf8, 0xbf, 0xbd, 0x0c, 0x8b, 0x46, 0xb3, 0xc2, 0xe1, 0x7d, 0x0b, 0x46, 0x23, 0x6c, 0xb8, 0xe6, 0xe0, 0xff, 0xf6, 0x32, 0x2c, 0x1a, 0xcd, 0x0a, 0x87, 0xf7, 0x2d,
0x96, 0x77, 0xbc, 0xb8, 0x9f, 0xef, 0xb0, 0x03, 0xb3, 0xe3, 0xc9, 0x71, 0x2f, 0x95, 0x44, 0x59, 0x58, 0xde, 0xf1, 0xe2, 0x7e, 0xbe, 0xc3, 0x0e, 0xcc, 0x8e, 0x27, 0xc7, 0xbd, 0x54, 0x12, 0x65,
0x64, 0x3e, 0x52, 0xf6, 0x13, 0xd1, 0xd8, 0xcf, 0x5b, 0x50, 0xd9, 0x7b, 0xb4, 0xbf, 0xcd, 0x94, 0x91, 0xf9, 0x48, 0xd9, 0x4f, 0x44, 0x63, 0x3f, 0x6b, 0x41, 0x65, 0xef, 0xd1, 0xfe, 0x36, 0x53,
0x9f, 0x17, 0xf4, 0xc3, 0x11, 0x33, 0x20, 0x7c, 0xd2, 0xaa, 0x3c, 0x55, 0xc2, 0xae, 0x41, 0x1d, 0x7e, 0x5e, 0xd0, 0x0f, 0x47, 0xcc, 0x80, 0xf0, 0x49, 0xab, 0xf2, 0x54, 0x09, 0xbb, 0x06, 0x75,
0xed, 0x0e, 0x73, 0xfb, 0xc4, 0x01, 0x27, 0x05, 0x98, 0xcb, 0x49, 0x9f, 0x8d, 0xbd, 0x08, 0x7d, 0xb4, 0x3b, 0xcc, 0xed, 0x13, 0x07, 0x9c, 0x14, 0x60, 0x2e, 0x27, 0x7d, 0x36, 0xf6, 0x22, 0xf4,
0x4a, 0xe9, 0x29, 0x56, 0x50, 0x6f, 0xe6, 0x2b, 0xec, 0xef, 0x56, 0x61, 0x56, 0x58, 0x13, 0xec, 0x29, 0xa5, 0xa7, 0x58, 0x41, 0xbd, 0x99, 0xaf, 0xb0, 0xbf, 0x53, 0x85, 0x59, 0x61, 0x4d, 0xb0,
0xaf, 0x9f, 0x78, 0x67, 0x54, 0x8c, 0x44, 0x94, 0x98, 0x4d, 0x8f, 0xe8, 0x28, 0x4c, 0x68, 0xcf, 0xbf, 0x7e, 0xe2, 0x9d, 0x51, 0x31, 0x12, 0x51, 0x62, 0x36, 0x3d, 0xa2, 0xa3, 0x30, 0xa1, 0x3d,
0xd8, 0x06, 0x13, 0x44, 0x97, 0x9a, 0x37, 0xd4, 0xe3, 0x4e, 0x78, 0x99, 0x53, 0x19, 0x20, 0x5b, 0x63, 0x1b, 0x4c, 0x10, 0x5d, 0x6a, 0xde, 0x50, 0x8f, 0x3b, 0xe1, 0x65, 0x4e, 0x65, 0x80, 0x6c,
0x2c, 0xe9, 0x51, 0x54, 0xd0, 0xa3, 0x90, 0x45, 0xb6, 0x12, 0x7d, 0x77, 0xec, 0xf6, 0xbd, 0xe4, 0xb1, 0xa4, 0x47, 0x51, 0x41, 0x8f, 0x42, 0x16, 0xd9, 0x4a, 0xf4, 0xdd, 0xb1, 0xdb, 0xf7, 0x92,
0x42, 0xa8, 0x04, 0x55, 0x66, 0x6d, 0xfb, 0x61, 0xdf, 0xf5, 0x7b, 0xc7, 0xae, 0xef, 0x06, 0x7d, 0x0b, 0xa1, 0x12, 0x54, 0x99, 0xb5, 0xed, 0x87, 0x7d, 0xd7, 0xef, 0x1d, 0xbb, 0xbe, 0x1b, 0xf4,
0x2a, 0xdd, 0x75, 0x03, 0x64, 0xae, 0xab, 0x18, 0x92, 0x24, 0xe3, 0xee, 0x6d, 0x06, 0x65, 0x06, 0xa9, 0x74, 0xd7, 0x0d, 0x90, 0xb9, 0xae, 0x62, 0x48, 0x92, 0x8c, 0xbb, 0xb7, 0x19, 0x94, 0x19,
0xa9, 0x1f, 0x8e, 0x46, 0x5e, 0xc2, 0x3c, 0x5e, 0xf4, 0x86, 0xca, 0x8e, 0x86, 0xf0, 0xc3, 0x01, 0xa4, 0x7e, 0x38, 0x1a, 0x79, 0x09, 0xf3, 0x78, 0xd1, 0x1b, 0x2a, 0x3b, 0x1a, 0xc2, 0x0f, 0x07,
0x96, 0xce, 0xf9, 0xea, 0xd5, 0xe5, 0xe1, 0x40, 0x03, 0x59, 0x2b, 0xcc, 0xa5, 0x62, 0x6a, 0xec, 0x58, 0x3a, 0xe7, 0xab, 0x57, 0x97, 0x87, 0x03, 0x0d, 0x64, 0xad, 0x30, 0x97, 0x8a, 0xa9, 0xb1,
0xe9, 0x79, 0x07, 0x78, 0x2b, 0x29, 0xc2, 0xf6, 0x61, 0x12, 0xc4, 0x34, 0x49, 0x7c, 0x3a, 0x50, 0xa7, 0xe7, 0x1d, 0xe0, 0xad, 0xa4, 0x08, 0xdb, 0x87, 0x49, 0x10, 0xd3, 0x24, 0xf1, 0xe9, 0x40,
0x03, 0x6a, 0x20, 0x59, 0xbe, 0x82, 0xdc, 0x86, 0x45, 0xee, 0x84, 0xc7, 0x6e, 0x12, 0xc6, 0xa7, 0x0d, 0xa8, 0x81, 0x64, 0xf9, 0x0a, 0x72, 0x1b, 0x16, 0xb9, 0x13, 0x1e, 0xbb, 0x49, 0x18, 0x9f,
0x5e, 0xdc, 0x8b, 0x99, 0x3b, 0xdb, 0x44, 0xfa, 0xa2, 0x2a, 0xf2, 0x0e, 0xac, 0x66, 0xe0, 0x88, 0x7a, 0x71, 0x2f, 0x66, 0xee, 0x6c, 0x13, 0xe9, 0x8b, 0xaa, 0xc8, 0x3b, 0xb0, 0x9a, 0x81, 0x23,
0xf6, 0xa9, 0x77, 0x46, 0x07, 0x9d, 0x39, 0xfc, 0x6a, 0x5a, 0x35, 0x59, 0x83, 0x06, 0x3b, 0x7b, 0xda, 0xa7, 0xde, 0x19, 0x1d, 0x74, 0xe6, 0xf0, 0xab, 0x69, 0xd5, 0x64, 0x0d, 0x1a, 0xec, 0xec,
0x4c, 0xc6, 0x03, 0x97, 0x59, 0xe4, 0x16, 0xee, 0x83, 0x0e, 0x91, 0xb7, 0x60, 0x6e, 0x4c, 0xb9, 0x31, 0x19, 0x0f, 0x5c, 0x66, 0x91, 0x5b, 0xb8, 0x0f, 0x3a, 0x44, 0xde, 0x82, 0xb9, 0x31, 0xe5,
0x39, 0x3f, 0x4d, 0xfc, 0x7e, 0xdc, 0x99, 0x37, 0xb4, 0x1b, 0xe3, 0x5c, 0xc7, 0xa4, 0x60, 0x4c, 0xe6, 0xfc, 0x34, 0xf1, 0xfb, 0x71, 0x67, 0xde, 0xd0, 0x6e, 0x8c, 0x73, 0x1d, 0x93, 0x82, 0x31,
0xd9, 0x8f, 0xd1, 0x09, 0x75, 0x2f, 0x3a, 0x6d, 0x64, 0xb7, 0x14, 0x40, 0x19, 0x89, 0xbc, 0x33, 0x65, 0x3f, 0x46, 0x27, 0xd4, 0xbd, 0xe8, 0xb4, 0x91, 0xdd, 0x52, 0x00, 0x65, 0x24, 0xf2, 0xce,
0x37, 0xa1, 0x9d, 0x05, 0xae, 0xd0, 0x45, 0x91, 0x7d, 0xe7, 0x05, 0x5e, 0xe2, 0xb9, 0x49, 0x18, 0xdc, 0x84, 0x76, 0x16, 0xb8, 0x42, 0x17, 0x45, 0xf6, 0x9d, 0x17, 0x78, 0x89, 0xe7, 0x26, 0x61,
0x75, 0x08, 0xd6, 0xa5, 0x80, 0xfd, 0x5b, 0x16, 0x57, 0xbb, 0x82, 0x45, 0x95, 0xfa, 0x7c, 0x0d, 0xd4, 0x21, 0x58, 0x97, 0x02, 0xf6, 0x6f, 0x59, 0x5c, 0xed, 0x0a, 0x16, 0x55, 0xea, 0xf3, 0x35,
0x1a, 0x9c, 0x39, 0x7b, 0x61, 0xe0, 0x5f, 0x08, 0x7e, 0x05, 0x0e, 0x3d, 0x0c, 0xfc, 0x0b, 0xf2, 0x68, 0x70, 0xe6, 0xec, 0x85, 0x81, 0x7f, 0x21, 0xf8, 0x15, 0x38, 0xf4, 0x30, 0xf0, 0x2f, 0xc8,
0x09, 0x98, 0xf3, 0x02, 0x9d, 0x84, 0x4b, 0x78, 0x53, 0x82, 0x48, 0xf4, 0x1a, 0x34, 0xc6, 0x93, 0x27, 0x60, 0xce, 0x0b, 0x74, 0x12, 0x2e, 0xe1, 0x4d, 0x09, 0x22, 0xd1, 0x6b, 0xd0, 0x18, 0x4f,
0x63, 0xdf, 0xeb, 0x73, 0x92, 0x32, 0x6f, 0x85, 0x43, 0x48, 0xc0, 0x9c, 0x41, 0x3e, 0x4e, 0x4e, 0x8e, 0x7d, 0xaf, 0xcf, 0x49, 0xca, 0xbc, 0x15, 0x0e, 0x21, 0x01, 0x73, 0x06, 0xf9, 0x38, 0x39,
0x51, 0x41, 0x8a, 0x86, 0xc0, 0x18, 0x89, 0x7d, 0x17, 0x96, 0xcc, 0x01, 0x0a, 0x55, 0xb6, 0x01, 0x45, 0x05, 0x29, 0x1a, 0x02, 0x63, 0x24, 0xf6, 0x5d, 0x58, 0x32, 0x07, 0x28, 0x54, 0xd9, 0x06,
0x35, 0xc1, 0xf9, 0x71, 0xa7, 0x81, 0xab, 0xd7, 0x12, 0xab, 0x27, 0x48, 0x1d, 0x55, 0x6f, 0xff, 0xd4, 0x04, 0xe7, 0xc7, 0x9d, 0x06, 0xae, 0x5e, 0x4b, 0xac, 0x9e, 0x20, 0x75, 0x54, 0xbd, 0xfd,
0x41, 0x05, 0x16, 0x05, 0xba, 0xed, 0x87, 0x31, 0x3d, 0x9a, 0x8c, 0x46, 0x6e, 0x54, 0x20, 0x52, 0x07, 0x15, 0x58, 0x14, 0xe8, 0xb6, 0x1f, 0xc6, 0xf4, 0x68, 0x32, 0x1a, 0xb9, 0x51, 0x81, 0x48,
0xd6, 0x25, 0x22, 0x55, 0x32, 0x45, 0x8a, 0x31, 0xfa, 0xa9, 0xeb, 0x05, 0xdc, 0x93, 0xe5, 0xf2, 0x59, 0x97, 0x88, 0x54, 0xc9, 0x14, 0x29, 0xc6, 0xe8, 0xa7, 0xae, 0x17, 0x70, 0x4f, 0x96, 0xcb,
0xa8, 0x21, 0x64, 0x1d, 0xe6, 0xfb, 0x7e, 0x18, 0x73, 0xaf, 0x4d, 0x3f, 0x74, 0x66, 0xe1, 0xbc, 0xa3, 0x86, 0x90, 0x75, 0x98, 0xef, 0xfb, 0x61, 0xcc, 0xbd, 0x36, 0xfd, 0xd0, 0x99, 0x85, 0xf3,
0x0a, 0xa8, 0x16, 0xa9, 0x00, 0x5d, 0x84, 0x67, 0x32, 0x22, 0x6c, 0x43, 0x93, 0x35, 0x4a, 0xa5, 0x2a, 0xa0, 0x5a, 0xa4, 0x02, 0x74, 0x11, 0x9e, 0xc9, 0x88, 0xb0, 0x0d, 0x4d, 0xd6, 0x28, 0x95,
0x46, 0x9a, 0xe5, 0x9e, 0x9c, 0x8e, 0xb1, 0xf1, 0x64, 0x05, 0x86, 0x4b, 0xe7, 0x7c, 0x91, 0xb8, 0x1a, 0x69, 0x96, 0x7b, 0x72, 0x3a, 0xc6, 0xc6, 0x93, 0x15, 0x18, 0x2e, 0x9d, 0xf3, 0x45, 0xe2,
0xb0, 0x33, 0x2d, 0xd3, 0x78, 0x1a, 0x75, 0x5d, 0x88, 0x4b, 0xbe, 0x8a, 0xdc, 0x03, 0xe0, 0x7d, 0xc2, 0xce, 0xb4, 0x4c, 0xe3, 0x69, 0xd4, 0x75, 0x21, 0x2e, 0xf9, 0x2a, 0x72, 0x0f, 0x80, 0xf7,
0xa1, 0xd9, 0x05, 0x34, 0xbb, 0x6f, 0x98, 0x3b, 0xa2, 0xaf, 0xfd, 0x2d, 0x56, 0x98, 0x44, 0x14, 0x85, 0x66, 0x17, 0xd0, 0xec, 0xbe, 0x61, 0xee, 0x88, 0xbe, 0xf6, 0xb7, 0x58, 0x61, 0x12, 0x51,
0x4d, 0xb1, 0xf6, 0xa5, 0xfd, 0x0b, 0x16, 0x34, 0xb4, 0x3a, 0xb2, 0x0c, 0x0b, 0xdb, 0x0f, 0x1f, 0x34, 0xc5, 0xda, 0x97, 0xf6, 0xcf, 0x5b, 0xd0, 0xd0, 0xea, 0xc8, 0x32, 0x2c, 0x6c, 0x3f, 0x7c,
0x1e, 0xee, 0x3a, 0x5b, 0x8f, 0x1e, 0x7c, 0x65, 0xb7, 0xb7, 0xbd, 0xff, 0xf0, 0x68, 0xb7, 0x7d, 0x78, 0xb8, 0xeb, 0x6c, 0x3d, 0x7a, 0xf0, 0x95, 0xdd, 0xde, 0xf6, 0xfe, 0xc3, 0xa3, 0xdd, 0xf6,
0x85, 0xc1, 0xfb, 0x0f, 0xb7, 0xb7, 0xf6, 0x7b, 0xf7, 0x1e, 0x3a, 0xdb, 0x12, 0xb6, 0xc8, 0x0a, 0x15, 0x06, 0xef, 0x3f, 0xdc, 0xde, 0xda, 0xef, 0xdd, 0x7b, 0xe8, 0x6c, 0x4b, 0xd8, 0x22, 0x2b,
0x10, 0x67, 0xf7, 0xfd, 0x87, 0x8f, 0x76, 0x0d, 0xbc, 0x44, 0xda, 0xd0, 0xbc, 0xeb, 0xec, 0x6e, 0x40, 0x9c, 0xdd, 0xf7, 0x1f, 0x3e, 0xda, 0x35, 0xf0, 0x12, 0x69, 0x43, 0xf3, 0xae, 0xb3, 0xbb,
0x6d, 0xef, 0x09, 0xa4, 0x4c, 0x96, 0xa0, 0x7d, 0xef, 0xf1, 0xc1, 0xce, 0x83, 0x83, 0xfb, 0xbd, 0xb5, 0xbd, 0x27, 0x90, 0x32, 0x59, 0x82, 0xf6, 0xbd, 0xc7, 0x07, 0x3b, 0x0f, 0x0e, 0xee, 0xf7,
0xed, 0xad, 0x83, 0xed, 0xdd, 0xfd, 0xdd, 0x9d, 0x76, 0x85, 0xcc, 0x41, 0x7d, 0xeb, 0xee, 0xd6, 0xb6, 0xb7, 0x0e, 0xb6, 0x77, 0xf7, 0x77, 0x77, 0xda, 0x15, 0x32, 0x07, 0xf5, 0xad, 0xbb, 0x5b,
0xc1, 0xce, 0xc3, 0x83, 0xdd, 0x9d, 0x76, 0xd5, 0xfe, 0x3b, 0x0b, 0x96, 0x71, 0xd4, 0x83, 0xac, 0x07, 0x3b, 0x0f, 0x0f, 0x76, 0x77, 0xda, 0x55, 0xfb, 0xef, 0x2c, 0x58, 0xc6, 0x51, 0x0f, 0xb2,
0x80, 0xac, 0x41, 0xa3, 0x1f, 0x86, 0x63, 0xca, 0xb4, 0xbd, 0x52, 0xe8, 0x3a, 0xc4, 0x98, 0x9f, 0x02, 0xb2, 0x06, 0x8d, 0x7e, 0x18, 0x8e, 0x29, 0xd3, 0xf6, 0x4a, 0xa1, 0xeb, 0x10, 0x63, 0x7e,
0xab, 0xcf, 0x93, 0x30, 0xea, 0x53, 0x21, 0x1f, 0x80, 0xd0, 0x3d, 0x86, 0x30, 0xe6, 0x17, 0xdb, 0xae, 0x3e, 0x4f, 0xc2, 0xa8, 0x4f, 0x85, 0x7c, 0x00, 0x42, 0xf7, 0x18, 0xc2, 0x98, 0x5f, 0x6c,
0xcb, 0x29, 0xb8, 0x78, 0x34, 0x38, 0xc6, 0x49, 0x56, 0x60, 0xe6, 0x38, 0xa2, 0x6e, 0xff, 0x54, 0x2f, 0xa7, 0xe0, 0xe2, 0xd1, 0xe0, 0x18, 0x27, 0x59, 0x81, 0x99, 0xe3, 0x88, 0xba, 0xfd, 0x53,
0x48, 0x86, 0x28, 0x91, 0x4f, 0xa6, 0x07, 0x8c, 0x3e, 0x5b, 0x7d, 0x9f, 0x0e, 0x90, 0x63, 0x6a, 0x21, 0x19, 0xa2, 0x44, 0x3e, 0x99, 0x1e, 0x30, 0xfa, 0x6c, 0xf5, 0x7d, 0x3a, 0x40, 0x8e, 0xa9,
0xce, 0xbc, 0xc0, 0xb7, 0x05, 0xcc, 0xe4, 0xdf, 0x3d, 0x76, 0x83, 0x41, 0x18, 0xd0, 0x81, 0x70, 0x39, 0xf3, 0x02, 0xdf, 0x16, 0x30, 0x93, 0x7f, 0xf7, 0xd8, 0x0d, 0x06, 0x61, 0x40, 0x07, 0xc2,
0xf6, 0x52, 0xc0, 0x3e, 0x84, 0x95, 0xec, 0xfc, 0x84, 0x7c, 0xbd, 0xad, 0xc9, 0x17, 0xf7, 0xbd, 0xd9, 0x4b, 0x01, 0xfb, 0x10, 0x56, 0xb2, 0xf3, 0x13, 0xf2, 0xf5, 0xb6, 0x26, 0x5f, 0xdc, 0xf7,
0xba, 0xd3, 0x77, 0x53, 0x93, 0xb5, 0x7f, 0xb6, 0xa0, 0xc2, 0x4c, 0xf1, 0x74, 0xb3, 0xad, 0x7b, 0xea, 0x4e, 0xdf, 0x4d, 0x4d, 0xd6, 0xfe, 0xd9, 0x82, 0x0a, 0x33, 0xc5, 0xd3, 0xcd, 0xb6, 0xee,
0x57, 0xe5, 0x5c, 0x84, 0x0a, 0xcf, 0x2c, 0x5c, 0x39, 0x73, 0x03, 0xa6, 0x21, 0x69, 0x7d, 0x44, 0x5d, 0x95, 0x73, 0x11, 0x2a, 0x3c, 0xb3, 0x70, 0xe5, 0xcc, 0x0d, 0x98, 0x86, 0xa4, 0xf5, 0x11,
0xfb, 0x67, 0x38, 0x63, 0x55, 0xcf, 0x10, 0x26, 0x20, 0xcc, 0xf5, 0xc5, 0xaf, 0x85, 0x80, 0xc8, 0xed, 0x9f, 0xe1, 0x8c, 0x55, 0x3d, 0x43, 0x98, 0x80, 0x30, 0xd7, 0x17, 0xbf, 0x16, 0x02, 0x22,
0xb2, 0xac, 0xc3, 0x2f, 0x67, 0xd3, 0x3a, 0xfc, 0xae, 0x03, 0xb3, 0x5e, 0x70, 0x1c, 0x4e, 0x82, 0xcb, 0xb2, 0x0e, 0xbf, 0x9c, 0x4d, 0xeb, 0xf0, 0xbb, 0x0e, 0xcc, 0x7a, 0xc1, 0x71, 0x38, 0x09,
0x01, 0x0a, 0x44, 0xcd, 0x91, 0x45, 0x8c, 0x89, 0xa1, 0xa0, 0x7a, 0x23, 0xc9, 0xfe, 0x29, 0x60, 0x06, 0x28, 0x10, 0x35, 0x47, 0x16, 0x31, 0x26, 0x86, 0x82, 0xea, 0x8d, 0x24, 0xfb, 0xa7, 0x80,
0x13, 0x76, 0x34, 0x8a, 0xd1, 0xf5, 0x50, 0xe1, 0x99, 0xb7, 0x61, 0x41, 0xc3, 0x52, 0x37, 0x76, 0x4d, 0xd8, 0xd1, 0x28, 0x46, 0xd7, 0x43, 0x85, 0x67, 0xde, 0x86, 0x05, 0x0d, 0x4b, 0xdd, 0xd8,
0xcc, 0x80, 0x8c, 0x1b, 0x8b, 0x3e, 0x0b, 0xaf, 0xb1, 0xdb, 0xd0, 0xba, 0x4f, 0x93, 0x07, 0xc1, 0x31, 0x03, 0x32, 0x6e, 0x2c, 0xfa, 0x2c, 0xbc, 0xc6, 0x6e, 0x43, 0xeb, 0x3e, 0x4d, 0x1e, 0x04,
0x49, 0x28, 0x5b, 0xfa, 0xdd, 0x0a, 0xcc, 0x2b, 0x48, 0x34, 0xb4, 0x0e, 0xf3, 0xde, 0x80, 0x06, 0x27, 0xa1, 0x6c, 0xe9, 0x77, 0x2b, 0x30, 0xaf, 0x20, 0xd1, 0xd0, 0x3a, 0xcc, 0x7b, 0x03, 0x1a,
0x89, 0x97, 0x5c, 0xf4, 0x8c, 0x13, 0x58, 0x16, 0x66, 0xbe, 0x9e, 0xeb, 0x7b, 0xae, 0x8c, 0x02, 0x24, 0x5e, 0x72, 0xd1, 0x33, 0x4e, 0x60, 0x59, 0x98, 0xf9, 0x7a, 0xae, 0xef, 0xb9, 0x32, 0x0a,
0xf2, 0x02, 0xb9, 0x03, 0x4b, 0xcc, 0x10, 0x49, 0xdb, 0xa2, 0xb6, 0x98, 0x1f, 0xfc, 0x0a, 0xeb, 0xc8, 0x0b, 0xe4, 0x0e, 0x2c, 0x31, 0x43, 0x24, 0x6d, 0x8b, 0xda, 0x62, 0x7e, 0xf0, 0x2b, 0xac,
0x98, 0x32, 0x60, 0xb8, 0xd0, 0xf6, 0xea, 0x13, 0xee, 0xf3, 0x14, 0x55, 0xb1, 0x55, 0xe3, 0x2d, 0x63, 0xca, 0x80, 0xe1, 0x42, 0xdb, 0xab, 0x4f, 0xb8, 0xcf, 0x53, 0x54, 0xc5, 0x56, 0x8d, 0xb7,
0xb1, 0x29, 0x57, 0xb9, 0xb1, 0x52, 0x40, 0x2e, 0xcc, 0x36, 0xc3, 0x55, 0x55, 0x36, 0xcc, 0xa6, 0xc4, 0xa6, 0x5c, 0xe5, 0xc6, 0x4a, 0x01, 0xb9, 0x30, 0xdb, 0x0c, 0x57, 0x55, 0xd9, 0x30, 0x9b,
0x85, 0xea, 0x6a, 0xb9, 0x50, 0x1d, 0x53, 0x65, 0x17, 0x41, 0x9f, 0x0e, 0x7a, 0x49, 0xd8, 0x43, 0x16, 0xaa, 0xab, 0xe5, 0x42, 0x75, 0x4c, 0x95, 0x5d, 0x04, 0x7d, 0x3a, 0xe8, 0x25, 0x61, 0x0f,
0x95, 0x8b, 0xbb, 0x53, 0x73, 0xb2, 0x30, 0xb9, 0x06, 0xb3, 0x09, 0x8d, 0x93, 0x80, 0x26, 0xa8, 0x55, 0x2e, 0xee, 0x4e, 0xcd, 0xc9, 0xc2, 0xe4, 0x1a, 0xcc, 0x26, 0x34, 0x4e, 0x02, 0x9a, 0xa0,
0x95, 0x6a, 0x18, 0x10, 0x90, 0x10, 0x73, 0x50, 0x27, 0x91, 0x17, 0x77, 0x9a, 0x18, 0x84, 0xc3, 0x56, 0xaa, 0x61, 0x40, 0x40, 0x42, 0xcc, 0x41, 0x9d, 0x44, 0x5e, 0xdc, 0x69, 0x62, 0x10, 0x0e,
0xff, 0xc9, 0x67, 0x60, 0xf9, 0x98, 0xc6, 0x49, 0xef, 0x94, 0xba, 0x03, 0x1a, 0xe1, 0x4e, 0xf3, 0xff, 0x27, 0x9f, 0x81, 0xe5, 0x63, 0x1a, 0x27, 0xbd, 0x53, 0xea, 0x0e, 0x68, 0x84, 0x3b, 0xcd,
0x68, 0x1f, 0xb7, 0xfb, 0xc5, 0x95, 0x8c, 0x87, 0xce, 0x68, 0x14, 0x7b, 0x61, 0x80, 0x16, 0xbf, 0xa3, 0x7d, 0xdc, 0xee, 0x17, 0x57, 0x32, 0x1e, 0x3a, 0xa3, 0x51, 0xec, 0x85, 0x01, 0x5a, 0xfc,
0xee, 0xc8, 0x22, 0x6b, 0x8f, 0x4d, 0x5e, 0xd9, 0x4b, 0xb5, 0x82, 0xf3, 0x38, 0xf1, 0xe2, 0x4a, 0xba, 0x23, 0x8b, 0xac, 0x3d, 0x36, 0x79, 0x65, 0x2f, 0xd5, 0x0a, 0xce, 0xe3, 0xc4, 0x8b, 0x2b,
0x72, 0x03, 0x66, 0x70, 0x02, 0x71, 0xa7, 0x6d, 0x44, 0x35, 0xb6, 0x19, 0xe8, 0x88, 0xba, 0x2f, 0xc9, 0x0d, 0x98, 0xc1, 0x09, 0xc4, 0x9d, 0xb6, 0x11, 0xd5, 0xd8, 0x66, 0xa0, 0x23, 0xea, 0xbe,
0x56, 0x6a, 0x8d, 0x76, 0xd3, 0xfe, 0x1c, 0x54, 0x11, 0x66, 0x9b, 0xce, 0x17, 0x83, 0x33, 0x05, 0x58, 0xa9, 0x35, 0xda, 0x4d, 0xfb, 0x73, 0x50, 0x45, 0x98, 0x6d, 0x3a, 0x5f, 0x0c, 0xce, 0x14,
0x2f, 0xb0, 0xa1, 0x05, 0x34, 0x39, 0x0f, 0xa3, 0xa7, 0x32, 0x24, 0x2c, 0x8a, 0xf6, 0xb7, 0xd0, 0xbc, 0xc0, 0x86, 0x16, 0xd0, 0xe4, 0x3c, 0x8c, 0x9e, 0xca, 0x90, 0xb0, 0x28, 0xda, 0xdf, 0x42,
0xc5, 0x57, 0x21, 0xd2, 0xc7, 0xe8, 0x9f, 0xb0, 0x83, 0x1a, 0x5f, 0xea, 0xf8, 0xd4, 0x15, 0xa7, 0x17, 0x5f, 0x85, 0x48, 0x1f, 0xa3, 0x7f, 0xc2, 0x0e, 0x6a, 0x7c, 0xa9, 0xe3, 0x53, 0x57, 0x9c,
0x8e, 0x1a, 0x02, 0x47, 0xa7, 0x2e, 0x53, 0x5b, 0xc6, 0xee, 0xf1, 0x83, 0x5c, 0x03, 0xb1, 0x3d, 0x3a, 0x6a, 0x08, 0x1c, 0x9d, 0xba, 0x4c, 0x6d, 0x19, 0xbb, 0xc7, 0x0f, 0x72, 0x0d, 0xc4, 0xf6,
0xbe, 0x79, 0x37, 0xa0, 0x25, 0x83, 0xaf, 0x71, 0xcf, 0xa7, 0x27, 0x89, 0x8c, 0x2b, 0x04, 0x93, 0xf8, 0xe6, 0xdd, 0x80, 0x96, 0x0c, 0xbe, 0xc6, 0x3d, 0x9f, 0x9e, 0x24, 0x32, 0xae, 0x10, 0x4c,
0x11, 0x9e, 0xf6, 0xf6, 0xe9, 0x49, 0x62, 0x1f, 0xc0, 0x82, 0x50, 0x25, 0x0f, 0xc7, 0x54, 0x76, 0x46, 0x78, 0xda, 0xdb, 0xa7, 0x27, 0x89, 0x7d, 0x00, 0x0b, 0x42, 0x95, 0x3c, 0x1c, 0x53, 0xd9,
0xfd, 0xf9, 0x22, 0x93, 0xdc, 0xb8, 0xb3, 0x68, 0xea, 0x1e, 0x1e, 0x6e, 0x36, 0x29, 0x6d, 0x07, 0xf5, 0xe7, 0x8b, 0x4c, 0x72, 0xe3, 0xce, 0xa2, 0xa9, 0x7b, 0x78, 0xb8, 0xd9, 0xa4, 0xb4, 0x1d,
0x88, 0xae, 0x9a, 0x44, 0x83, 0xc2, 0x2e, 0xca, 0xc8, 0x89, 0x98, 0x8e, 0x81, 0xb1, 0xf5, 0x89, 0x20, 0xba, 0x6a, 0x12, 0x0d, 0x0a, 0xbb, 0x28, 0x23, 0x27, 0x62, 0x3a, 0x06, 0xc6, 0xd6, 0x27,
0x27, 0xfd, 0xbe, 0x0c, 0x99, 0xb3, 0xe3, 0x30, 0x2f, 0xda, 0xbf, 0x67, 0xc1, 0x22, 0xb6, 0x26, 0x9e, 0xf4, 0xfb, 0x32, 0x64, 0xce, 0x8e, 0xc3, 0xbc, 0x68, 0xff, 0x9e, 0x05, 0x8b, 0xd8, 0x9a,
0x9d, 0x0a, 0xa1, 0xfe, 0xdf, 0xf9, 0x18, 0xc3, 0x6c, 0xf6, 0xf5, 0x68, 0xd2, 0x12, 0x54, 0x75, 0x74, 0x2a, 0x84, 0xfa, 0x7f, 0xe7, 0x63, 0x0c, 0xb3, 0xd9, 0xd7, 0xa3, 0x49, 0x4b, 0x50, 0xd5,
0x83, 0xc0, 0x0b, 0x1f, 0xff, 0x50, 0x5f, 0xc9, 0x1e, 0xea, 0xed, 0x5f, 0xb7, 0x60, 0x81, 0xeb, 0x0d, 0x02, 0x2f, 0x7c, 0xfc, 0x43, 0x7d, 0x25, 0x7b, 0xa8, 0xb7, 0x7f, 0xdd, 0x82, 0x05, 0xae,
0xe4, 0xc4, 0x4d, 0x26, 0xb1, 0x98, 0xfe, 0xff, 0x82, 0x39, 0x6e, 0x5c, 0x85, 0x54, 0x8b, 0x81, 0x93, 0x13, 0x37, 0x99, 0xc4, 0x62, 0xfa, 0xff, 0x0b, 0xe6, 0xb8, 0x71, 0x15, 0x52, 0x2d, 0x06,
0x2e, 0x29, 0x05, 0x84, 0x28, 0x27, 0xde, 0xbb, 0xe2, 0x98, 0xc4, 0xe4, 0x3d, 0x74, 0x70, 0x82, 0xba, 0xa4, 0x14, 0x10, 0xa2, 0x9c, 0x78, 0xef, 0x8a, 0x63, 0x12, 0x93, 0xf7, 0xd0, 0xc1, 0x09,
0x1e, 0xa2, 0x22, 0x30, 0x78, 0xb5, 0xc0, 0x0c, 0xa8, 0xef, 0x35, 0xf2, 0xbb, 0x35, 0x98, 0xe1, 0x7a, 0x88, 0x8a, 0xc0, 0xe0, 0xd5, 0x02, 0x33, 0xa0, 0xbe, 0xd7, 0xc8, 0xef, 0xd6, 0x60, 0x86,
0xfe, 0xae, 0x7d, 0x1f, 0xe6, 0x8c, 0x8e, 0x8c, 0x80, 0x42, 0x93, 0x07, 0x14, 0x72, 0xa1, 0xa8, 0xfb, 0xbb, 0xf6, 0x7d, 0x98, 0x33, 0x3a, 0x32, 0x02, 0x0a, 0x4d, 0x1e, 0x50, 0xc8, 0x85, 0xa2,
0x52, 0x41, 0x28, 0xea, 0xf7, 0xcb, 0x40, 0x18, 0xb3, 0x64, 0x76, 0x83, 0x39, 0xdc, 0xe1, 0xc0, 0x4a, 0x05, 0xa1, 0xa8, 0xdf, 0x2f, 0x03, 0x61, 0xcc, 0x92, 0xd9, 0x0d, 0xe6, 0x70, 0x87, 0x03,
0x38, 0x3e, 0x35, 0x1d, 0x1d, 0x22, 0xb7, 0x80, 0x68, 0x45, 0x19, 0x51, 0xe4, 0xd6, 0xa7, 0xa0, 0xe3, 0xf8, 0xd4, 0x74, 0x74, 0x88, 0xdc, 0x02, 0xa2, 0x15, 0x65, 0x44, 0x91, 0x5b, 0x9f, 0x82,
0x86, 0xa9, 0x49, 0x61, 0xbc, 0x85, 0x99, 0x15, 0x07, 0x45, 0xbe, 0xec, 0x85, 0x75, 0xcc, 0xc0, 0x1a, 0xa6, 0x26, 0x85, 0xf1, 0x16, 0x66, 0x56, 0x1c, 0x14, 0xf9, 0xb2, 0x17, 0xd6, 0x31, 0x03,
0x8c, 0x27, 0xf1, 0x29, 0x5e, 0xae, 0x88, 0x03, 0x96, 0x2c, 0x67, 0xf7, 0x77, 0xe6, 0xd2, 0xfd, 0x33, 0x9e, 0xc4, 0xa7, 0x78, 0xb9, 0x22, 0x0e, 0x58, 0xb2, 0x9c, 0xdd, 0xdf, 0x99, 0x4b, 0xf7,
0x9d, 0xcd, 0x05, 0x6d, 0x34, 0x17, 0xbf, 0x66, 0xba, 0xf8, 0x37, 0x60, 0x6e, 0xc4, 0x5c, 0xce, 0x77, 0x36, 0x17, 0xb4, 0xd1, 0x5c, 0xfc, 0x9a, 0xe9, 0xe2, 0xdf, 0x80, 0xb9, 0x11, 0x73, 0x39,
0xc4, 0xef, 0xf7, 0x46, 0xac, 0x77, 0x71, 0x9e, 0x32, 0x40, 0xb2, 0x01, 0x6d, 0xe1, 0x6e, 0xa4, 0x13, 0xbf, 0xdf, 0x1b, 0xb1, 0xde, 0xc5, 0x79, 0xca, 0x00, 0xc9, 0x06, 0xb4, 0x85, 0xbb, 0x91,
0xe7, 0x08, 0xc0, 0x35, 0xce, 0xe1, 0x4c, 0x7f, 0xa7, 0x61, 0x9c, 0x06, 0x0e, 0x36, 0x05, 0xd8, 0x9e, 0x23, 0x00, 0xd7, 0x38, 0x87, 0x33, 0xfd, 0x9d, 0x86, 0x71, 0x1a, 0x38, 0xd8, 0x14, 0x60,
0xc9, 0x2b, 0x66, 0x1c, 0xd2, 0x9b, 0x04, 0xe2, 0x7e, 0x85, 0x0e, 0xf0, 0x24, 0x55, 0x73, 0xf2, 0x27, 0xaf, 0x98, 0x71, 0x48, 0x6f, 0x12, 0x88, 0xfb, 0x15, 0x3a, 0xc0, 0x93, 0x54, 0xcd, 0xc9,
0x15, 0xf6, 0xaf, 0x5a, 0xd0, 0x66, 0x7b, 0x66, 0xb0, 0xe5, 0xbb, 0x80, 0x52, 0xf1, 0x92, 0x5c, 0x57, 0xd8, 0xbf, 0x62, 0x41, 0x9b, 0xed, 0x99, 0xc1, 0x96, 0xef, 0x02, 0x4a, 0xc5, 0x4b, 0x72,
0x69, 0xd0, 0x92, 0x77, 0xa0, 0x8e, 0xe5, 0x70, 0x4c, 0x03, 0xc1, 0x93, 0x1d, 0x93, 0x27, 0x53, 0xa5, 0x41, 0x4b, 0xde, 0x81, 0x3a, 0x96, 0xc3, 0x31, 0x0d, 0x04, 0x4f, 0x76, 0x4c, 0x9e, 0x4c,
0x7d, 0xb2, 0x77, 0xc5, 0x49, 0x89, 0x35, 0x8e, 0xfc, 0x2b, 0x0b, 0x1a, 0xa2, 0x97, 0x1f, 0x39, 0xf5, 0xc9, 0xde, 0x15, 0x27, 0x25, 0xd6, 0x38, 0xf2, 0xaf, 0x2c, 0x68, 0x88, 0x5e, 0x7e, 0xe8,
0x4c, 0xd0, 0xd5, 0x2e, 0xc4, 0x38, 0x27, 0xa5, 0xf7, 0x5f, 0xeb, 0x30, 0x3f, 0x72, 0x93, 0x49, 0x30, 0x41, 0x57, 0xbb, 0x10, 0xe3, 0x9c, 0x94, 0xde, 0x7f, 0xad, 0xc3, 0xfc, 0xc8, 0x4d, 0x26,
0xc4, 0xec, 0xb1, 0x11, 0x22, 0xc8, 0xc2, 0xcc, 0xb8, 0xa2, 0xea, 0x8c, 0x7b, 0x89, 0xe7, 0xf7, 0x11, 0xb3, 0xc7, 0x46, 0x88, 0x20, 0x0b, 0x33, 0xe3, 0x8a, 0xaa, 0x33, 0xee, 0x25, 0x9e, 0xdf,
0x64, 0xad, 0xb8, 0x7a, 0x2a, 0xaa, 0x62, 0x1a, 0x24, 0x4e, 0xdc, 0x21, 0x15, 0x76, 0x93, 0x17, 0x93, 0xb5, 0xe2, 0xea, 0xa9, 0xa8, 0x8a, 0x69, 0x90, 0x38, 0x71, 0x87, 0x54, 0xd8, 0x4d, 0x5e,
0xec, 0x0e, 0xac, 0x88, 0x09, 0x65, 0x5c, 0x55, 0xfb, 0x4f, 0x9a, 0xb0, 0x9a, 0xab, 0x52, 0xf7, 0xb0, 0x3b, 0xb0, 0x22, 0x26, 0x94, 0x71, 0x55, 0xed, 0x3f, 0x69, 0xc2, 0x6a, 0xae, 0x4a, 0xdd,
0xd3, 0xe2, 0xec, 0xeb, 0x7b, 0xa3, 0xe3, 0x50, 0xf9, 0xf9, 0x96, 0x7e, 0x2c, 0x36, 0xaa, 0xc8, 0x4f, 0x8b, 0xb3, 0xaf, 0xef, 0x8d, 0x8e, 0x43, 0xe5, 0xe7, 0x5b, 0xfa, 0xb1, 0xd8, 0xa8, 0x22,
0x10, 0x96, 0xa5, 0x83, 0xc0, 0xd6, 0x34, 0x35, 0x66, 0x25, 0xb4, 0x52, 0x6f, 0x99, 0x5b, 0x98, 0x43, 0x58, 0x96, 0x0e, 0x02, 0x5b, 0xd3, 0xd4, 0x98, 0x95, 0xd0, 0x4a, 0xbd, 0x65, 0x6e, 0x61,
0xed, 0x50, 0xe2, 0xba, 0x10, 0x17, 0xb7, 0x47, 0x4e, 0xa1, 0xa3, 0x3c, 0x11, 0xa1, 0xac, 0x35, 0xb6, 0x43, 0x89, 0xeb, 0x42, 0x5c, 0xdc, 0x1e, 0x39, 0x85, 0x8e, 0xf2, 0x44, 0x84, 0xb2, 0xd6,
0x6f, 0x85, 0xf5, 0xf5, 0xe6, 0x25, 0x7d, 0x19, 0x9e, 0xad, 0x33, 0xb5, 0x35, 0x72, 0x01, 0xd7, 0xbc, 0x15, 0xd6, 0xd7, 0x9b, 0x97, 0xf4, 0x65, 0x78, 0xb6, 0xce, 0xd4, 0xd6, 0xc8, 0x05, 0x5c,
0x65, 0x1d, 0x6a, 0xe3, 0x7c, 0x7f, 0x95, 0x97, 0x9a, 0x1b, 0xfa, 0xec, 0x66, 0xa7, 0x97, 0x34, 0x97, 0x75, 0xa8, 0x8d, 0xf3, 0xfd, 0x55, 0x5e, 0x6a, 0x6e, 0xe8, 0xb3, 0x9b, 0x9d, 0x5e, 0xd2,
0x4c, 0x3e, 0x84, 0x95, 0x73, 0xd7, 0x4b, 0xe4, 0xb0, 0x34, 0xdf, 0xa0, 0x8a, 0x5d, 0xde, 0xb9, 0x30, 0xf9, 0x10, 0x56, 0xce, 0x5d, 0x2f, 0x91, 0xc3, 0xd2, 0x7c, 0x83, 0x2a, 0x76, 0x79, 0xe7,
0xa4, 0xcb, 0x27, 0xfc, 0x63, 0xc3, 0x44, 0x4d, 0x69, 0xb1, 0xfb, 0x17, 0x16, 0xb4, 0xcc, 0x76, 0x92, 0x2e, 0x9f, 0xf0, 0x8f, 0x0d, 0x13, 0x35, 0xa5, 0xc5, 0xee, 0x5f, 0x58, 0xd0, 0x32, 0xdb,
0x18, 0x9b, 0x0a, 0xd9, 0x97, 0x3a, 0x50, 0x7a, 0x93, 0x19, 0x38, 0x7f, 0x54, 0x2e, 0x15, 0x1d, 0x61, 0x6c, 0x2a, 0x64, 0x5f, 0xea, 0x40, 0xe9, 0x4d, 0x66, 0xe0, 0xfc, 0x51, 0xb9, 0x54, 0x74,
0x95, 0xf5, 0x03, 0x6a, 0xf9, 0xb2, 0x18, 0x53, 0xe5, 0xe5, 0x62, 0x4c, 0xd5, 0xa2, 0x18, 0x53, 0x54, 0xd6, 0x0f, 0xa8, 0xe5, 0xcb, 0x62, 0x4c, 0x95, 0x97, 0x8b, 0x31, 0x55, 0x8b, 0x62, 0x4c,
0xf7, 0xdf, 0x2d, 0x20, 0x79, 0x5e, 0x22, 0xf7, 0xf9, 0x59, 0x3d, 0xa0, 0xbe, 0x50, 0x29, 0xff, 0xdd, 0x7f, 0xb7, 0x80, 0xe4, 0x79, 0x89, 0xdc, 0xe7, 0x67, 0xf5, 0x80, 0xfa, 0x42, 0xa5, 0xfc,
0xf3, 0xe5, 0xf8, 0x51, 0xae, 0x9d, 0xfc, 0x9a, 0x09, 0x86, 0x7e, 0x77, 0xac, 0x3b, 0x3b, 0x73, 0xcf, 0x97, 0xe3, 0x47, 0xb9, 0x76, 0xf2, 0x6b, 0x26, 0x18, 0xfa, 0xdd, 0xb1, 0xee, 0xec, 0xcc,
0x4e, 0x51, 0x55, 0x26, 0xea, 0x55, 0xb9, 0x3c, 0xea, 0x55, 0xbd, 0x3c, 0xea, 0x35, 0x93, 0x8d, 0x39, 0x45, 0x55, 0x99, 0xa8, 0x57, 0xe5, 0xf2, 0xa8, 0x57, 0xf5, 0xf2, 0xa8, 0xd7, 0x4c, 0x36,
0x7a, 0x75, 0x7f, 0xce, 0x82, 0xc5, 0x82, 0x4d, 0xff, 0xc9, 0x4d, 0x9c, 0x6d, 0x93, 0xa1, 0x0b, 0xea, 0xd5, 0xfd, 0x19, 0x0b, 0x16, 0x0b, 0x36, 0xfd, 0xc7, 0x37, 0x71, 0xb6, 0x4d, 0x86, 0x2e,
0x4a, 0x62, 0x9b, 0x74, 0xb0, 0xfb, 0x53, 0x30, 0x67, 0x30, 0xfa, 0x4f, 0xae, 0xff, 0xac, 0xbf, 0x28, 0x89, 0x6d, 0xd2, 0xc1, 0xee, 0x4f, 0xc0, 0x9c, 0xc1, 0xe8, 0x3f, 0xbe, 0xfe, 0xb3, 0xfe,
0xc6, 0xf9, 0xcc, 0xc0, 0xba, 0xff, 0x52, 0x02, 0x92, 0x17, 0xb6, 0xff, 0xd6, 0x31, 0xe4, 0xd7, 0x1a, 0xe7, 0x33, 0x03, 0xeb, 0xfe, 0x4b, 0x09, 0x48, 0x5e, 0xd8, 0xfe, 0x5b, 0xc7, 0x90, 0x5f,
0xa9, 0x5c, 0xb0, 0x4e, 0xff, 0xa5, 0x76, 0xe0, 0x4d, 0x58, 0x10, 0xc9, 0x2c, 0x5a, 0x84, 0x86, 0xa7, 0x72, 0xc1, 0x3a, 0xfd, 0x97, 0xda, 0x81, 0x37, 0x61, 0x41, 0x24, 0xb3, 0x68, 0x11, 0x1a,
0x73, 0x4c, 0xbe, 0x82, 0x79, 0xac, 0x66, 0xc8, 0xb1, 0x66, 0x24, 0x08, 0x68, 0xc6, 0x30, 0x13, 0xce, 0x31, 0xf9, 0x0a, 0xe6, 0xb1, 0x9a, 0x21, 0xc7, 0x9a, 0x91, 0x20, 0xa0, 0x19, 0xc3, 0x4c,
0x79, 0xb4, 0xbb, 0xd0, 0x11, 0x2b, 0xb4, 0x7b, 0x46, 0x83, 0xe4, 0x68, 0x72, 0xcc, 0x33, 0x42, 0xe4, 0xd1, 0xee, 0x42, 0x47, 0xac, 0xd0, 0xee, 0x19, 0x0d, 0x92, 0xa3, 0xc9, 0x31, 0xcf, 0x08,
0xbc, 0x30, 0xb0, 0x7f, 0x50, 0x56, 0x4e, 0x37, 0x56, 0x0a, 0xf3, 0xfe, 0x19, 0x68, 0xea, 0xca, 0xf1, 0xc2, 0xc0, 0xfe, 0x7e, 0x59, 0x39, 0xdd, 0x58, 0x29, 0xcc, 0xfb, 0x67, 0xa0, 0xa9, 0x2b,
0x5c, 0x6c, 0x47, 0x26, 0x40, 0xc7, 0x0c, 0xbb, 0x4e, 0x45, 0x76, 0xa0, 0x85, 0x2a, 0x6b, 0xa0, 0x73, 0xb1, 0x1d, 0x99, 0x00, 0x1d, 0x33, 0xec, 0x3a, 0x15, 0xd9, 0x81, 0x16, 0xaa, 0xac, 0x81,
0xbe, 0x2b, 0xe1, 0x77, 0x2f, 0x08, 0x3c, 0xec, 0x5d, 0x71, 0x32, 0xdf, 0x90, 0x2f, 0x40, 0xcb, 0xfa, 0xae, 0x84, 0xdf, 0xbd, 0x20, 0xf0, 0xb0, 0x77, 0xc5, 0xc9, 0x7c, 0x43, 0xbe, 0x00, 0x2d,
0x3c, 0x4a, 0x09, 0x1f, 0xa1, 0xc8, 0x37, 0x67, 0x9f, 0x9b, 0xc4, 0x64, 0x0b, 0xda, 0xd9, 0xb3, 0xf3, 0x28, 0x25, 0x7c, 0x84, 0x22, 0xdf, 0x9c, 0x7d, 0x6e, 0x12, 0x93, 0x2d, 0x68, 0x67, 0xcf,
0x98, 0xb8, 0x2d, 0x9e, 0xd2, 0x40, 0x8e, 0x9c, 0xbc, 0x23, 0xee, 0x9e, 0xaa, 0x18, 0x04, 0xbb, 0x62, 0xe2, 0xb6, 0x78, 0x4a, 0x03, 0x39, 0x72, 0xf2, 0x8e, 0xb8, 0x7b, 0xaa, 0x62, 0x10, 0xec,
0x61, 0x7e, 0xa6, 0x2d, 0xd3, 0x2d, 0xfe, 0x47, 0xbb, 0x8d, 0xfa, 0x3a, 0x40, 0x8a, 0x91, 0x36, 0x86, 0xf9, 0x99, 0xb6, 0x4c, 0xb7, 0xf8, 0x1f, 0xed, 0x36, 0xea, 0xeb, 0x00, 0x29, 0x46, 0xda,
0x34, 0x1f, 0x1e, 0xee, 0x1e, 0xf4, 0xb6, 0xf7, 0xb6, 0x0e, 0x0e, 0x76, 0xf7, 0xdb, 0x57, 0x08, 0xd0, 0x7c, 0x78, 0xb8, 0x7b, 0xd0, 0xdb, 0xde, 0xdb, 0x3a, 0x38, 0xd8, 0xdd, 0x6f, 0x5f, 0x21,
0x81, 0x16, 0xc6, 0xaf, 0x76, 0x14, 0x66, 0x31, 0x6c, 0x6b, 0x9b, 0xc7, 0xc6, 0x04, 0x56, 0x22, 0x04, 0x5a, 0x18, 0xbf, 0xda, 0x51, 0x98, 0xc5, 0xb0, 0xad, 0x6d, 0x1e, 0x1b, 0x13, 0x58, 0x89,
0x4b, 0xd0, 0x7e, 0x70, 0x90, 0x41, 0xcb, 0x77, 0xeb, 0x4a, 0x3e, 0xec, 0x15, 0x58, 0xe2, 0x09, 0x2c, 0x41, 0xfb, 0xc1, 0x41, 0x06, 0x2d, 0xdf, 0xad, 0x2b, 0xf9, 0xb0, 0x57, 0x60, 0x89, 0x27,
0x4f, 0x77, 0x39, 0x7b, 0x48, 0x5f, 0xe1, 0x37, 0x2d, 0x58, 0xce, 0x54, 0xa4, 0x89, 0x07, 0xdc, 0x3c, 0xdd, 0xe5, 0xec, 0x21, 0x7d, 0x85, 0xdf, 0xb4, 0x60, 0x39, 0x53, 0x91, 0x26, 0x1e, 0x70,
0x1d, 0x30, 0x7d, 0x04, 0x13, 0x64, 0x3c, 0xa9, 0x3c, 0xbf, 0x8c, 0x06, 0xc9, 0x57, 0x30, 0x9e, 0x77, 0xc0, 0xf4, 0x11, 0x4c, 0x90, 0xf1, 0xa4, 0xf2, 0xfc, 0x32, 0x1a, 0x24, 0x5f, 0xc1, 0x78,
0xd7, 0x3c, 0xc5, 0x8c, 0x24, 0x15, 0x55, 0xd9, 0xab, 0x3c, 0x2d, 0x2b, 0xa0, 0x7e, 0x66, 0xe0, 0x5e, 0xf3, 0x14, 0x33, 0x92, 0x54, 0x54, 0x65, 0xaf, 0xf2, 0xb4, 0xac, 0x80, 0xfa, 0x99, 0x81,
0x27, 0x3c, 0x91, 0x4a, 0xaf, 0x48, 0xef, 0xf2, 0xcc, 0x21, 0xcb, 0x22, 0x73, 0xf2, 0x0d, 0xd7, 0x9f, 0xf0, 0x44, 0x2a, 0xbd, 0x22, 0xbd, 0xcb, 0x33, 0x87, 0x2c, 0x8b, 0xcc, 0xc9, 0x37, 0x5c,
0xc3, 0x1c, 0x6f, 0x61, 0x9d, 0xfd, 0xa7, 0x25, 0x20, 0x5f, 0x9e, 0xd0, 0xe8, 0x02, 0x73, 0x06, 0x0f, 0x73, 0xbc, 0x85, 0x75, 0xf6, 0x9f, 0x96, 0x80, 0x7c, 0x79, 0x42, 0xa3, 0x0b, 0xcc, 0x19,
0x54, 0x38, 0x70, 0x35, 0x1b, 0xec, 0x9a, 0x19, 0x4f, 0x8e, 0xbf, 0x44, 0x2f, 0x64, 0x42, 0x4b, 0x50, 0xe1, 0xc0, 0xd5, 0x6c, 0xb0, 0x6b, 0x66, 0x3c, 0x39, 0xfe, 0x12, 0xbd, 0x90, 0x09, 0x2d,
0x49, 0x4f, 0x68, 0x01, 0x76, 0x38, 0x56, 0x19, 0x0b, 0xd6, 0x7a, 0x15, 0x43, 0x12, 0xf5, 0x60, 0x25, 0x3d, 0xa1, 0x05, 0xd8, 0xe1, 0x58, 0x65, 0x2c, 0x58, 0xeb, 0x55, 0x0c, 0x49, 0xd4, 0x83,
0x32, 0xe2, 0x8d, 0x16, 0xe6, 0x9d, 0x54, 0x2e, 0xcf, 0x3b, 0xa9, 0x5e, 0x96, 0x77, 0xf2, 0x09, 0xc9, 0x88, 0x37, 0x5a, 0x98, 0x77, 0x52, 0xb9, 0x3c, 0xef, 0xa4, 0x7a, 0x59, 0xde, 0xc9, 0x27,
0x98, 0xf3, 0x86, 0x41, 0xc8, 0xd4, 0x02, 0x33, 0xec, 0x71, 0x67, 0x66, 0xad, 0xcc, 0x0e, 0xc3, 0x60, 0xce, 0x1b, 0x06, 0x21, 0x53, 0x0b, 0xcc, 0xb0, 0xc7, 0x9d, 0x99, 0xb5, 0x32, 0x3b, 0x0c,
0x02, 0x3c, 0x60, 0x18, 0xf9, 0x5c, 0x4a, 0x44, 0x07, 0x43, 0xcc, 0x61, 0xd2, 0x15, 0xc5, 0xee, 0x0b, 0xf0, 0x80, 0x61, 0xe4, 0x73, 0x29, 0x11, 0x1d, 0x0c, 0x31, 0x87, 0x49, 0x57, 0x14, 0xbb,
0x60, 0x48, 0xf7, 0xc3, 0xbe, 0x9b, 0x84, 0x91, 0xfa, 0x90, 0x61, 0x31, 0x3b, 0xf5, 0xc7, 0xe1, 0x83, 0x21, 0xdd, 0x0f, 0xfb, 0x6e, 0x12, 0x46, 0xea, 0x43, 0x86, 0xc5, 0xec, 0xd4, 0x1f, 0x87,
0x84, 0xb9, 0x39, 0x72, 0x29, 0x78, 0xd8, 0xa6, 0xc9, 0xd1, 0x43, 0x5c, 0x10, 0xfb, 0xab, 0xd0, 0x13, 0xe6, 0xe6, 0xc8, 0xa5, 0xe0, 0x61, 0x9b, 0x26, 0x47, 0x0f, 0x71, 0x41, 0xec, 0xaf, 0x42,
0xd0, 0x9a, 0x20, 0xaf, 0xf2, 0x13, 0x26, 0x73, 0x21, 0xc4, 0x79, 0xb0, 0xc2, 0x3d, 0xf6, 0x80, 0x43, 0x6b, 0x82, 0xbc, 0xca, 0x4f, 0x98, 0xcc, 0x85, 0x10, 0xe7, 0xc1, 0x0a, 0xf7, 0xd8, 0x03,
0xfa, 0x0f, 0x06, 0xe4, 0x53, 0xb0, 0x30, 0xf0, 0x22, 0x8a, 0xb9, 0x4a, 0xbd, 0x88, 0x9e, 0xd1, 0xea, 0x3f, 0x18, 0x90, 0x4f, 0xc1, 0xc2, 0xc0, 0x8b, 0x28, 0xe6, 0x2a, 0xf5, 0x22, 0x7a, 0x46,
0x28, 0x96, 0x27, 0xe7, 0xb6, 0xaa, 0x70, 0x38, 0x6e, 0xbf, 0x07, 0x8b, 0xc6, 0xd6, 0x28, 0xce, 0xa3, 0x58, 0x9e, 0x9c, 0xdb, 0xaa, 0xc2, 0xe1, 0xb8, 0xfd, 0x1e, 0x2c, 0x1a, 0x5b, 0xa3, 0x38,
0x95, 0xe9, 0x21, 0x56, 0x3e, 0x3d, 0x44, 0xa6, 0x86, 0xd8, 0xdf, 0x2e, 0x41, 0x79, 0x2f, 0x1c, 0x57, 0xa6, 0x87, 0x58, 0xf9, 0xf4, 0x10, 0x99, 0x1a, 0x62, 0xff, 0x5c, 0x09, 0xca, 0x7b, 0xe1,
0xeb, 0xd1, 0x7e, 0xcb, 0x8c, 0xf6, 0x0b, 0x17, 0xa8, 0xa7, 0x3c, 0x1c, 0x61, 0x19, 0x0d, 0x90, 0x58, 0x8f, 0xf6, 0x5b, 0x66, 0xb4, 0x5f, 0xb8, 0x40, 0x3d, 0xe5, 0xe1, 0x08, 0xcb, 0x68, 0x80,
0x6c, 0x40, 0xcb, 0x1d, 0x25, 0xbd, 0x24, 0x64, 0x2e, 0xdf, 0xb9, 0x1b, 0x0d, 0x38, 0x3b, 0xe3, 0x64, 0x03, 0x5a, 0xee, 0x28, 0xe9, 0x25, 0x21, 0x73, 0xf9, 0xce, 0xdd, 0x68, 0xc0, 0xd9, 0x19,
0x16, 0x67, 0x6a, 0xc8, 0x12, 0x94, 0x95, 0xaf, 0x80, 0x04, 0xac, 0xc8, 0xce, 0x1b, 0x78, 0x8f, 0xb7, 0x38, 0x53, 0x43, 0x96, 0xa0, 0xac, 0x7c, 0x05, 0x24, 0x60, 0x45, 0x76, 0xde, 0xc0, 0x7b,
0x78, 0x21, 0x22, 0x67, 0xa2, 0xc4, 0xa4, 0xc5, 0xfc, 0x9e, 0x1f, 0xf6, 0xb8, 0xc6, 0x2f, 0xaa, 0xc4, 0x0b, 0x11, 0x39, 0x13, 0x25, 0x26, 0x2d, 0xe6, 0xf7, 0xfc, 0xb0, 0xc7, 0x35, 0x7e, 0x51,
0x62, 0xee, 0x18, 0xe3, 0x0e, 0x24, 0x13, 0x21, 0x4f, 0x59, 0xd6, 0xc3, 0xb3, 0x35, 0xf3, 0x56, 0x15, 0x73, 0xc7, 0x18, 0x77, 0x20, 0x99, 0x08, 0x79, 0xca, 0xb2, 0x1e, 0x9e, 0xad, 0x99, 0xb7,
0xf5, 0x9f, 0x2c, 0xa8, 0xe2, 0xda, 0x30, 0xeb, 0xc5, 0xc5, 0x5b, 0x05, 0xfc, 0x71, 0x4d, 0xe6, 0xaa, 0xff, 0x64, 0x41, 0x15, 0xd7, 0x86, 0x59, 0x2f, 0x2e, 0xde, 0x2a, 0xe0, 0x8f, 0x6b, 0x32,
0x9c, 0x2c, 0x4c, 0x6c, 0x23, 0xeb, 0xad, 0xa4, 0x26, 0xa4, 0x67, 0xbe, 0xad, 0x41, 0x9d, 0x97, 0xe7, 0x64, 0x61, 0x62, 0x1b, 0x59, 0x6f, 0x25, 0x35, 0x21, 0x3d, 0xf3, 0x6d, 0x0d, 0xea, 0xbc,
0x54, 0x86, 0x17, 0xe7, 0x7b, 0x05, 0x92, 0xeb, 0x50, 0x39, 0x0d, 0xc7, 0xd2, 0xdd, 0x06, 0x79, 0xa4, 0x32, 0xbc, 0x38, 0xdf, 0x2b, 0x90, 0x5c, 0x87, 0xca, 0x69, 0x38, 0x96, 0xee, 0x36, 0xc8,
0x1b, 0x16, 0x8e, 0x1d, 0xc4, 0xd3, 0xf1, 0xb0, 0xf6, 0xf8, 0xb4, 0xb8, 0x13, 0x95, 0x85, 0x99, 0xdb, 0xb0, 0x70, 0xec, 0x20, 0x9e, 0x8e, 0x87, 0xb5, 0xc7, 0xa7, 0xc5, 0x9d, 0xa8, 0x2c, 0xcc,
0x1b, 0xa9, 0x9a, 0xd5, 0x97, 0x29, 0x83, 0xda, 0x1b, 0x30, 0xcf, 0xb8, 0x5e, 0x8b, 0xba, 0x4e, 0xdc, 0x48, 0xd5, 0xac, 0xbe, 0x4c, 0x19, 0xd4, 0xde, 0x80, 0x79, 0xc6, 0xf5, 0x5a, 0xd4, 0x75,
0x15, 0x65, 0xfb, 0xa7, 0x2d, 0xa8, 0x49, 0x62, 0xb2, 0x0e, 0x15, 0x26, 0x42, 0x99, 0x83, 0xab, 0xaa, 0x28, 0xdb, 0x3f, 0x69, 0x41, 0x4d, 0x12, 0x93, 0x75, 0xa8, 0x30, 0x11, 0xca, 0x1c, 0x5c,
0xba, 0x05, 0x67, 0x74, 0x0e, 0x52, 0x30, 0x67, 0x02, 0x83, 0x61, 0xe9, 0x39, 0x49, 0x86, 0xc2, 0xd5, 0x2d, 0x38, 0xa3, 0x73, 0x90, 0x82, 0x39, 0x13, 0x18, 0x0c, 0x4b, 0xcf, 0x49, 0x32, 0x14,
0xd2, 0x63, 0x80, 0x1a, 0x6e, 0xc6, 0x7b, 0xce, 0xa0, 0xf6, 0xf7, 0x2d, 0x98, 0x33, 0xfa, 0x20, 0x96, 0x1e, 0x03, 0xd4, 0x70, 0x33, 0xde, 0x73, 0x06, 0xb5, 0xbf, 0x67, 0xc1, 0x9c, 0xd1, 0x07,
0x6b, 0xd0, 0xf0, 0xdd, 0x38, 0x11, 0x37, 0x8b, 0x62, 0x7b, 0x74, 0x48, 0xdf, 0xe8, 0x92, 0x19, 0x59, 0x83, 0x86, 0xef, 0xc6, 0x89, 0xb8, 0x59, 0x14, 0xdb, 0xa3, 0x43, 0xfa, 0x46, 0x97, 0xcc,
0x87, 0x57, 0x11, 0xe2, 0xb2, 0x1e, 0x21, 0xbe, 0x0d, 0xf5, 0x34, 0x37, 0xb1, 0x62, 0xc8, 0x3e, 0x38, 0xbc, 0x8a, 0x10, 0x97, 0xf5, 0x08, 0xf1, 0x6d, 0xa8, 0xa7, 0xb9, 0x89, 0x15, 0x43, 0xf6,
0xeb, 0x51, 0xde, 0xef, 0xa7, 0x44, 0x18, 0x74, 0x0c, 0xfd, 0x30, 0x12, 0x97, 0x56, 0xbc, 0x60, 0x59, 0x8f, 0xf2, 0x7e, 0x3f, 0x25, 0xc2, 0xa0, 0x63, 0xe8, 0x87, 0x91, 0xb8, 0xb4, 0xe2, 0x05,
0xbf, 0x07, 0x0d, 0x8d, 0x5e, 0x8f, 0x41, 0x5a, 0x46, 0x0c, 0x52, 0x25, 0xbf, 0x94, 0xd2, 0xe4, 0xfb, 0x3d, 0x68, 0x68, 0xf4, 0x7a, 0x0c, 0xd2, 0x32, 0x62, 0x90, 0x2a, 0xf9, 0xa5, 0x94, 0x26,
0x17, 0xfb, 0x5f, 0x2d, 0x98, 0x63, 0x3c, 0xe8, 0x05, 0xc3, 0xc3, 0xd0, 0xf7, 0xfa, 0x17, 0xb8, 0xbf, 0xd8, 0xff, 0x6a, 0xc1, 0x1c, 0xe3, 0x41, 0x2f, 0x18, 0x1e, 0x86, 0xbe, 0xd7, 0xbf, 0xc0,
0xf7, 0x92, 0xdd, 0x84, 0x4a, 0x94, 0xbc, 0x68, 0xc2, 0x8c, 0xeb, 0x65, 0xe4, 0x43, 0x88, 0xa8, 0xbd, 0x97, 0xec, 0x26, 0x54, 0xa2, 0xe4, 0x45, 0x13, 0x66, 0x5c, 0x2f, 0x23, 0x1f, 0x42, 0x44,
0x2a, 0x33, 0x19, 0x66, 0x12, 0x70, 0xec, 0xc6, 0x42, 0x2c, 0x84, 0xd7, 0x66, 0x80, 0x4c, 0xd2, 0x55, 0x99, 0xc9, 0x30, 0x93, 0x80, 0x63, 0x37, 0x16, 0x62, 0x21, 0xbc, 0x36, 0x03, 0x64, 0x92,
0x18, 0x10, 0xb9, 0x09, 0xed, 0x8d, 0x3c, 0xdf, 0xf7, 0x38, 0x2d, 0xf7, 0xe9, 0x8b, 0xaa, 0x58, 0xc6, 0x80, 0xc8, 0x4d, 0x68, 0x6f, 0xe4, 0xf9, 0xbe, 0xc7, 0x69, 0xb9, 0x4f, 0x5f, 0x54, 0xc5,
0x9f, 0x03, 0x2f, 0x76, 0x8f, 0xd3, 0x8b, 0x18, 0x55, 0xc6, 0xf0, 0x8c, 0xfb, 0x4c, 0x0b, 0xcf, 0xfa, 0x1c, 0x78, 0xb1, 0x7b, 0x9c, 0x5e, 0xc4, 0xa8, 0x32, 0x86, 0x67, 0xdc, 0x67, 0x5a, 0x78,
0xcc, 0xa0, 0x5e, 0x31, 0x41, 0xfb, 0x8f, 0x4a, 0xd0, 0x90, 0x2e, 0xc2, 0x60, 0x48, 0xc5, 0xdd, 0x66, 0x06, 0xf5, 0x8a, 0x09, 0xda, 0x7f, 0x54, 0x82, 0x86, 0x74, 0x11, 0x06, 0x43, 0x2a, 0xee,
0xa2, 0xa9, 0x18, 0x35, 0x44, 0xd6, 0x1b, 0xa7, 0x31, 0x0d, 0xc9, 0x32, 0x46, 0x39, 0xcf, 0x18, 0x16, 0x4d, 0xc5, 0xa8, 0x21, 0xb2, 0xde, 0x38, 0x8d, 0x69, 0x48, 0x96, 0x31, 0xca, 0x79, 0xc6,
0xd7, 0xa0, 0xce, 0x18, 0xf4, 0x2d, 0x3c, 0xf6, 0x89, 0x74, 0x5f, 0x05, 0xc8, 0xda, 0x3b, 0x58, 0xb8, 0x06, 0x75, 0xc6, 0xa0, 0x6f, 0xe1, 0xb1, 0x4f, 0xa4, 0xfb, 0x2a, 0x40, 0xd6, 0xde, 0xc1,
0x5b, 0x4d, 0x6b, 0x11, 0x78, 0xe1, 0x4d, 0xe4, 0x3b, 0xd0, 0x14, 0xcd, 0xe0, 0xce, 0xa1, 0xe6, 0xda, 0x6a, 0x5a, 0x8b, 0xc0, 0x0b, 0x6f, 0x22, 0xdf, 0x81, 0xa6, 0x68, 0x06, 0x77, 0x0e, 0x35,
0x49, 0x45, 0xc4, 0xd8, 0x55, 0xc7, 0xa0, 0x94, 0x5f, 0xde, 0x91, 0x5f, 0xd6, 0x2e, 0xfb, 0x52, 0x4f, 0x2a, 0x22, 0xc6, 0xae, 0x3a, 0x06, 0xa5, 0xfc, 0xf2, 0x8e, 0xfc, 0xb2, 0x76, 0xd9, 0x97,
0x52, 0xda, 0xf7, 0xd5, 0x05, 0xef, 0xfd, 0xc8, 0x1d, 0x9f, 0x4a, 0x59, 0xbe, 0x0d, 0x8b, 0x5e, 0x92, 0xd2, 0xbe, 0xaf, 0x2e, 0x78, 0xef, 0x47, 0xee, 0xf8, 0x54, 0xca, 0xf2, 0x6d, 0x58, 0xf4,
0xd0, 0xf7, 0x27, 0x03, 0xda, 0x9b, 0x04, 0x6e, 0x10, 0x84, 0x93, 0xa0, 0x4f, 0x65, 0xf6, 0x4b, 0x82, 0xbe, 0x3f, 0x19, 0xd0, 0xde, 0x24, 0x70, 0x83, 0x20, 0x9c, 0x04, 0x7d, 0x2a, 0xb3, 0x5f,
0x51, 0x95, 0x3d, 0x50, 0xc9, 0x7f, 0xd8, 0x10, 0xd9, 0x80, 0x2a, 0x37, 0x95, 0xdc, 0x76, 0x14, 0x8a, 0xaa, 0xec, 0x81, 0x4a, 0xfe, 0xc3, 0x86, 0xc8, 0x06, 0x54, 0xb9, 0xa9, 0xe4, 0xb6, 0xa3,
0x0b, 0x3a, 0x27, 0x21, 0xeb, 0x50, 0xe5, 0x16, 0xb3, 0x64, 0x48, 0x8d, 0xb6, 0xab, 0x0e, 0x27, 0x58, 0xd0, 0x39, 0x09, 0x59, 0x87, 0x2a, 0xb7, 0x98, 0x25, 0x43, 0x6a, 0xb4, 0x5d, 0x75, 0x38,
0x60, 0x6a, 0x07, 0x13, 0x3c, 0x4d, 0xb5, 0x63, 0xda, 0x9d, 0x99, 0x3e, 0x4f, 0x01, 0x5d, 0x02, 0x01, 0x53, 0x3b, 0x98, 0xe0, 0x69, 0xaa, 0x1d, 0xd3, 0xee, 0xcc, 0xf4, 0x79, 0x0a, 0xe8, 0x12,
0x72, 0xc0, 0x25, 0x45, 0xbf, 0x1b, 0xfa, 0xd9, 0x32, 0x34, 0x34, 0x98, 0x69, 0x90, 0x21, 0x1b, 0x90, 0x03, 0x2e, 0x29, 0xfa, 0xdd, 0xd0, 0x4f, 0x97, 0xa1, 0xa1, 0xc1, 0x4c, 0x83, 0x0c, 0xd9,
0x70, 0x6f, 0xe0, 0xb9, 0x23, 0x9a, 0xd0, 0x48, 0x48, 0x47, 0x06, 0x65, 0x74, 0xee, 0xd9, 0xb0, 0x80, 0x7b, 0x03, 0xcf, 0x1d, 0xd1, 0x84, 0x46, 0x42, 0x3a, 0x32, 0x28, 0xa3, 0x73, 0xcf, 0x86,
0x17, 0x4e, 0x92, 0xde, 0x80, 0x0e, 0x23, 0xca, 0xad, 0x29, 0x33, 0x4d, 0x06, 0xca, 0xe8, 0x18, 0xbd, 0x70, 0x92, 0xf4, 0x06, 0x74, 0x18, 0x51, 0x6e, 0x4d, 0x99, 0x69, 0x32, 0x50, 0x46, 0xc7,
0x7f, 0x6a, 0x74, 0x9c, 0x83, 0x32, 0xa8, 0xbc, 0xe9, 0xe1, 0x6b, 0x54, 0x49, 0x6f, 0x7a, 0xf8, 0xf8, 0x53, 0xa3, 0xe3, 0x1c, 0x94, 0x41, 0xe5, 0x4d, 0x0f, 0x5f, 0xa3, 0x4a, 0x7a, 0xd3, 0xc3,
0x8a, 0x64, 0x75, 0x5f, 0xb5, 0x40, 0xf7, 0xbd, 0x0d, 0x2b, 0x5c, 0xcb, 0x09, 0x7d, 0xd0, 0xcb, 0x57, 0x24, 0xab, 0xfb, 0xaa, 0x05, 0xba, 0xef, 0x6d, 0x58, 0xe1, 0x5a, 0x4e, 0xe8, 0x83, 0x5e,
0x30, 0xd6, 0x94, 0x5a, 0xb2, 0x01, 0x6d, 0x36, 0x66, 0x29, 0x12, 0xb1, 0xf7, 0x2d, 0x1e, 0x35, 0x86, 0xb1, 0xa6, 0xd4, 0x92, 0x0d, 0x68, 0xb3, 0x31, 0x4b, 0x91, 0x88, 0xbd, 0x6f, 0xf1, 0xa8,
0xb5, 0x9c, 0x1c, 0xce, 0x68, 0x31, 0x7c, 0xa9, 0xd3, 0xf2, 0x9b, 0xef, 0x1c, 0x8e, 0xb4, 0xee, 0xa9, 0xe5, 0xe4, 0x70, 0x46, 0x8b, 0xe1, 0x4b, 0x9d, 0x96, 0xdf, 0x7c, 0xe7, 0x70, 0xa4, 0x75,
0x33, 0x93, 0xb6, 0x2e, 0x68, 0x33, 0xb8, 0x3d, 0x07, 0x8d, 0xa3, 0x24, 0x1c, 0xcb, 0x4d, 0x69, 0x9f, 0x99, 0xb4, 0x75, 0x41, 0x9b, 0xc1, 0xed, 0x39, 0x68, 0x1c, 0x25, 0xe1, 0x58, 0x6e, 0x4a,
0x41, 0x93, 0x17, 0x45, 0x16, 0xd2, 0x2b, 0x70, 0x15, 0xb9, 0xe8, 0x51, 0x38, 0x0e, 0xfd, 0x70, 0x0b, 0x9a, 0xbc, 0x28, 0xb2, 0x90, 0x5e, 0x81, 0xab, 0xc8, 0x45, 0x8f, 0xc2, 0x71, 0xe8, 0x87,
0x78, 0x61, 0x1c, 0x9d, 0xfe, 0xd2, 0x82, 0x45, 0xa3, 0x36, 0x3d, 0x3b, 0x61, 0xd4, 0x45, 0xa6, 0xc3, 0x0b, 0xe3, 0xe8, 0xf4, 0x97, 0x16, 0x2c, 0x1a, 0xb5, 0xe9, 0xd9, 0x09, 0xa3, 0x2e, 0x32,
0x8f, 0x70, 0xc6, 0x5b, 0xd0, 0x54, 0x30, 0x27, 0xe4, 0x01, 0xee, 0xc7, 0x22, 0xa3, 0x64, 0x0b, 0x7d, 0x84, 0x33, 0xde, 0x82, 0xa6, 0x82, 0x39, 0x21, 0x0f, 0x70, 0x3f, 0x16, 0x19, 0x25, 0x5b,
0xe6, 0xe5, 0xc8, 0xe4, 0x87, 0x9c, 0x0b, 0x3b, 0x79, 0x2e, 0x14, 0xdf, 0xb7, 0xc4, 0x07, 0xb2, 0x30, 0x2f, 0x47, 0x26, 0x3f, 0xe4, 0x5c, 0xd8, 0xc9, 0x73, 0xa1, 0xf8, 0xbe, 0x25, 0x3e, 0x90,
0x89, 0x2f, 0x88, 0x0c, 0x02, 0x7e, 0x94, 0x92, 0x41, 0x36, 0x75, 0xf8, 0xd2, 0x8f, 0xda, 0x72, 0x4d, 0x7c, 0x41, 0x64, 0x10, 0xf0, 0xa3, 0x94, 0x0c, 0xb2, 0xa9, 0xc3, 0x97, 0x7e, 0xd4, 0x96,
0x04, 0x7d, 0x05, 0xc6, 0xf6, 0x2f, 0x5a, 0x00, 0xe9, 0xe8, 0xf0, 0xde, 0x59, 0x99, 0x11, 0xfe, 0x23, 0xe8, 0x2b, 0x30, 0xb6, 0x7f, 0xc1, 0x02, 0x48, 0x47, 0x87, 0xf7, 0xce, 0xca, 0x8c, 0xf0,
0xdc, 0x45, 0x33, 0x19, 0xaf, 0x43, 0x53, 0xdd, 0x57, 0xa6, 0x96, 0xa9, 0x21, 0x31, 0xe6, 0x39, 0xe7, 0x2e, 0x9a, 0xc9, 0x78, 0x1d, 0x9a, 0xea, 0xbe, 0x32, 0xb5, 0x4c, 0x0d, 0x89, 0x31, 0xcf,
0xdf, 0x84, 0xf9, 0xa1, 0x1f, 0x1e, 0xa3, 0x59, 0xc7, 0xb4, 0xb6, 0x58, 0xe4, 0x62, 0xb5, 0x38, 0xf9, 0x26, 0xcc, 0x0f, 0xfd, 0xf0, 0x18, 0xcd, 0x3a, 0xa6, 0xb5, 0xc5, 0x22, 0x17, 0xab, 0xc5,
0x7c, 0x4f, 0xa0, 0xa9, 0x19, 0xab, 0x68, 0x66, 0xcc, 0xfe, 0xa5, 0x92, 0xba, 0x5e, 0x4a, 0xe7, 0xe1, 0x7b, 0x02, 0x4d, 0xcd, 0x58, 0x45, 0x33, 0x63, 0xf6, 0x2f, 0x96, 0xd4, 0xf5, 0x52, 0x3a,
0x3c, 0x55, 0xca, 0xc8, 0x9d, 0x9c, 0x3a, 0x9d, 0x72, 0x9b, 0x83, 0xce, 0xe9, 0xe1, 0xa5, 0xd1, 0xe7, 0xa9, 0x52, 0x46, 0xee, 0xe4, 0xd4, 0xe9, 0x94, 0xdb, 0x1c, 0x74, 0x4e, 0x0f, 0x2f, 0x8d,
0xae, 0xf7, 0xa0, 0x15, 0x71, 0x7d, 0x25, 0x95, 0x59, 0xe5, 0x05, 0xca, 0x6c, 0x2e, 0x32, 0x6c, 0x76, 0xbd, 0x07, 0xad, 0x88, 0xeb, 0x2b, 0xa9, 0xcc, 0x2a, 0x2f, 0x50, 0x66, 0x73, 0x91, 0x61,
0xdd, 0x27, 0xa1, 0xed, 0x0e, 0xce, 0x68, 0x94, 0x78, 0x18, 0x6f, 0x40, 0x47, 0x83, 0xab, 0xe0, 0xeb, 0x3e, 0x09, 0x6d, 0x77, 0x70, 0x46, 0xa3, 0xc4, 0xc3, 0x78, 0x03, 0x3a, 0x1a, 0x5c, 0x05,
0x79, 0x0d, 0x47, 0xfb, 0x7f, 0x13, 0xe6, 0x45, 0xfe, 0x9b, 0xa2, 0x14, 0xb9, 0xec, 0x29, 0xcc, 0xcf, 0x6b, 0x38, 0xda, 0xff, 0x9b, 0x30, 0x2f, 0xf2, 0xdf, 0x14, 0xa5, 0xc8, 0x65, 0x4f, 0x61,
0x08, 0xed, 0xdf, 0x96, 0x37, 0x59, 0xe6, 0x1e, 0x4e, 0x5f, 0x11, 0x7d, 0x76, 0xa5, 0xcc, 0xec, 0x46, 0x68, 0xff, 0xb6, 0xbc, 0xc9, 0x32, 0xf7, 0x70, 0xfa, 0x8a, 0xe8, 0xb3, 0x2b, 0x65, 0x66,
0x3e, 0x21, 0x6e, 0x95, 0x06, 0x32, 0xa8, 0x51, 0xd6, 0xb2, 0x4d, 0x06, 0xe2, 0x16, 0xd0, 0x5c, 0xf7, 0x09, 0x71, 0xab, 0x34, 0x90, 0x41, 0x8d, 0xb2, 0x96, 0x6d, 0x32, 0x10, 0xb7, 0x80, 0xe6,
0xd2, 0xca, 0xcb, 0x2c, 0xa9, 0xfd, 0x43, 0x0b, 0x66, 0xf7, 0xc2, 0xf1, 0x9e, 0xc8, 0xbb, 0x41, 0x92, 0x56, 0x5e, 0x66, 0x49, 0xed, 0x1f, 0x58, 0x30, 0xbb, 0x17, 0x8e, 0xf7, 0x44, 0xde, 0x0d,
0x41, 0x50, 0x89, 0xa7, 0xb2, 0xf8, 0x82, 0x8c, 0x9c, 0x42, 0xfb, 0x3e, 0x97, 0xb5, 0xef, 0xff, 0x0a, 0x82, 0x4a, 0x3c, 0x95, 0xc5, 0x17, 0x64, 0xe4, 0x14, 0xda, 0xf7, 0xb9, 0xac, 0x7d, 0xff,
0x17, 0x5e, 0xc1, 0x90, 0x5a, 0x14, 0x8e, 0xc3, 0x88, 0x09, 0xa3, 0xeb, 0x73, 0x63, 0x1e, 0x06, 0xbf, 0xf0, 0x0a, 0x86, 0xd4, 0xa2, 0x70, 0x1c, 0x46, 0x4c, 0x18, 0x5d, 0x9f, 0x1b, 0xf3, 0x30,
0xc9, 0xa9, 0x54, 0x63, 0x2f, 0x22, 0xc1, 0x73, 0x2e, 0x3b, 0x9b, 0x71, 0xd7, 0x5c, 0xf8, 0x23, 0x48, 0x4e, 0xa5, 0x1a, 0x7b, 0x11, 0x09, 0x9e, 0x73, 0xd9, 0xd9, 0x8c, 0xbb, 0xe6, 0xc2, 0x1f,
0x5c, 0xbb, 0xe5, 0x2b, 0xec, 0xcf, 0x43, 0x1d, 0x1d, 0x6a, 0x9c, 0xd6, 0x9b, 0x50, 0x3f, 0x0d, 0xe1, 0xda, 0x2d, 0x5f, 0x61, 0x7f, 0x1e, 0xea, 0xe8, 0x50, 0xe3, 0xb4, 0xde, 0x84, 0xfa, 0x69,
0xc7, 0xbd, 0x53, 0x2f, 0x48, 0xa4, 0x70, 0xb7, 0x52, 0x4f, 0x77, 0x0f, 0x17, 0x44, 0x11, 0xd8, 0x38, 0xee, 0x9d, 0x7a, 0x41, 0x22, 0x85, 0xbb, 0x95, 0x7a, 0xba, 0x7b, 0xb8, 0x20, 0x8a, 0xc0,
0xdf, 0x9e, 0x81, 0xd9, 0x07, 0xc1, 0x59, 0xe8, 0xf5, 0xf1, 0xd6, 0x6c, 0x44, 0x47, 0xa1, 0x4c, 0xfe, 0xb5, 0x19, 0x98, 0x7d, 0x10, 0x9c, 0x85, 0x5e, 0x1f, 0x6f, 0xcd, 0x46, 0x74, 0x14, 0xca,
0xc3, 0x65, 0xff, 0x93, 0x6b, 0x30, 0x8b, 0x79, 0x67, 0x63, 0xce, 0xb4, 0x4d, 0x7e, 0xbb, 0x2d, 0x34, 0x5c, 0xf6, 0x3f, 0xb9, 0x06, 0xb3, 0x98, 0x77, 0x36, 0xe6, 0x4c, 0xdb, 0xe4, 0xb7, 0xdb,
0x20, 0xe6, 0x24, 0x44, 0xe9, 0x0b, 0x00, 0x2e, 0x3e, 0x1a, 0xc2, 0x8e, 0x1a, 0x91, 0x9e, 0xc1, 0x02, 0x62, 0x4e, 0x42, 0x94, 0xbe, 0x00, 0xe0, 0xe2, 0xa3, 0x21, 0xec, 0xa8, 0x11, 0xe9, 0x19,
0x2f, 0x4a, 0x69, 0x9a, 0x73, 0x55, 0x4b, 0x73, 0x66, 0x7d, 0x89, 0x3c, 0x21, 0x9e, 0x48, 0xc2, 0xfc, 0xa2, 0x94, 0xa6, 0x39, 0x57, 0xb5, 0x34, 0x67, 0xd6, 0x97, 0xc8, 0x13, 0xe2, 0x89, 0x24,
0xfb, 0x12, 0x10, 0x1e, 0x8f, 0x22, 0xca, 0x43, 0xa2, 0xe8, 0x72, 0xcc, 0x8a, 0xe3, 0x91, 0x0e, 0xbc, 0x2f, 0x01, 0xe1, 0xf1, 0x28, 0xa2, 0x3c, 0x24, 0x8a, 0x2e, 0xc7, 0xac, 0x38, 0x1e, 0xe9,
0x32, 0xb7, 0x84, 0x7f, 0xc0, 0x69, 0xb8, 0x12, 0xd6, 0x21, 0xe6, 0xe8, 0x65, 0x5f, 0x67, 0xd4, 0x20, 0x73, 0x4b, 0xf8, 0x07, 0x9c, 0x86, 0x2b, 0x61, 0x1d, 0x62, 0x8e, 0x5e, 0xf6, 0x75, 0x46,
0x39, 0xef, 0x67, 0x60, 0xa6, 0xa9, 0x07, 0x54, 0x29, 0x54, 0x3e, 0x0f, 0xe0, 0xaf, 0x1c, 0xb2, 0x9d, 0xf3, 0x7e, 0x06, 0x66, 0x9a, 0x7a, 0x40, 0x95, 0x42, 0xe5, 0xf3, 0x00, 0xfe, 0xca, 0x21,
0xb8, 0x76, 0xa8, 0xe2, 0x29, 0x82, 0xf2, 0x50, 0xc5, 0x18, 0xc6, 0xf5, 0xfd, 0x63, 0xb7, 0xff, 0x8b, 0x6b, 0x87, 0x2a, 0x9e, 0x22, 0x28, 0x0f, 0x55, 0x8c, 0x61, 0x5c, 0xdf, 0x3f, 0x76, 0xfb,
0x14, 0x1f, 0xdf, 0xe0, 0x3d, 0x56, 0xdd, 0x31, 0x41, 0xcc, 0xf6, 0x49, 0x77, 0x15, 0xf3, 0x00, 0x4f, 0xf1, 0xf1, 0x0d, 0xde, 0x63, 0xd5, 0x1d, 0x13, 0xc4, 0x6c, 0x9f, 0x74, 0x57, 0x31, 0x0f,
0x2a, 0x8e, 0x0e, 0x91, 0x3b, 0xd0, 0xc0, 0x83, 0xa4, 0xd8, 0xd7, 0x16, 0xee, 0x6b, 0x5b, 0x3f, 0xa0, 0xe2, 0xe8, 0x10, 0xb9, 0x03, 0x0d, 0x3c, 0x48, 0x8a, 0x7d, 0x6d, 0xe1, 0xbe, 0xb6, 0xf5,
0x69, 0xe2, 0xce, 0xea, 0x44, 0xfa, 0x8d, 0xde, 0x7c, 0x2e, 0x69, 0xcf, 0x1d, 0x0c, 0xc4, 0x45, 0x93, 0x26, 0xee, 0xac, 0x4e, 0xa4, 0xdf, 0xe8, 0xcd, 0xe7, 0x92, 0xf6, 0xdc, 0xc1, 0x40, 0x5c,
0x68, 0x9b, 0x1f, 0x8a, 0x15, 0xc0, 0xac, 0xaa, 0x58, 0x30, 0x4e, 0xb0, 0x80, 0x04, 0x06, 0x46, 0x84, 0xb6, 0xf9, 0xa1, 0x58, 0x01, 0xcc, 0xaa, 0x8a, 0x05, 0xe3, 0x04, 0x0b, 0x48, 0x60, 0x60,
0xae, 0x43, 0x8d, 0x1d, 0x72, 0xc6, 0xae, 0x37, 0xc0, 0xac, 0x3f, 0x7e, 0xd6, 0x52, 0x18, 0x6b, 0xe4, 0x3a, 0xd4, 0xd8, 0x21, 0x67, 0xec, 0x7a, 0x03, 0xcc, 0xfa, 0xe3, 0x67, 0x2d, 0x85, 0xb1,
0x43, 0xfe, 0x8f, 0x17, 0x96, 0x8b, 0xb8, 0x2a, 0x06, 0xc6, 0xd6, 0x46, 0x95, 0x51, 0x98, 0x96, 0x36, 0xe4, 0xff, 0x78, 0x61, 0xb9, 0x88, 0xab, 0x62, 0x60, 0x6c, 0x6d, 0x54, 0x19, 0x85, 0x69,
0xf8, 0x8e, 0x1a, 0x20, 0x79, 0x0b, 0xaf, 0xa3, 0x12, 0xda, 0x59, 0xc6, 0x70, 0xd7, 0x2b, 0x62, 0x89, 0xef, 0xa8, 0x01, 0x92, 0xb7, 0xf0, 0x3a, 0x2a, 0xa1, 0x9d, 0x65, 0x0c, 0x77, 0xbd, 0x22,
0xce, 0x82, 0x69, 0xe5, 0xdf, 0x23, 0x46, 0xe2, 0x70, 0x4a, 0xfb, 0xd3, 0xd0, 0xd4, 0x61, 0x52, 0xe6, 0x2c, 0x98, 0x56, 0xfe, 0x3d, 0x62, 0x24, 0x0e, 0xa7, 0xb4, 0xb7, 0xa0, 0xa9, 0xc3, 0xa4,
0x83, 0xca, 0xc3, 0xc3, 0xdd, 0x83, 0xf6, 0x15, 0xd2, 0x80, 0xd9, 0xa3, 0xdd, 0x47, 0x8f, 0xf6, 0x06, 0x95, 0x87, 0x87, 0xbb, 0x07, 0xed, 0x2b, 0xa4, 0x01, 0xb3, 0x47, 0xbb, 0x8f, 0x1e, 0xed,
0x77, 0x77, 0xda, 0x16, 0x69, 0x42, 0x4d, 0xa5, 0x66, 0x95, 0xec, 0x04, 0xc8, 0xd6, 0x60, 0x20, 0xef, 0xee, 0xb4, 0x2d, 0xd2, 0x84, 0x9a, 0x4a, 0xcd, 0x2a, 0xb1, 0xd2, 0xd6, 0xf6, 0xf6, 0xee,
0xbe, 0x53, 0x87, 0xfb, 0x94, 0x83, 0x2d, 0x83, 0x83, 0x0b, 0xb8, 0xa8, 0x54, 0xcc, 0x45, 0x2f, 0xe1, 0xa3, 0xdd, 0x9d, 0x76, 0xd9, 0x4e, 0x80, 0x6c, 0x0d, 0x06, 0xa2, 0x15, 0x75, 0xd4, 0x4f,
0x5c, 0x6b, 0x7b, 0x17, 0x1a, 0x87, 0xda, 0xd3, 0x14, 0x14, 0x28, 0xf9, 0x28, 0x45, 0x08, 0xa2, 0xf9, 0xd9, 0x32, 0xf8, 0xb9, 0x80, 0xa7, 0x4a, 0xc5, 0x3c, 0xf5, 0xc2, 0x95, 0xb7, 0x77, 0xa1,
0x86, 0x68, 0xc3, 0x29, 0xe9, 0xc3, 0xb1, 0x7f, 0xc7, 0xe2, 0xe9, 0xf2, 0x6a, 0xf8, 0xbc, 0x6f, 0x71, 0xa8, 0x3d, 0x54, 0x41, 0xf1, 0x92, 0x4f, 0x54, 0x84, 0x58, 0x6a, 0x88, 0x36, 0x9c, 0x92,
0x1b, 0x9a, 0x2a, 0xca, 0x94, 0xe6, 0x59, 0x1a, 0x18, 0xa3, 0xc1, 0xa1, 0xf4, 0xc2, 0x93, 0x93, 0x3e, 0x1c, 0xfb, 0x77, 0x2c, 0x9e, 0x3c, 0xaf, 0x86, 0xcf, 0xfb, 0xb6, 0xa1, 0xa9, 0x62, 0x4e,
0x98, 0xca, 0xac, 0x28, 0x03, 0x63, 0x92, 0xc0, 0x7c, 0x2a, 0xe6, 0x9f, 0x78, 0xbc, 0x87, 0x58, 0x69, 0xd6, 0xa5, 0x81, 0x31, 0x1a, 0x1c, 0x4a, 0x2f, 0x3c, 0x39, 0x89, 0xa9, 0xcc, 0x91, 0x32,
0x64, 0x47, 0xe5, 0x70, 0xa6, 0xd7, 0x45, 0x20, 0x45, 0xe6, 0x83, 0xa9, 0xb2, 0x4a, 0x07, 0xcd, 0x30, 0x26, 0x17, 0xcc, 0xc3, 0x62, 0xde, 0x8a, 0xc7, 0x7b, 0x88, 0x45, 0xae, 0x54, 0x0e, 0x67,
0xae, 0xf2, 0x06, 0xd4, 0x54, 0xbb, 0xa6, 0xca, 0x92, 0x94, 0xaa, 0x9e, 0xa9, 0x46, 0x3c, 0x65, 0x5a, 0x5e, 0x84, 0x55, 0x64, 0x76, 0x98, 0x2a, 0xab, 0xe4, 0xd0, 0xec, 0x2a, 0x6f, 0x40, 0x4d,
0x18, 0x83, 0xe6, 0x6a, 0x3a, 0x5f, 0x41, 0x6e, 0x01, 0x39, 0xf1, 0xa2, 0x2c, 0x79, 0x19, 0xc9, 0xb5, 0x6b, 0x2a, 0x30, 0x49, 0xa9, 0xea, 0x99, 0xa2, 0xc4, 0x33, 0x87, 0x31, 0x68, 0xae, 0xb4,
0x0b, 0x6a, 0xec, 0x27, 0xb0, 0x28, 0x59, 0x47, 0x73, 0xa6, 0xcc, 0x4d, 0xb4, 0x2e, 0x13, 0x98, 0xf3, 0x15, 0xe4, 0x16, 0x90, 0x13, 0x2f, 0xca, 0x92, 0x97, 0x91, 0xbc, 0xa0, 0xc6, 0x7e, 0x02,
0x52, 0x5e, 0x60, 0xec, 0xff, 0xb0, 0x60, 0x56, 0xec, 0x74, 0xee, 0x79, 0x13, 0xdf, 0x67, 0x03, 0x8b, 0x92, 0x91, 0x34, 0xd7, 0xca, 0xdc, 0x44, 0xeb, 0x32, 0xf1, 0x29, 0xe5, 0xc5, 0xc7, 0xfe,
0x23, 0x1d, 0xe3, 0x25, 0x08, 0x4a, 0x97, 0x50, 0x93, 0x39, 0x45, 0x58, 0x2e, 0x52, 0x84, 0x04, 0x0f, 0x0b, 0x66, 0xc5, 0x4e, 0xe7, 0x1e, 0x3b, 0xf1, 0x7d, 0x36, 0x30, 0xd2, 0x31, 0xde, 0x85,
0x2a, 0x63, 0x37, 0x39, 0xc5, 0x13, 0x76, 0xdd, 0xc1, 0xff, 0x49, 0x9b, 0xc7, 0x83, 0xb8, 0xd2, 0xa0, 0xac, 0x09, 0xa5, 0x99, 0x53, 0x8b, 0xe5, 0x22, 0xb5, 0x48, 0xa0, 0x32, 0x76, 0x93, 0x53,
0xc5, 0x58, 0x50, 0xd1, 0x43, 0x2e, 0x6e, 0xdf, 0xf3, 0x0f, 0xb9, 0xae, 0x41, 0x1d, 0x07, 0xd0, 0x3c, 0x6f, 0xd7, 0x1d, 0xfc, 0x9f, 0xb4, 0x79, 0x74, 0x88, 0xab, 0x60, 0x8c, 0x0c, 0x15, 0x3d,
0x4b, 0xc3, 0x3d, 0x29, 0xc0, 0x38, 0x97, 0x17, 0x50, 0x92, 0x45, 0x52, 0x76, 0x8a, 0xd8, 0xcb, 0xeb, 0xe2, 0xd6, 0x3e, 0xff, 0xac, 0xeb, 0x1a, 0xd4, 0x71, 0x00, 0xbd, 0x34, 0xf8, 0x93, 0x02,
0x7c, 0xe7, 0xc5, 0x12, 0xa8, 0xcb, 0x63, 0x91, 0x7e, 0x9b, 0xc2, 0x29, 0x47, 0x88, 0x01, 0x64, 0x8c, 0x73, 0x79, 0x01, 0xe5, 0x5a, 0xa4, 0x68, 0xa7, 0x88, 0xbd, 0xcc, 0x77, 0x5e, 0x2c, 0x81,
0x39, 0x42, 0x90, 0x3a, 0xaa, 0xde, 0xee, 0x42, 0x67, 0x87, 0xfa, 0x34, 0xa1, 0x5b, 0xbe, 0x9f, 0xba, 0x4a, 0x16, 0xc9, 0xb8, 0x29, 0x9c, 0x72, 0x84, 0x18, 0x40, 0x96, 0x23, 0x04, 0xa9, 0xa3,
0x6d, 0xff, 0x15, 0xb8, 0x5a, 0x50, 0x27, 0xfc, 0xe7, 0x2f, 0xc3, 0xf2, 0x16, 0x4f, 0x55, 0xfc, 0xea, 0xed, 0x2e, 0x74, 0x76, 0xa8, 0x4f, 0x13, 0xba, 0xe5, 0xfb, 0xd9, 0xf6, 0x5f, 0x81, 0xab,
0x49, 0xa5, 0xdf, 0xd8, 0x1d, 0x58, 0xc9, 0x36, 0x29, 0x3a, 0xbb, 0x07, 0x0b, 0x3b, 0xf4, 0x78, 0x05, 0x75, 0xc2, 0x9b, 0xfe, 0x32, 0x2c, 0x6f, 0xf1, 0xc4, 0xc5, 0x1f, 0x57, 0x32, 0x8e, 0xdd,
0x32, 0xdc, 0xa7, 0x67, 0x69, 0x47, 0x04, 0x2a, 0xf1, 0x69, 0x78, 0x2e, 0x04, 0x13, 0xff, 0x27, 0x81, 0x95, 0x6c, 0x93, 0xa2, 0xb3, 0x7b, 0xb0, 0xb0, 0x43, 0x8f, 0x27, 0xc3, 0x7d, 0x7a, 0x96,
0xaf, 0x02, 0xf8, 0x8c, 0xa6, 0x17, 0x8f, 0x69, 0x5f, 0x3e, 0xbe, 0x40, 0xe4, 0x68, 0x4c, 0xfb, 0x76, 0x44, 0xa0, 0x12, 0x9f, 0x86, 0xe7, 0x42, 0x30, 0xf1, 0x7f, 0xf2, 0x2a, 0x80, 0xcf, 0x68,
0xf6, 0xdb, 0x40, 0xf4, 0x76, 0xc4, 0x7a, 0x31, 0xbb, 0x37, 0x39, 0xee, 0xc5, 0x17, 0x71, 0x42, 0x7a, 0xf1, 0x98, 0xf6, 0xe5, 0x53, 0x0c, 0x44, 0x8e, 0xc6, 0xb4, 0x6f, 0xbf, 0x0d, 0x44, 0x6f,
0x47, 0xf2, 0x55, 0x89, 0x0e, 0xd9, 0x37, 0xa1, 0x79, 0xe8, 0x5e, 0x38, 0xf4, 0x9b, 0xe2, 0x55, 0x47, 0xac, 0x17, 0xb3, 0x82, 0x93, 0xe3, 0x5e, 0x7c, 0x11, 0x27, 0x74, 0x24, 0xdf, 0x98, 0xe8,
0xdb, 0x2a, 0xcc, 0x8e, 0xdd, 0x0b, 0xa6, 0xa6, 0x54, 0x1c, 0x0a, 0xab, 0xed, 0x7f, 0x2b, 0xc1, 0x90, 0x7d, 0x13, 0x9a, 0x87, 0xee, 0x85, 0x43, 0xbf, 0x29, 0xde, 0xb8, 0xad, 0xc2, 0xec, 0xd8,
0x0c, 0xa7, 0x64, 0xad, 0x0e, 0x68, 0x9c, 0x78, 0x01, 0x32, 0x96, 0x6c, 0x55, 0x83, 0x72, 0xac, 0xbd, 0x60, 0x6a, 0x4a, 0x45, 0xa5, 0xb0, 0xda, 0xfe, 0xb7, 0x12, 0xcc, 0x70, 0x4a, 0xd6, 0xea,
0x5c, 0x2a, 0x60, 0x65, 0x71, 0x4a, 0x93, 0x89, 0xec, 0x82, 0x5f, 0x0d, 0x8c, 0x31, 0x57, 0x9a, 0x80, 0xc6, 0x89, 0x17, 0x20, 0x63, 0xc9, 0x56, 0x35, 0x28, 0xc7, 0xca, 0xa5, 0x02, 0x56, 0x16,
0x07, 0xc7, 0x03, 0x21, 0x29, 0x90, 0x09, 0x59, 0xa6, 0xd6, 0x95, 0x8f, 0x4f, 0x4a, 0xa9, 0xe0, 0x67, 0x36, 0x99, 0xd6, 0x2e, 0xf8, 0xd5, 0xc0, 0x18, 0x73, 0xa5, 0x59, 0x71, 0x3c, 0x2c, 0x92,
0x5c, 0x1d, 0x2a, 0xb4, 0xe1, 0xb3, 0x9c, 0xc1, 0x73, 0x36, 0x3c, 0x67, 0xab, 0x6b, 0x2f, 0x61, 0x02, 0x99, 0x00, 0x66, 0x6a, 0x6b, 0xf9, 0xf8, 0xa4, 0x94, 0x0a, 0xce, 0xd5, 0xa1, 0x42, 0x8b,
0xab, 0xf9, 0xd1, 0xed, 0x45, 0xb6, 0x1a, 0x5e, 0xc2, 0x56, 0xdb, 0x04, 0xda, 0xf7, 0x28, 0x75, 0x3e, 0xcb, 0x19, 0x3c, 0x67, 0xd1, 0x73, 0x96, 0xbb, 0xf6, 0x12, 0x96, 0x9b, 0x1f, 0xe4, 0x5e,
0x28, 0xf3, 0x06, 0x25, 0xef, 0x7e, 0xc7, 0x82, 0xb6, 0xe0, 0x22, 0x55, 0x47, 0x5e, 0x37, 0xbc, 0x64, 0xb9, 0xe1, 0x25, 0x2c, 0xb7, 0x4d, 0xa0, 0x7d, 0x8f, 0x52, 0x87, 0x32, 0xdf, 0x50, 0xf2,
0xde, 0xc2, 0x84, 0xf2, 0x1b, 0x30, 0x87, 0xbe, 0xa8, 0x8a, 0xcd, 0x8a, 0x40, 0xb2, 0x01, 0xb2, 0xee, 0xb7, 0x2d, 0x68, 0x0b, 0x2e, 0x52, 0x75, 0xe4, 0x75, 0xc3, 0x07, 0x2e, 0x4c, 0x2f, 0xbf,
0x79, 0xc8, 0x7b, 0xdf, 0x91, 0xe7, 0x8b, 0x4d, 0xd1, 0x21, 0x19, 0xde, 0x8d, 0x5c, 0x91, 0x0f, 0x01, 0x73, 0xe8, 0x99, 0xaa, 0x48, 0xad, 0x08, 0x2b, 0x1b, 0x20, 0x9b, 0x87, 0xbc, 0x05, 0x1e,
0x66, 0x39, 0xaa, 0x6c, 0xff, 0xb1, 0x05, 0x0b, 0xda, 0x80, 0x05, 0x17, 0xbe, 0x07, 0x52, 0x1a, 0x79, 0xbe, 0xd8, 0x14, 0x1d, 0x92, 0xc1, 0xde, 0xc8, 0x15, 0xd9, 0x61, 0x96, 0xa3, 0xca, 0xf6,
0x78, 0xa0, 0x96, 0x4b, 0xee, 0xaa, 0x29, 0x36, 0xe9, 0x67, 0x06, 0x31, 0x6e, 0xa6, 0x7b, 0x81, 0x1f, 0x5b, 0xb0, 0xa0, 0x0d, 0x58, 0x70, 0xe1, 0x7b, 0x20, 0xa5, 0x81, 0x87, 0x6d, 0xb9, 0xe4,
0x03, 0x8c, 0x27, 0x23, 0xa1, 0x44, 0x75, 0x88, 0x31, 0xd2, 0x39, 0xa5, 0x4f, 0x15, 0x09, 0x57, 0xae, 0x9a, 0x62, 0x93, 0x7e, 0x66, 0x10, 0xe3, 0x66, 0xba, 0x17, 0x38, 0xc0, 0x78, 0x32, 0x12,
0xe3, 0x06, 0x86, 0xd1, 0x30, 0xe6, 0x43, 0x2b, 0xa2, 0x8a, 0x88, 0x86, 0xe9, 0xa0, 0xfd, 0xb7, 0x4a, 0x54, 0x87, 0x18, 0x23, 0x9d, 0x53, 0xfa, 0x54, 0x91, 0x70, 0x35, 0x6e, 0x60, 0x18, 0x1b,
0x16, 0x2c, 0xf2, 0xc3, 0x90, 0x38, 0x6a, 0xaa, 0xb7, 0x40, 0x33, 0xfc, 0xf4, 0xc7, 0x25, 0x72, 0x63, 0x1e, 0xb5, 0x22, 0xaa, 0x88, 0xd8, 0x98, 0x0e, 0xda, 0x7f, 0x6b, 0xc1, 0x22, 0x3f, 0x1a,
0xef, 0x8a, 0x23, 0xca, 0xe4, 0xb3, 0x2f, 0x79, 0x80, 0x53, 0x49, 0x6a, 0x53, 0xf6, 0xa2, 0x5c, 0x89, 0x83, 0xa7, 0x7a, 0x19, 0x34, 0xc3, 0xcf, 0x82, 0x5c, 0x22, 0xf7, 0xae, 0x38, 0xa2, 0x4c,
0xb4, 0x17, 0x2f, 0x58, 0xe9, 0xa2, 0xc0, 0x64, 0xb5, 0x30, 0x30, 0x79, 0x77, 0x16, 0xaa, 0x71, 0x3e, 0xfb, 0x92, 0xc7, 0x39, 0x95, 0xb2, 0x36, 0x65, 0x2f, 0xca, 0x45, 0x7b, 0xf1, 0x82, 0x95,
0x3f, 0x1c, 0x53, 0x7b, 0x05, 0x96, 0xcc, 0xc9, 0x09, 0x15, 0xf4, 0x3d, 0x0b, 0x3a, 0xf7, 0x78, 0x2e, 0x0a, 0x53, 0x56, 0x0b, 0xc3, 0x94, 0x77, 0x67, 0xa1, 0x1a, 0xf7, 0xc3, 0x31, 0xb5, 0x57,
0x00, 0xdf, 0x0b, 0x86, 0x7b, 0x5e, 0x9c, 0x84, 0x91, 0x7a, 0x32, 0x79, 0x1d, 0x20, 0x4e, 0xdc, 0x60, 0xc9, 0x9c, 0x9c, 0x50, 0x41, 0xdf, 0xb5, 0xa0, 0x73, 0x8f, 0x87, 0xf3, 0xbd, 0x60, 0xb8,
0x28, 0xe1, 0xa9, 0xc8, 0x22, 0x20, 0x98, 0x22, 0x6c, 0x8c, 0x34, 0x18, 0xf0, 0x5a, 0xbe, 0x37, 0xe7, 0xc5, 0x49, 0x18, 0xa9, 0x07, 0x94, 0xd7, 0x01, 0xe2, 0xc4, 0x8d, 0x12, 0x9e, 0x98, 0x2c,
0xaa, 0x9c, 0xf3, 0x21, 0xc4, 0x71, 0xcd, 0xb0, 0xc4, 0x6f, 0xf0, 0xa4, 0x4d, 0xe6, 0x2b, 0xd0, 0xc2, 0x83, 0x29, 0xc2, 0xc6, 0x48, 0x83, 0x01, 0xaf, 0xe5, 0x7b, 0xa3, 0xca, 0x39, 0x1f, 0x42,
0x33, 0xd4, 0xeb, 0xfc, 0x1c, 0x94, 0x41, 0xed, 0xbf, 0xb6, 0x60, 0x3e, 0x1d, 0x24, 0x5e, 0x67, 0x1c, 0xde, 0x0c, 0x4b, 0xfc, 0x06, 0x4f, 0xe1, 0x64, 0xbe, 0x02, 0x3d, 0x43, 0xbd, 0xce, 0x4f,
0x9a, 0xda, 0x41, 0x98, 0xdf, 0x54, 0x3b, 0xc8, 0x50, 0xa5, 0xc7, 0xec, 0xb1, 0x18, 0x9b, 0x86, 0x45, 0x19, 0xd4, 0xfe, 0x6b, 0x0b, 0xe6, 0xd3, 0x41, 0xe2, 0xe5, 0xa6, 0xa9, 0x1d, 0x84, 0xf9,
0xa0, 0xc4, 0x8a, 0x52, 0x38, 0x91, 0x0e, 0x8e, 0x0e, 0xf1, 0x14, 0x2c, 0xe6, 0x09, 0x08, 0xaf, 0x4d, 0xb5, 0x83, 0x0c, 0x5c, 0x7a, 0xcc, 0x1e, 0x8b, 0xb1, 0x69, 0x08, 0x4a, 0xac, 0x28, 0x85,
0x46, 0x94, 0x30, 0x93, 0x7c, 0x94, 0xe0, 0x57, 0x3c, 0xa8, 0x2a, 0x8b, 0xd2, 0x94, 0xce, 0x22, 0x13, 0xe9, 0xe0, 0xe8, 0x10, 0x4f, 0xc8, 0x62, 0x9e, 0x80, 0xf0, 0x6a, 0x44, 0x09, 0xf3, 0xca,
0x8a, 0xa6, 0x54, 0xbf, 0x0c, 0xa9, 0xf1, 0xf5, 0x91, 0x65, 0xfb, 0x97, 0x2d, 0xb8, 0x5a, 0xb0, 0x47, 0x09, 0x7e, 0xc5, 0x43, 0xac, 0xb2, 0x28, 0x4d, 0xe9, 0x2c, 0xa2, 0x68, 0x4a, 0xf5, 0xab,
0xf0, 0x42, 0x6a, 0x76, 0x60, 0xe1, 0x44, 0x55, 0xca, 0xc5, 0xe1, 0xa2, 0xb3, 0x22, 0x2f, 0xdb, 0x91, 0x1a, 0x5f, 0x1f, 0x59, 0xb6, 0x7f, 0xc9, 0x82, 0xab, 0x05, 0x0b, 0x2f, 0xa4, 0x66, 0x07,
0xcc, 0x05, 0x71, 0xf2, 0x1f, 0x28, 0xbf, 0x88, 0x2f, 0xb7, 0x91, 0xe4, 0x98, 0xaf, 0xd8, 0x78, 0x16, 0x4e, 0x54, 0xa5, 0x5c, 0x1c, 0x2e, 0x3a, 0x2b, 0xf2, 0xea, 0xcd, 0x5c, 0x10, 0x27, 0xff,
0x0e, 0x0d, 0xed, 0xb1, 0x22, 0x59, 0x85, 0xc5, 0x27, 0x0f, 0x1e, 0x1d, 0xec, 0x1e, 0x1d, 0xf5, 0x81, 0xf2, 0x8b, 0xf8, 0x72, 0x1b, 0x29, 0x8f, 0xf9, 0x8a, 0x8d, 0xe7, 0xd0, 0xd0, 0x9e, 0x2e,
0x0e, 0x1f, 0xdf, 0xfd, 0xd2, 0xee, 0x57, 0x7b, 0x7b, 0x5b, 0x47, 0x7b, 0xed, 0x2b, 0x64, 0x05, 0x92, 0x55, 0x58, 0x7c, 0xf2, 0xe0, 0xd1, 0xc1, 0xee, 0xd1, 0x51, 0xef, 0xf0, 0xf1, 0xdd, 0x2f,
0xc8, 0xc1, 0xee, 0xd1, 0xa3, 0xdd, 0x1d, 0x03, 0xb7, 0xc8, 0x75, 0xe8, 0x3e, 0x3e, 0x78, 0x7c, 0xed, 0x7e, 0xb5, 0xb7, 0xb7, 0x75, 0xb4, 0xd7, 0xbe, 0x42, 0x56, 0x80, 0x1c, 0xec, 0x1e, 0x3d,
0xb4, 0xbb, 0xd3, 0x2b, 0xfa, 0xae, 0x44, 0x5e, 0x85, 0xab, 0xa2, 0xbe, 0xe0, 0xf3, 0xf2, 0x9d, 0xda, 0xdd, 0x31, 0x70, 0x8b, 0x5c, 0x87, 0xee, 0xe3, 0x83, 0xc7, 0x47, 0xbb, 0x3b, 0xbd, 0xa2,
0x5f, 0x29, 0x43, 0x8b, 0xdf, 0x03, 0xf3, 0x5f, 0xc7, 0xa0, 0x11, 0x79, 0x1f, 0x66, 0xc5, 0xaf, 0xef, 0x4a, 0xe4, 0x55, 0xb8, 0x2a, 0xea, 0x0b, 0x3e, 0x2f, 0xdf, 0xf9, 0xe5, 0x32, 0xb4, 0xf8,
0x9b, 0x90, 0x65, 0x31, 0x6b, 0xf3, 0xf7, 0x54, 0xba, 0x2b, 0x59, 0x58, 0xb0, 0xf5, 0xe2, 0xcf, 0xad, 0x30, 0xff, 0xad, 0x0c, 0x1a, 0x91, 0xf7, 0x61, 0x56, 0xfc, 0xd6, 0x09, 0x59, 0x16, 0xb3,
0xfc, 0xf0, 0x1f, 0x7f, 0xad, 0x34, 0x47, 0x1a, 0x9b, 0x67, 0x6f, 0x6d, 0x0e, 0x69, 0x10, 0xb3, 0x36, 0x7f, 0x5d, 0xa5, 0xbb, 0x92, 0x85, 0x05, 0x5b, 0x2f, 0xfe, 0xd4, 0x0f, 0xfe, 0xf1, 0x57,
0x36, 0xbe, 0x0e, 0x90, 0xfe, 0xee, 0x07, 0xe9, 0x28, 0x77, 0x32, 0xf3, 0x83, 0x26, 0xdd, 0xab, 0x4b, 0x73, 0xa4, 0xb1, 0x79, 0xf6, 0xd6, 0xe6, 0x90, 0x06, 0x31, 0x6b, 0xe3, 0xeb, 0x00, 0xe9,
0x05, 0x35, 0xa2, 0xdd, 0xab, 0xd8, 0xee, 0xa2, 0xdd, 0x62, 0xed, 0x7a, 0x81, 0x97, 0xf0, 0x1f, 0xaf, 0x80, 0x90, 0x8e, 0x72, 0x27, 0x33, 0x3f, 0x6f, 0xd2, 0xbd, 0x5a, 0x50, 0x23, 0xda, 0xbd,
0x01, 0x79, 0xd7, 0xda, 0x20, 0x03, 0x68, 0xea, 0x3f, 0xeb, 0x41, 0x64, 0x14, 0xab, 0xe0, 0x47, 0x8a, 0xed, 0x2e, 0xda, 0x2d, 0xd6, 0xae, 0x17, 0x78, 0x09, 0xff, 0x49, 0x90, 0x77, 0xad, 0x0d,
0x45, 0xba, 0xaf, 0x14, 0xd6, 0xc9, 0x10, 0x1e, 0xf6, 0xb1, 0x6c, 0xb7, 0x59, 0x1f, 0x13, 0xa4, 0x32, 0x80, 0xa6, 0xfe, 0x23, 0x1f, 0x44, 0xc6, 0xb4, 0x0a, 0x7e, 0x62, 0xa4, 0xfb, 0x4a, 0x61,
0x48, 0x7b, 0xf1, 0xa1, 0x65, 0xfe, 0x7a, 0x07, 0xb9, 0xa6, 0x69, 0x9c, 0xdc, 0x6f, 0x87, 0x74, 0x9d, 0x0c, 0xe8, 0x61, 0x1f, 0xcb, 0x76, 0x9b, 0xf5, 0x31, 0x41, 0x8a, 0xb4, 0x17, 0x1f, 0x5a,
0x5f, 0x9d, 0x52, 0x2b, 0xfa, 0x7a, 0x15, 0xfb, 0x5a, 0xb5, 0x09, 0xeb, 0xab, 0x8f, 0x34, 0xf2, 0xe6, 0x6f, 0x79, 0x90, 0x6b, 0x9a, 0xc6, 0xc9, 0xfd, 0x92, 0x48, 0xf7, 0xd5, 0x29, 0xb5, 0xa2,
0xb7, 0x43, 0xde, 0xb5, 0x36, 0xee, 0xfc, 0xf9, 0x1a, 0xd4, 0x55, 0xdc, 0x99, 0x7c, 0x08, 0x73, 0xaf, 0x57, 0xb1, 0xaf, 0x55, 0x9b, 0xb0, 0xbe, 0xfa, 0x48, 0x23, 0x7f, 0x49, 0xe4, 0x5d, 0x6b,
0xc6, 0x45, 0x3d, 0x91, 0xd3, 0x28, 0xba, 0xd7, 0xef, 0x5e, 0x2b, 0xae, 0x14, 0x1d, 0x5f, 0xc7, 0xe3, 0xce, 0x9f, 0xaf, 0x41, 0x5d, 0x45, 0xa1, 0xc9, 0x87, 0x30, 0x67, 0x5c, 0xdb, 0x13, 0x39,
0x8e, 0x3b, 0x64, 0x85, 0x75, 0x2c, 0x6e, 0xba, 0x37, 0x31, 0xe5, 0x84, 0xe7, 0x8f, 0x3f, 0xe5, 0x8d, 0xa2, 0x5b, 0xfe, 0xee, 0xb5, 0xe2, 0x4a, 0xd1, 0xf1, 0x75, 0xec, 0xb8, 0x43, 0x56, 0x58,
0xf3, 0x4c, 0x2f, 0xd7, 0x8d, 0x79, 0xe6, 0x2e, 0xe3, 0x8d, 0x79, 0xe6, 0x6f, 0xe4, 0xed, 0x6b, 0xc7, 0xe2, 0xde, 0x7b, 0x13, 0x13, 0x50, 0x78, 0x36, 0xf9, 0x53, 0x3e, 0xcf, 0xf4, 0xaa, 0xdd,
0xd8, 0xdd, 0x0a, 0x59, 0xd2, 0xbb, 0x53, 0xf1, 0x60, 0x8a, 0x8f, 0x1e, 0xf4, 0x1f, 0xbe, 0x20, 0x98, 0x67, 0xee, 0x6a, 0xde, 0x98, 0x67, 0xfe, 0x7e, 0xde, 0xbe, 0x86, 0xdd, 0xad, 0x90, 0x25,
0xaf, 0x2a, 0xc6, 0x2a, 0xfa, 0x41, 0x0c, 0xc5, 0x22, 0xf9, 0x5f, 0xc5, 0xb0, 0x3b, 0xd8, 0x15, 0xbd, 0x3b, 0x15, 0x1d, 0xa6, 0xf8, 0x04, 0x42, 0xff, 0x19, 0x0c, 0xf2, 0xaa, 0x62, 0xac, 0xa2,
0x21, 0xb8, 0x7d, 0xfa, 0xef, 0x5e, 0x90, 0xaf, 0x41, 0x5d, 0x3d, 0x74, 0x26, 0xab, 0xda, 0xc3, 0x9f, 0xc7, 0x50, 0x2c, 0x92, 0xff, 0x8d, 0x0c, 0xbb, 0x83, 0x5d, 0x11, 0x82, 0xdb, 0xa7, 0xff,
0x73, 0xfd, 0x61, 0x76, 0xb7, 0x93, 0xaf, 0x28, 0x62, 0x0c, 0xbd, 0x65, 0xc6, 0x18, 0x4f, 0xa0, 0x0a, 0x06, 0xf9, 0x1a, 0xd4, 0xd5, 0xb3, 0x67, 0xb2, 0xaa, 0x3d, 0x43, 0xd7, 0x9f, 0x69, 0x77,
0xa1, 0x3d, 0x66, 0x26, 0x57, 0xd5, 0xad, 0x41, 0xf6, 0xc1, 0x74, 0xb7, 0x5b, 0x54, 0x25, 0xba, 0x3b, 0xf9, 0x8a, 0x22, 0xc6, 0xd0, 0x5b, 0x66, 0x8c, 0xf1, 0x04, 0x1a, 0xda, 0xd3, 0x66, 0x72,
0x58, 0xc0, 0x2e, 0x1a, 0xa4, 0x8e, 0xbc, 0x97, 0x3c, 0x0b, 0x63, 0xb2, 0x0f, 0xcb, 0xe2, 0xdc, 0x55, 0xdd, 0x21, 0x64, 0x9f, 0x4f, 0x77, 0xbb, 0x45, 0x55, 0xa2, 0x8b, 0x05, 0xec, 0xa2, 0x41,
0x73, 0x4c, 0x3f, 0xce, 0x12, 0x15, 0xfc, 0x0e, 0xc8, 0x6d, 0x8b, 0xbc, 0x07, 0x35, 0xf9, 0x66, 0xea, 0xc8, 0x7b, 0xc9, 0xb3, 0x30, 0x26, 0xfb, 0xb0, 0x2c, 0xce, 0x3d, 0xc7, 0xf4, 0xe3, 0x2c,
0x9d, 0xac, 0x14, 0xbf, 0xbd, 0xef, 0xae, 0xe6, 0x70, 0xa1, 0x15, 0xbf, 0x0a, 0x90, 0xbe, 0x9c, 0x51, 0xc1, 0xaf, 0x82, 0xdc, 0xb6, 0xc8, 0x7b, 0x50, 0x93, 0x2f, 0xd8, 0xc9, 0x4a, 0xf1, 0x4b,
0x56, 0x02, 0x9c, 0x7b, 0x89, 0xad, 0x76, 0x27, 0xff, 0xcc, 0xda, 0x5e, 0xc1, 0x09, 0xb6, 0x09, 0xfc, 0xee, 0x6a, 0x0e, 0x17, 0x5a, 0xf1, 0xab, 0x00, 0xe9, 0x3b, 0x6a, 0x25, 0xc0, 0xb9, 0x77,
0x0a, 0x70, 0x40, 0xcf, 0xe5, 0x33, 0xa0, 0x6f, 0x40, 0x43, 0x7b, 0x3c, 0xad, 0x96, 0x2f, 0xff, 0xd9, 0x6a, 0x77, 0xf2, 0x8f, 0xae, 0xed, 0x15, 0x9c, 0x60, 0x9b, 0xa0, 0x00, 0x07, 0xf4, 0x5c,
0xf0, 0x5a, 0x2d, 0x5f, 0xc1, 0x5b, 0x6b, 0xbb, 0x8b, 0xad, 0x2f, 0xd9, 0xf3, 0xac, 0xf5, 0xd8, 0x3e, 0x0a, 0xfa, 0x06, 0x34, 0xb4, 0xa7, 0xd4, 0x6a, 0xf9, 0xf2, 0xcf, 0xb0, 0xd5, 0xf2, 0x15,
0x1b, 0x06, 0x23, 0x4e, 0xc0, 0x36, 0xe8, 0x14, 0xe6, 0x8c, 0x17, 0xd2, 0x4a, 0x7a, 0x8a, 0xde, 0xbc, 0xbc, 0xb6, 0xbb, 0xd8, 0xfa, 0x92, 0x3d, 0xcf, 0x5a, 0x8f, 0xbd, 0x61, 0x30, 0xe2, 0x04,
0x5f, 0x2b, 0xe9, 0x29, 0x7c, 0x54, 0x2d, 0xd9, 0xd9, 0x5e, 0x60, 0xfd, 0x9c, 0x21, 0x89, 0xd6, 0x6c, 0x83, 0x4e, 0x61, 0xce, 0x78, 0x2f, 0xad, 0xa4, 0xa7, 0xe8, 0x35, 0xb6, 0x92, 0x9e, 0xc2,
0xd3, 0x07, 0xd0, 0xd0, 0x5e, 0x3b, 0xab, 0xb9, 0xe4, 0x1f, 0x56, 0xab, 0xb9, 0x14, 0x3d, 0x8e, 0x27, 0xd6, 0x92, 0x9d, 0xed, 0x05, 0xd6, 0xcf, 0x19, 0x92, 0x68, 0x3d, 0x7d, 0x00, 0x0d, 0xed,
0x5e, 0xc2, 0x3e, 0x5a, 0x36, 0xb2, 0x02, 0xbe, 0xa2, 0x61, 0x6d, 0x7f, 0x08, 0x2d, 0xf3, 0xfd, 0xed, 0xb3, 0x9a, 0x4b, 0xfe, 0x99, 0xb5, 0x9a, 0x4b, 0xd1, 0x53, 0xe9, 0x25, 0xec, 0xa3, 0x65,
0xb3, 0x92, 0xcb, 0xc2, 0x97, 0xd4, 0x4a, 0x2e, 0xa7, 0x3c, 0x9a, 0x16, 0x2c, 0xbd, 0xb1, 0xa8, 0x23, 0x2b, 0xe0, 0x9b, 0x1a, 0xd6, 0xf6, 0x87, 0xd0, 0x32, 0x5f, 0x43, 0x2b, 0xb9, 0x2c, 0x7c,
0x3a, 0xd9, 0xfc, 0x48, 0xdc, 0x15, 0x3f, 0x27, 0x5f, 0x66, 0xca, 0x47, 0x3c, 0x6b, 0x22, 0xab, 0x57, 0xad, 0xe4, 0x72, 0xca, 0x13, 0x6a, 0xc1, 0xd2, 0x1b, 0x8b, 0xaa, 0x93, 0xcd, 0x8f, 0xc4,
0x1a, 0xd7, 0xea, 0x8f, 0x9f, 0x94, 0xbc, 0xe4, 0x5e, 0x40, 0x99, 0xcc, 0xcc, 0xdf, 0x01, 0xa1, 0xcd, 0xf1, 0x73, 0xf2, 0x65, 0xa6, 0x7c, 0xc4, 0x23, 0x27, 0xb2, 0xaa, 0x71, 0xad, 0xfe, 0x14,
0x45, 0xc1, 0xe7, 0x4d, 0x9a, 0x45, 0xd1, 0x5f, 0x40, 0x69, 0x16, 0xc5, 0x78, 0x05, 0x95, 0xb5, 0x4a, 0xc9, 0x4b, 0xee, 0x3d, 0x94, 0xc9, 0xcc, 0xfc, 0x55, 0x10, 0x5a, 0x14, 0x7c, 0xec, 0xa4,
0x28, 0x89, 0xc7, 0xda, 0x08, 0x60, 0x3e, 0x93, 0xe8, 0xa7, 0xa4, 0xa2, 0x38, 0x33, 0xba, 0x7b, 0x59, 0x14, 0xfd, 0x3d, 0x94, 0x66, 0x51, 0x8c, 0x37, 0x51, 0x59, 0x8b, 0x92, 0x78, 0xac, 0x8d,
0xfd, 0xc5, 0xf9, 0x81, 0xa6, 0xa2, 0x92, 0x0a, 0x6a, 0x53, 0xe6, 0xa1, 0xff, 0x7f, 0x68, 0xea, 0x00, 0xe6, 0x33, 0x69, 0x7f, 0x4a, 0x2a, 0x8a, 0xf3, 0xa4, 0xbb, 0xd7, 0x5f, 0x9c, 0x2d, 0x68,
0x2f, 0x53, 0x89, 0x2e, 0xca, 0xd9, 0x9e, 0x5e, 0x29, 0xac, 0x33, 0x37, 0x97, 0x34, 0xf5, 0x6e, 0x2a, 0x2a, 0xa9, 0xa0, 0x36, 0x65, 0x56, 0xfa, 0xff, 0x87, 0xa6, 0xfe, 0x4e, 0x95, 0xe8, 0xa2,
0xc8, 0x57, 0x60, 0x45, 0x89, 0xba, 0x9e, 0x3b, 0x16, 0x93, 0xd7, 0x0a, 0x32, 0xca, 0xf4, 0x68, 0x9c, 0xed, 0xe9, 0x95, 0xc2, 0x3a, 0x73, 0x73, 0x49, 0x53, 0xef, 0x86, 0x7c, 0x05, 0x56, 0x94,
0x48, 0xf7, 0xea, 0xd4, 0x94, 0xb3, 0xdb, 0x16, 0x63, 0x1a, 0xf3, 0xc9, 0x5f, 0xaa, 0xcc, 0x8b, 0xa8, 0xeb, 0x99, 0x64, 0x31, 0x79, 0xad, 0x20, 0xbf, 0x4c, 0x8f, 0x86, 0x74, 0xaf, 0x4e, 0x4d,
0x5e, 0x3a, 0xa6, 0xca, 0xbc, 0xf0, 0x9d, 0xa0, 0x64, 0x1a, 0xb2, 0x68, 0xac, 0x11, 0xbf, 0x08, 0x40, 0xbb, 0x6d, 0x31, 0xa6, 0x31, 0x1f, 0x00, 0xa6, 0xca, 0xbc, 0xe8, 0xdd, 0x63, 0xaa, 0xcc,
0x20, 0x1f, 0xc0, 0xbc, 0x96, 0x9d, 0x7b, 0x74, 0x11, 0xf4, 0x95, 0x00, 0xe4, 0x9f, 0x71, 0x74, 0x0b, 0x5f, 0x0d, 0x4a, 0xa6, 0x21, 0x8b, 0xc6, 0x1a, 0xf1, 0x6b, 0x01, 0xf2, 0x01, 0xcc, 0x6b,
0x8b, 0xdc, 0x75, 0x7b, 0x15, 0xdb, 0x5f, 0xb0, 0x8d, 0xc5, 0x61, 0xcc, 0xbf, 0x0d, 0x0d, 0x3d, 0xb9, 0xba, 0x47, 0x17, 0x41, 0x5f, 0x09, 0x40, 0xfe, 0x51, 0x47, 0xb7, 0xc8, 0x5d, 0xb7, 0x57,
0xf3, 0xf7, 0x05, 0xed, 0xae, 0x6a, 0x55, 0xfa, 0x2b, 0x84, 0xdb, 0x16, 0xf9, 0x0d, 0x0b, 0x9a, 0xb1, 0xfd, 0x05, 0xdb, 0x58, 0x1c, 0xc6, 0xfc, 0xdb, 0xd0, 0xd0, 0xf3, 0x80, 0x5f, 0xd0, 0xee,
0x46, 0x1e, 0xad, 0x71, 0xdd, 0x95, 0x69, 0xa7, 0xa3, 0xd7, 0xe9, 0x0d, 0xd9, 0x0e, 0x0e, 0x72, 0xaa, 0x56, 0xa5, 0xbf, 0x49, 0xb8, 0x6d, 0x91, 0xdf, 0xb0, 0xa0, 0x69, 0x64, 0xd5, 0x1a, 0x97,
0x7f, 0xe3, 0x8b, 0xc6, 0x22, 0x7c, 0x64, 0x1c, 0xfb, 0x6e, 0x65, 0x7f, 0xf8, 0xe5, 0x79, 0x96, 0x5f, 0x99, 0x76, 0x3a, 0x7a, 0x9d, 0xde, 0x90, 0xed, 0xe0, 0x20, 0xf7, 0x37, 0xbe, 0x68, 0x2c,
0x40, 0x7f, 0xea, 0xf2, 0xfc, 0xb6, 0x45, 0xbe, 0x6f, 0x41, 0xcb, 0x0c, 0x56, 0xa8, 0xad, 0x2a, 0xc2, 0x47, 0xc6, 0xb1, 0xef, 0x56, 0xf6, 0x67, 0x60, 0x9e, 0x67, 0x09, 0xf4, 0x87, 0x2f, 0xcf,
0x0c, 0x8b, 0xa8, 0xad, 0x9a, 0x12, 0xe1, 0xf8, 0x00, 0x47, 0xf9, 0x68, 0xc3, 0x31, 0x46, 0x29, 0x6f, 0x5b, 0xe4, 0x7b, 0x16, 0xb4, 0xcc, 0x60, 0x85, 0xda, 0xaa, 0xc2, 0xb0, 0x88, 0xda, 0xaa,
0x1e, 0x83, 0xfe, 0x78, 0xa3, 0x25, 0xef, 0xf2, 0x1f, 0x7f, 0x92, 0x11, 0x34, 0xa2, 0x59, 0x8d, 0x29, 0x11, 0x8e, 0x0f, 0x70, 0x94, 0x8f, 0x36, 0x1c, 0x63, 0x94, 0xe2, 0x69, 0xe8, 0x8f, 0x36,
0xec, 0xf6, 0xea, 0xbf, 0x67, 0xb4, 0x6e, 0xdd, 0xb6, 0xc8, 0x37, 0xf8, 0xef, 0xc3, 0x88, 0x6f, 0x5a, 0xf2, 0x2e, 0xff, 0x29, 0x28, 0x19, 0x41, 0x23, 0x9a, 0xd5, 0xc8, 0x6e, 0xaf, 0xfe, 0xeb,
0x91, 0x4b, 0x5e, 0xf6, 0x7b, 0xfb, 0x06, 0xce, 0xe9, 0xba, 0x7d, 0xd5, 0x98, 0x53, 0xd6, 0x1e, 0x46, 0xeb, 0xd6, 0x6d, 0x8b, 0x7c, 0x83, 0xff, 0x5a, 0x8c, 0xf8, 0x16, 0xb9, 0xe4, 0x65, 0xbf,
0x6f, 0xf1, 0xd1, 0x89, 0x9f, 0x22, 0x4a, 0x0d, 0x4a, 0xee, 0xe7, 0x89, 0xa6, 0x0f, 0x72, 0xc4, 0xb7, 0x6f, 0xe0, 0x9c, 0xae, 0xdb, 0x57, 0x8d, 0x39, 0x65, 0xed, 0xf1, 0x16, 0x1f, 0x9d, 0xf8,
0x07, 0x29, 0xc8, 0x0d, 0x56, 0x7e, 0xc9, 0x66, 0xec, 0x0d, 0x1c, 0xeb, 0x0d, 0xfb, 0xb5, 0xa9, 0x61, 0xa2, 0xd4, 0xa0, 0xe4, 0x7e, 0xac, 0x68, 0xfa, 0x20, 0x47, 0x7c, 0x90, 0x82, 0xdc, 0x60,
0x63, 0xdd, 0xc4, 0x90, 0x03, 0x1b, 0xf1, 0x21, 0x40, 0x1a, 0xed, 0x26, 0x99, 0x68, 0xab, 0x12, 0xe5, 0x97, 0x6c, 0xc6, 0xde, 0xc0, 0xb1, 0xde, 0xb0, 0x5f, 0x9b, 0x3a, 0xd6, 0x4d, 0x0c, 0x39,
0xf0, 0x7c, 0x40, 0xdc, 0x94, 0x17, 0x19, 0x94, 0x65, 0x2d, 0x7e, 0x8d, 0xab, 0xab, 0x07, 0x32, 0xb0, 0x11, 0x1f, 0x02, 0xa4, 0xd1, 0x6e, 0x92, 0x89, 0xb6, 0x2a, 0x01, 0xcf, 0x07, 0xc4, 0x4d,
0x4e, 0xab, 0x3b, 0x25, 0x66, 0x58, 0xda, 0x70, 0x4a, 0xb2, 0xed, 0x1b, 0xca, 0x4a, 0x05, 0x7d, 0x79, 0x91, 0x41, 0x59, 0xd6, 0xe2, 0xd7, 0xb8, 0xba, 0x7a, 0x20, 0xe3, 0xb4, 0xba, 0x53, 0x62,
0x1f, 0xc3, 0xdc, 0x7e, 0x18, 0x3e, 0x9d, 0x8c, 0xd5, 0x5d, 0x95, 0x19, 0x0d, 0xdc, 0x73, 0xe3, 0x86, 0xa5, 0x0d, 0xa7, 0x24, 0xdb, 0xbe, 0xa1, 0xac, 0x54, 0xd0, 0xf7, 0x31, 0xcc, 0xed, 0x87,
0xd3, 0x6e, 0x66, 0x16, 0xf6, 0x1a, 0x36, 0xd5, 0x25, 0x1d, 0xad, 0xa9, 0xcd, 0x8f, 0xd2, 0x68, 0xe1, 0xd3, 0xc9, 0x58, 0xdd, 0x5c, 0x99, 0xd1, 0xc0, 0x3d, 0x37, 0x3e, 0xed, 0x66, 0x66, 0x61,
0xfa, 0x73, 0xe2, 0xc2, 0x82, 0xd2, 0x81, 0x6a, 0xe0, 0x5d, 0xb3, 0x19, 0x43, 0xf3, 0x65, 0xbb, 0xaf, 0x61, 0x53, 0x5d, 0xd2, 0xd1, 0x9a, 0xda, 0xfc, 0x28, 0x8d, 0xa6, 0x3f, 0x27, 0x2e, 0x2c,
0x30, 0x3c, 0x5b, 0x39, 0xda, 0xcd, 0x58, 0xb6, 0x79, 0xdb, 0x22, 0x87, 0xd0, 0xdc, 0xa1, 0xfd, 0x28, 0x1d, 0xa8, 0x06, 0xde, 0x35, 0x9b, 0x31, 0x34, 0x5f, 0xb6, 0x0b, 0xc3, 0xb3, 0x95, 0xa3,
0x70, 0x40, 0x45, 0x48, 0x6d, 0x31, 0x1d, 0xb8, 0x8a, 0xc5, 0x75, 0xe7, 0x0c, 0xd0, 0xb4, 0x0b, 0xdd, 0x8c, 0x65, 0x9b, 0xb7, 0x2d, 0x72, 0x08, 0xcd, 0x1d, 0xda, 0x0f, 0x07, 0x54, 0x84, 0xd4,
0x63, 0xf7, 0x22, 0xa2, 0xdf, 0xdc, 0xfc, 0x48, 0x04, 0xeb, 0x9e, 0x4b, 0xbb, 0x20, 0xa3, 0x99, 0x16, 0xd3, 0x81, 0xab, 0x58, 0x5c, 0x77, 0xce, 0x00, 0x4d, 0xbb, 0x30, 0x76, 0x2f, 0x22, 0xfa,
0x86, 0x5d, 0xc8, 0x84, 0x3f, 0x0d, 0xbb, 0x90, 0x0b, 0x7f, 0x1a, 0x4b, 0x2d, 0xa3, 0xa9, 0xc4, 0xcd, 0xcd, 0x8f, 0x44, 0xb0, 0xee, 0xb9, 0xb4, 0x0b, 0x32, 0x9a, 0x69, 0xd8, 0x85, 0x4c, 0xf8,
0x87, 0x85, 0x5c, 0xc4, 0x54, 0x99, 0x84, 0x69, 0x71, 0xd6, 0xee, 0xda, 0x74, 0x02, 0xb3, 0xb7, 0xd3, 0xb0, 0x0b, 0xb9, 0xf0, 0xa7, 0xb1, 0xd4, 0x32, 0x9a, 0x4a, 0x7c, 0x58, 0xc8, 0x45, 0x4c,
0x0d, 0xb3, 0xb7, 0x23, 0x98, 0xdb, 0xa1, 0x7c, 0xb1, 0x78, 0x42, 0x4c, 0x26, 0x19, 0x5b, 0x4f, 0x95, 0x49, 0x98, 0x16, 0x67, 0xed, 0xae, 0x4d, 0x27, 0x30, 0x7b, 0xdb, 0x30, 0x7b, 0x3b, 0x82,
0xb7, 0xc9, 0x2a, 0x70, 0xac, 0x33, 0x0d, 0x3f, 0x66, 0xa3, 0x90, 0xaf, 0x41, 0xe3, 0x3e, 0x4d, 0xb9, 0x1d, 0xca, 0x17, 0x8b, 0xa7, 0xc7, 0x64, 0x52, 0xb3, 0xf5, 0xe4, 0x9b, 0xac, 0x02, 0xc7,
0x64, 0x06, 0x8c, 0x72, 0x3d, 0x33, 0x29, 0x31, 0xdd, 0x82, 0x04, 0x1a, 0x93, 0x67, 0xb0, 0xb5, 0x3a, 0xd3, 0xf0, 0x63, 0x6e, 0x0a, 0xf9, 0x1a, 0x34, 0xee, 0xd3, 0x44, 0xe6, 0xc3, 0x28, 0xd7,
0x4d, 0x3a, 0x18, 0x52, 0xae, 0x9c, 0x7a, 0xde, 0xe0, 0x39, 0xf9, 0x7f, 0xd8, 0xb8, 0x4a, 0xd4, 0x33, 0x93, 0x20, 0xd3, 0x2d, 0x48, 0xa7, 0x31, 0x79, 0x06, 0x5b, 0xdb, 0xa4, 0x83, 0x21, 0xe5,
0x5b, 0xd1, 0x12, 0x27, 0xf4, 0xc6, 0xe7, 0x33, 0x78, 0x51, 0xcb, 0x41, 0x38, 0xa0, 0x9a, 0x0b, 0xca, 0xa9, 0xe7, 0x0d, 0x9e, 0x93, 0xff, 0x87, 0x8d, 0xab, 0xb4, 0xbd, 0x15, 0x2d, 0x8d, 0x42,
0x14, 0x40, 0x43, 0xcb, 0x2f, 0x55, 0x02, 0x94, 0x4f, 0x07, 0x56, 0x02, 0x54, 0x90, 0x8e, 0x6a, 0x6f, 0x7c, 0x3e, 0x83, 0x17, 0xb5, 0x1c, 0x84, 0x03, 0xaa, 0xb9, 0x40, 0x01, 0x34, 0xb4, 0x6c,
0xaf, 0x63, 0x3f, 0x36, 0x59, 0x4b, 0xfb, 0xe1, 0x29, 0xa8, 0x69, 0x4f, 0x9b, 0x1f, 0xb9, 0xa3, 0x53, 0x25, 0x40, 0xf9, 0xe4, 0x60, 0x25, 0x40, 0x05, 0xc9, 0xa9, 0xf6, 0x3a, 0xf6, 0x63, 0x93,
0xe4, 0x39, 0x79, 0x82, 0x2f, 0xc2, 0xf5, 0x2c, 0x9f, 0xd4, 0x97, 0xce, 0x26, 0x04, 0xa9, 0xc5, 0xb5, 0xb4, 0x1f, 0x9e, 0x90, 0x9a, 0xf6, 0xb4, 0xf9, 0x91, 0x3b, 0x4a, 0x9e, 0x93, 0x27, 0xf8,
0xd2, 0xaa, 0x4c, 0xff, 0x9a, 0x77, 0x85, 0x9e, 0xd2, 0x67, 0x01, 0x8e, 0x92, 0x70, 0xbc, 0xe3, 0x3e, 0x5c, 0xcf, 0xf9, 0x49, 0x7d, 0xe9, 0x6c, 0x7a, 0x90, 0x5a, 0x2c, 0xad, 0xca, 0xf4, 0xaf,
0xd2, 0x51, 0x18, 0xa4, 0xba, 0x36, 0xcd, 0x64, 0x49, 0xf5, 0x97, 0x96, 0xce, 0x42, 0x9e, 0x68, 0x79, 0x57, 0xe8, 0x29, 0x7d, 0x16, 0xe0, 0x28, 0x09, 0xc7, 0x3b, 0x2e, 0x1d, 0x85, 0x41, 0xaa,
0x87, 0x0f, 0x23, 0x49, 0x4a, 0x32, 0xd7, 0xd4, 0x64, 0x17, 0xb5, 0x20, 0x05, 0x09, 0x2f, 0xb7, 0x6b, 0xd3, 0xbc, 0x96, 0x54, 0x7f, 0x69, 0xc9, 0x2d, 0xe4, 0x89, 0x76, 0xf8, 0x30, 0x52, 0xa6,
0x2d, 0xb2, 0x05, 0x90, 0x86, 0xcc, 0xd5, 0x51, 0x22, 0x17, 0x8d, 0x57, 0x6a, 0xaf, 0x20, 0xbe, 0x24, 0x73, 0x4d, 0x4d, 0x7d, 0x51, 0x0b, 0x52, 0x90, 0xfe, 0x72, 0xdb, 0x22, 0x5b, 0x00, 0x69,
0x7e, 0x08, 0xf5, 0x34, 0x06, 0xbb, 0x9a, 0xa6, 0x40, 0x1b, 0x11, 0x5b, 0x65, 0xc1, 0x73, 0x91, 0xc8, 0x5c, 0x1d, 0x25, 0x72, 0xd1, 0x78, 0xa5, 0xf6, 0x0a, 0xe2, 0xeb, 0x87, 0x50, 0x4f, 0x63,
0x51, 0xbb, 0x8d, 0x4b, 0x05, 0xa4, 0xc6, 0x96, 0x0a, 0xc3, 0x9d, 0x1e, 0x2c, 0xf2, 0x01, 0x2a, 0xb0, 0xab, 0x69, 0x42, 0xb4, 0x11, 0xb1, 0x55, 0x16, 0x3c, 0x17, 0x19, 0xb5, 0xdb, 0xb8, 0x54,
0x77, 0x04, 0x73, 0x33, 0xe4, 0x4c, 0x0a, 0xa2, 0x93, 0x4a, 0x9a, 0x0b, 0x83, 0x7b, 0x46, 0xb4, 0x40, 0x6a, 0x6c, 0xa9, 0x30, 0xdc, 0xe9, 0xc1, 0x22, 0x1f, 0xa0, 0x72, 0x47, 0x30, 0x53, 0x43,
0x82, 0x71, 0x2b, 0xcf, 0x0b, 0x61, 0xaa, 0x79, 0x04, 0x0b, 0xb9, 0xe8, 0x93, 0x12, 0xe9, 0x69, 0xce, 0xa4, 0x20, 0x3a, 0xa9, 0xa4, 0xb9, 0x30, 0xb8, 0x67, 0x44, 0x2b, 0x18, 0xb7, 0xf2, 0x2c,
0x01, 0x41, 0x25, 0xd2, 0x53, 0x03, 0x57, 0xf6, 0x32, 0x76, 0x39, 0x6f, 0x03, 0x9e, 0x80, 0xce, 0x11, 0xa6, 0x9a, 0x47, 0xb0, 0x90, 0x8b, 0x3e, 0x29, 0x91, 0x9e, 0x16, 0x10, 0x54, 0x22, 0x3d,
0xbd, 0xa4, 0x7f, 0xfa, 0xae, 0xb5, 0x71, 0xf7, 0xe6, 0x07, 0xff, 0x63, 0xe8, 0x25, 0xa7, 0x93, 0x35, 0x70, 0x65, 0x2f, 0x63, 0x97, 0xf3, 0x36, 0xe0, 0x09, 0xe8, 0xdc, 0x4b, 0xfa, 0xa7, 0xef,
0xe3, 0x5b, 0xfd, 0x70, 0xb4, 0xe9, 0xcb, 0x90, 0x82, 0xc8, 0xb0, 0xda, 0xf4, 0x83, 0xc1, 0x26, 0x5a, 0x1b, 0x77, 0x6f, 0x7e, 0xf0, 0x3f, 0x86, 0x5e, 0x72, 0x3a, 0x39, 0xbe, 0xd5, 0x0f, 0x47,
0xb6, 0x7c, 0x3c, 0x83, 0xbf, 0x9c, 0xfb, 0xe9, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x58, 0x14, 0x9b, 0xbe, 0x0c, 0x29, 0x88, 0x7c, 0xab, 0x4d, 0x3f, 0x18, 0x6c, 0x62, 0xcb, 0xc7, 0x33, 0xf8,
0xcd, 0xde, 0x6b, 0x57, 0x00, 0x00, 0x3b, 0xba, 0x9f, 0xfe, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xc6, 0x71, 0x9a, 0x79, 0x57,
0x00, 0x00,
} }

@ -1892,6 +1892,7 @@ message Invoice {
OPEN = 0; OPEN = 0;
SETTLED = 1; SETTLED = 1;
CANCELED = 2; CANCELED = 2;
ACCEPTED = 3;
} }
/** /**

@ -1162,7 +1162,8 @@
"enum": [ "enum": [
"OPEN", "OPEN",
"SETTLED", "SETTLED",
"CANCELED" "CANCELED",
"ACCEPTED"
], ],
"default": "OPEN" "default": "OPEN"
}, },

@ -17,33 +17,33 @@ func (hash Hash) String() string {
return hex.EncodeToString(hash[:]) return hex.EncodeToString(hash[:])
} }
// NewHash returns a new Hash from a byte slice. An error is returned if // MakeHash returns a new Hash from a byte slice. An error is returned if
// the number of bytes passed in is not HashSize. // the number of bytes passed in is not HashSize.
func NewHash(newHash []byte) (*Hash, error) { func MakeHash(newHash []byte) (Hash, error) {
nhlen := len(newHash) nhlen := len(newHash)
if nhlen != HashSize { if nhlen != HashSize {
return nil, fmt.Errorf("invalid hash length of %v, want %v", return Hash{}, fmt.Errorf("invalid hash length of %v, want %v",
nhlen, HashSize) nhlen, HashSize)
} }
var hash Hash var hash Hash
copy(hash[:], newHash) copy(hash[:], newHash)
return &hash, nil return hash, nil
} }
// NewHashFromStr creates a Hash from a hex hash string. // MakeHashFromStr creates a Hash from a hex hash string.
func NewHashFromStr(newHash string) (*Hash, error) { func MakeHashFromStr(newHash string) (Hash, error) {
// Return error if hash string is of incorrect length. // Return error if hash string is of incorrect length.
if len(newHash) != HashSize*2 { if len(newHash) != HashSize*2 {
return nil, fmt.Errorf("invalid hash string length of %v, "+ return Hash{}, fmt.Errorf("invalid hash string length of %v, "+
"want %v", len(newHash), HashSize*2) "want %v", len(newHash), HashSize*2)
} }
hash, err := hex.DecodeString(newHash) hash, err := hex.DecodeString(newHash)
if err != nil { if err != nil {
return nil, err return Hash{}, err
} }
return NewHash(hash) return MakeHash(hash)
} }

@ -53,3 +53,8 @@ func MakePreimageFromStr(newPreimage string) (Preimage, error) {
func (p *Preimage) Hash() Hash { func (p *Preimage) Hash() Hash {
return Hash(sha256.Sum256(p[:])) return Hash(sha256.Sum256(p[:]))
} }
// Matches returns whether this preimage is the preimage of the given hash.
func (p *Preimage) Matches(h Hash) bool {
return h == p.Hash()
}

@ -409,6 +409,11 @@ func (f FailFinalExpiryTooSoon) Error() string {
return f.Code().String() return f.Code().String()
} }
// NewFinalExpiryTooSoon creates new instance of the FailFinalExpiryTooSoon.
func NewFinalExpiryTooSoon() *FailFinalExpiryTooSoon {
return &FailFinalExpiryTooSoon{}
}
// FailInvalidOnionVersion is returned if the onion version byte is unknown. // FailInvalidOnionVersion is returned if the onion version byte is unknown.
// //
// NOTE: May be returned only by intermediate nodes. // NOTE: May be returned only by intermediate nodes.

@ -8,6 +8,7 @@ import (
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
) )
const ( const (
@ -152,7 +153,7 @@ func (m *missionControl) GraphPruneView() graphPruneView {
// view from Mission Control. An optional set of routing hints can be provided // view from Mission Control. An optional set of routing hints can be provided
// in order to populate additional edges to explore when finding a path to the // in order to populate additional edges to explore when finding a path to the
// payment's destination. // payment's destination.
func (m *missionControl) NewPaymentSession(routeHints [][]HopHint, func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
target Vertex) (*paymentSession, error) { target Vertex) (*paymentSession, error) {
viewSnapshot := m.GraphPruneView() viewSnapshot := m.GraphPruneView()

@ -39,28 +39,6 @@ const (
RiskFactorBillionths = 15 RiskFactorBillionths = 15
) )
// HopHint is a routing hint that contains the minimum information of a channel
// required for an intermediate hop in a route to forward the payment to the
// next. This should be ideally used for private channels, since they are not
// publicly advertised to the network for routing.
type HopHint struct {
// NodeID is the public key of the node at the start of the channel.
NodeID *btcec.PublicKey
// ChannelID is the unique identifier of the channel.
ChannelID uint64
// FeeBaseMSat is the base fee of the channel in millisatoshis.
FeeBaseMSat uint32
// FeeProportionalMillionths is the fee rate, in millionths of a
// satoshi, for every satoshi sent through the channel.
FeeProportionalMillionths uint32
// CLTVExpiryDelta is the time-lock delta of the channel.
CLTVExpiryDelta uint16
}
// Hop represents an intermediate or final node of the route. This naming // Hop represents an intermediate or final node of the route. This naming
// is in line with the definition given in BOLT #4: Onion Routing Protocol. // is in line with the definition given in BOLT #4: Onion Routing Protocol.
// The struct houses the channel along which this hop can be reached and // The struct houses the channel along which this hop can be reached and

@ -22,6 +22,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
) )
const ( const (
@ -1746,11 +1747,11 @@ func TestPathFindSpecExample(t *testing.T) {
// The CLTV expiry should be the current height plus 9 (the expiry for // The CLTV expiry should be the current height plus 9 (the expiry for
// the B -> C channel. // the B -> C channel.
if firstRoute.TotalTimeLock != if firstRoute.TotalTimeLock !=
startingHeight+DefaultFinalCLTVDelta { startingHeight+zpay32.DefaultFinalCLTVDelta {
t.Fatalf("wrong total time lock: got %v, expecting %v", t.Fatalf("wrong total time lock: got %v, expecting %v",
firstRoute.TotalTimeLock, firstRoute.TotalTimeLock,
startingHeight+DefaultFinalCLTVDelta) startingHeight+zpay32.DefaultFinalCLTVDelta)
} }
// Next, we'll set A as the source node so we can assert that we create // Next, we'll set A as the source node so we can assert that we create
@ -1845,11 +1846,11 @@ func TestPathFindSpecExample(t *testing.T) {
// The outgoing CLTV value itself should be the current height plus 30 // The outgoing CLTV value itself should be the current height plus 30
// to meet Carol's requirements. // to meet Carol's requirements.
if routes[0].Hops[0].OutgoingTimeLock != if routes[0].Hops[0].OutgoingTimeLock !=
startingHeight+DefaultFinalCLTVDelta { startingHeight+zpay32.DefaultFinalCLTVDelta {
t.Fatalf("wrong total time lock: got %v, expecting %v", t.Fatalf("wrong total time lock: got %v, expecting %v",
routes[0].Hops[0].OutgoingTimeLock, routes[0].Hops[0].OutgoingTimeLock,
startingHeight+DefaultFinalCLTVDelta) startingHeight+zpay32.DefaultFinalCLTVDelta)
} }
// For B -> C, we assert that the final hop also has the proper // For B -> C, we assert that the final hop also has the proper
@ -1860,11 +1861,11 @@ func TestPathFindSpecExample(t *testing.T) {
lastHop.AmtToForward, amt) lastHop.AmtToForward, amt)
} }
if lastHop.OutgoingTimeLock != if lastHop.OutgoingTimeLock !=
startingHeight+DefaultFinalCLTVDelta { startingHeight+zpay32.DefaultFinalCLTVDelta {
t.Fatalf("wrong total time lock: got %v, expecting %v", t.Fatalf("wrong total time lock: got %v, expecting %v",
lastHop.OutgoingTimeLock, lastHop.OutgoingTimeLock,
startingHeight+DefaultFinalCLTVDelta) startingHeight+zpay32.DefaultFinalCLTVDelta)
} }
// We'll also make similar assertions for the second route from A to C // We'll also make similar assertions for the second route from A to C
@ -1875,7 +1876,7 @@ func TestPathFindSpecExample(t *testing.T) {
t.Fatalf("wrong amount: got %v, expected %v", t.Fatalf("wrong amount: got %v, expected %v",
secondRoute.TotalAmount, expectedAmt) secondRoute.TotalAmount, expectedAmt)
} }
expectedTimeLock := startingHeight + daveFinalCLTV + DefaultFinalCLTVDelta expectedTimeLock := startingHeight + daveFinalCLTV + zpay32.DefaultFinalCLTVDelta
if secondRoute.TotalTimeLock != uint32(expectedTimeLock) { if secondRoute.TotalTimeLock != uint32(expectedTimeLock) {
t.Fatalf("wrong total time lock: got %v, expecting %v", t.Fatalf("wrong total time lock: got %v, expecting %v",
secondRoute.TotalTimeLock, expectedTimeLock) secondRoute.TotalTimeLock, expectedTimeLock)
@ -1885,7 +1886,7 @@ func TestPathFindSpecExample(t *testing.T) {
t.Fatalf("wrong forward amount: got %v, expected %v", t.Fatalf("wrong forward amount: got %v, expected %v",
onionPayload.AmtToForward, amt) onionPayload.AmtToForward, amt)
} }
expectedTimeLock = startingHeight + DefaultFinalCLTVDelta expectedTimeLock = startingHeight + zpay32.DefaultFinalCLTVDelta
if onionPayload.OutgoingTimeLock != uint32(expectedTimeLock) { if onionPayload.OutgoingTimeLock != uint32(expectedTimeLock) {
t.Fatalf("wrong outgoing time lock: got %v, expecting %v", t.Fatalf("wrong outgoing time lock: got %v, expecting %v",
onionPayload.OutgoingTimeLock, onionPayload.OutgoingTimeLock,
@ -1899,11 +1900,11 @@ func TestPathFindSpecExample(t *testing.T) {
lastHop.AmtToForward, amt) lastHop.AmtToForward, amt)
} }
if lastHop.OutgoingTimeLock != if lastHop.OutgoingTimeLock !=
startingHeight+DefaultFinalCLTVDelta { startingHeight+zpay32.DefaultFinalCLTVDelta {
t.Fatalf("wrong total time lock: got %v, expecting %v", t.Fatalf("wrong total time lock: got %v, expecting %v",
lastHop.OutgoingTimeLock, lastHop.OutgoingTimeLock,
startingHeight+DefaultFinalCLTVDelta) startingHeight+zpay32.DefaultFinalCLTVDelta)
} }
} }

@ -25,13 +25,10 @@ import (
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/multimutex" "github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
"github.com/lightningnetwork/lnd/zpay32"
) )
const ( const (
// DefaultFinalCLTVDelta is the default value to be used as the final
// CLTV delta for a route if one is unspecified.
DefaultFinalCLTVDelta = 9
// defaultPayAttemptTimeout is a duration that we'll use to determine // defaultPayAttemptTimeout is a duration that we'll use to determine
// if we should give up on a payment attempt. This will be used if a // if we should give up on a payment attempt. This will be used if a
// value isn't specified in the LightningNode struct. // value isn't specified in the LightningNode struct.
@ -1331,7 +1328,7 @@ func (r *ChannelRouter) FindRoutes(source, target Vertex,
var finalCLTVDelta uint16 var finalCLTVDelta uint16
if len(finalExpiry) == 0 { if len(finalExpiry) == 0 {
finalCLTVDelta = DefaultFinalCLTVDelta finalCLTVDelta = zpay32.DefaultFinalCLTVDelta
} else { } else {
finalCLTVDelta = finalExpiry[0] finalCLTVDelta = finalExpiry[0]
} }
@ -1551,7 +1548,7 @@ type LightningPayment struct {
// multiple routes, ensure the hop hints within each route are chained // multiple routes, ensure the hop hints within each route are chained
// together and sorted in forward order in order to reach the // together and sorted in forward order in order to reach the
// destination successfully. // destination successfully.
RouteHints [][]HopHint RouteHints [][]zpay32.HopHint
// OutgoingChannelID is the channel that needs to be taken to the first // OutgoingChannelID is the channel that needs to be taken to the first
// hop. If nil, any channel may be used. // hop. If nil, any channel may be used.
@ -1628,7 +1625,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
var finalCLTVDelta uint16 var finalCLTVDelta uint16
if payment.FinalCLTVDelta == nil { if payment.FinalCLTVDelta == nil {
finalCLTVDelta = DefaultFinalCLTVDelta finalCLTVDelta = zpay32.DefaultFinalCLTVDelta
} else { } else {
finalCLTVDelta = *payment.FinalCLTVDelta finalCLTVDelta = *payment.FinalCLTVDelta
} }

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
) )
// defaultNumRoutes is the default value for the maximum number of routes to // defaultNumRoutes is the default value for the maximum number of routes to
@ -186,7 +187,7 @@ func TestFindRoutesFeeSorting(t *testing.T) {
routes, err := ctx.router.FindRoutes( routes, err := ctx.router.FindRoutes(
ctx.router.selfNode.PubKeyBytes, ctx.router.selfNode.PubKeyBytes,
target, paymentAmt, noRestrictions, defaultNumRoutes, target, paymentAmt, noRestrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, zpay32.DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find any routes: %v", err) t.Fatalf("unable to find any routes: %v", err)
@ -248,7 +249,7 @@ func TestFindRoutesWithFeeLimit(t *testing.T) {
routes, err := ctx.router.FindRoutes( routes, err := ctx.router.FindRoutes(
ctx.router.selfNode.PubKeyBytes, ctx.router.selfNode.PubKeyBytes,
target, paymentAmt, restrictions, defaultNumRoutes, target, paymentAmt, restrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, zpay32.DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find any routes: %v", err) t.Fatalf("unable to find any routes: %v", err)
@ -1345,7 +1346,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
routes, err := ctx.router.FindRoutes( routes, err := ctx.router.FindRoutes(
ctx.router.selfNode.PubKeyBytes, ctx.router.selfNode.PubKeyBytes,
targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes, targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, zpay32.DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find any routes: %v", err) t.Fatalf("unable to find any routes: %v", err)
@ -1391,7 +1392,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
routes, err = ctx.router.FindRoutes( routes, err = ctx.router.FindRoutes(
ctx.router.selfNode.PubKeyBytes, ctx.router.selfNode.PubKeyBytes,
targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes, targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, zpay32.DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find any routes: %v", err) t.Fatalf("unable to find any routes: %v", err)

@ -2,7 +2,6 @@ package main
import ( import (
"bytes" "bytes"
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
@ -39,6 +38,7 @@ import (
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
@ -422,7 +422,8 @@ func newRPCServer(s *server, macService *macaroons.Service,
// server configuration struct. // server configuration struct.
err := subServerCgs.PopulateDependencies( err := subServerCgs.PopulateDependencies(
s.cc, networkDir, macService, atpl, invoiceRegistry, s.cc, networkDir, macService, atpl, invoiceRegistry,
activeNetParams.Params, s.chanRouter, s.htlcSwitch, activeNetParams.Params, s.chanRouter,
s.nodeSigner, s.chanDB,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -2796,7 +2797,7 @@ type rpcPaymentIntent struct {
dest routing.Vertex dest routing.Vertex
rHash [32]byte rHash [32]byte
cltvDelta uint16 cltvDelta uint16
routeHints [][]routing.HopHint routeHints [][]zpay32.HopHint
outgoingChannelID *uint64 outgoingChannelID *uint64
routes []*routing.Route routes []*routing.Route
@ -3277,309 +3278,51 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context,
func (r *rpcServer) AddInvoice(ctx context.Context, func (r *rpcServer) AddInvoice(ctx context.Context,
invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) {
var paymentPreimage [32]byte defaultDelta := cfg.Bitcoin.TimeLockDelta
if registeredChains.PrimaryChain() == litecoinChain {
defaultDelta = cfg.Litecoin.TimeLockDelta
}
switch { addInvoiceCfg := &invoicesrpc.AddInvoiceConfig{
// If a preimage wasn't specified, then we'll generate a new preimage AddInvoice: r.server.invoices.AddInvoice,
// from fresh cryptographic randomness. IsChannelActive: r.server.htlcSwitch.HasActiveLink,
case len(invoice.RPreimage) == 0: ChainParams: activeNetParams.Params,
if _, err := rand.Read(paymentPreimage[:]); err != nil { NodeSigner: r.server.nodeSigner,
MaxPaymentMSat: maxPaymentMSat,
DefaultCLTVExpiry: defaultDelta,
ChanDB: r.server.chanDB,
}
addInvoiceData := &invoicesrpc.AddInvoiceData{
Memo: invoice.Memo,
Receipt: invoice.Receipt,
Value: btcutil.Amount(invoice.Value),
DescriptionHash: invoice.DescriptionHash,
Expiry: invoice.Expiry,
FallbackAddr: invoice.FallbackAddr,
CltvExpiry: invoice.CltvExpiry,
Private: invoice.Private,
}
if invoice.RPreimage != nil {
preimage, err := lntypes.MakePreimage(invoice.RPreimage)
if err != nil {
return nil, err return nil, err
} }
addInvoiceData.Preimage = &preimage
// Otherwise, if a preimage was specified, then it MUST be exactly
// 32-bytes.
case len(invoice.RPreimage) > 0 && len(invoice.RPreimage) != 32:
return nil, fmt.Errorf("payment preimage must be exactly "+
"32 bytes, is instead %v", len(invoice.RPreimage))
// If the preimage meets the size specifications, then it can be used
// as is.
default:
copy(paymentPreimage[:], invoice.RPreimage[:])
} }
// The size of the memo, receipt and description hash attached must not hash, dbInvoice, err := invoicesrpc.AddInvoice(
// exceed the maximum values for either of the fields. ctx, addInvoiceCfg, addInvoiceData,
if len(invoice.Memo) > channeldb.MaxMemoSize {
return nil, fmt.Errorf("memo too large: %v bytes "+
"(maxsize=%v)", len(invoice.Memo), channeldb.MaxMemoSize)
}
if len(invoice.Receipt) > channeldb.MaxReceiptSize {
return nil, fmt.Errorf("receipt too large: %v bytes "+
"(maxsize=%v)", len(invoice.Receipt), channeldb.MaxReceiptSize)
}
if len(invoice.DescriptionHash) > 0 && len(invoice.DescriptionHash) != 32 {
return nil, fmt.Errorf("description hash is %v bytes, must be %v",
len(invoice.DescriptionHash), channeldb.MaxPaymentRequestSize)
}
// The value of the invoice must not be negative.
if invoice.Value < 0 {
return nil, fmt.Errorf("payments of negative value "+
"are not allowed, value is %v", invoice.Value)
}
amt := btcutil.Amount(invoice.Value)
amtMSat := lnwire.NewMSatFromSatoshis(amt)
// The value of the invoice must also not exceed the current soft-limit
// on the largest payment within the network.
if amtMSat > maxPaymentMSat {
return nil, fmt.Errorf("payment of %v is too large, max "+
"payment allowed is %v", amt, maxPaymentMSat.ToSatoshis())
}
// Next, generate the payment hash itself from the preimage. This will
// be used by clients to query for the state of a particular invoice.
rHash := sha256.Sum256(paymentPreimage[:])
// We also create an encoded payment request which allows the
// caller to compactly send the invoice to the payer. We'll create a
// list of options to be added to the encoded payment request. For now
// we only support the required fields description/description_hash,
// expiry, fallback address, and the amount field.
var options []func(*zpay32.Invoice)
// We only include the amount in the invoice if it is greater than 0.
// By not including the amount, we enable the creation of invoices that
// allow the payee to specify the amount of satoshis they wish to send.
if amtMSat > 0 {
options = append(options, zpay32.Amount(amtMSat))
}
// If specified, add a fallback address to the payment request.
if len(invoice.FallbackAddr) > 0 {
addr, err := btcutil.DecodeAddress(invoice.FallbackAddr,
activeNetParams.Params)
if err != nil {
return nil, fmt.Errorf("invalid fallback address: %v",
err)
}
options = append(options, zpay32.FallbackAddr(addr))
}
// If expiry is set, specify it. If it is not provided, no expiry time
// will be explicitly added to this payment request, which will imply
// the default 3600 seconds.
if invoice.Expiry > 0 {
// We'll ensure that the specified expiry is restricted to sane
// number of seconds. As a result, we'll reject an invoice with
// an expiry greater than 1 year.
maxExpiry := time.Hour * 24 * 365
expSeconds := invoice.Expiry
if float64(expSeconds) > maxExpiry.Seconds() {
return nil, fmt.Errorf("expiry of %v seconds "+
"greater than max expiry of %v seconds",
float64(expSeconds), maxExpiry.Seconds())
}
expiry := time.Duration(invoice.Expiry) * time.Second
options = append(options, zpay32.Expiry(expiry))
}
// If the description hash is set, then we add it do the list of options.
// If not, use the memo field as the payment request description.
if len(invoice.DescriptionHash) > 0 {
var descHash [32]byte
copy(descHash[:], invoice.DescriptionHash[:])
options = append(options, zpay32.DescriptionHash(descHash))
} else {
// Use the memo field as the description. If this is not set
// this will just be an empty string.
options = append(options, zpay32.Description(invoice.Memo))
}
// We'll use our current default CLTV value unless one was specified as
// an option on the command line when creating an invoice.
switch {
case invoice.CltvExpiry > math.MaxUint16:
return nil, fmt.Errorf("CLTV delta of %v is too large, max "+
"accepted is: %v", invoice.CltvExpiry, math.MaxUint16)
case invoice.CltvExpiry != 0:
options = append(options,
zpay32.CLTVExpiry(invoice.CltvExpiry))
default:
// TODO(roasbeef): assumes set delta between versions
defaultDelta := cfg.Bitcoin.TimeLockDelta
if registeredChains.PrimaryChain() == litecoinChain {
defaultDelta = cfg.Litecoin.TimeLockDelta
}
options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
}
// If we were requested to include routing hints in the invoice, then
// we'll fetch all of our available private channels and create routing
// hints for them.
if invoice.Private {
openChannels, err := r.server.chanDB.FetchAllChannels()
if err != nil {
return nil, fmt.Errorf("could not fetch all channels")
}
graph := r.server.chanDB.ChannelGraph()
numHints := 0
for _, channel := range openChannels {
// We'll restrict the number of individual route hints
// to 20 to avoid creating overly large invoices.
if numHints > 20 {
break
}
// Since we're only interested in our private channels,
// we'll skip public ones.
isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
if isPublic {
continue
}
// Make sure the counterparty has enough balance in the
// channel for our amount. We do this in order to reduce
// payment errors when attempting to use this channel
// as a hint.
chanPoint := lnwire.NewChanIDFromOutPoint(
&channel.FundingOutpoint,
)
if amtMSat >= channel.LocalCommitment.RemoteBalance {
rpcsLog.Debugf("Skipping channel %v due to "+
"not having enough remote balance",
chanPoint)
continue
}
// Make sure the channel is active.
link, err := r.server.htlcSwitch.GetLink(chanPoint)
if err != nil {
rpcsLog.Errorf("Unable to get link for "+
"channel %v: %v", chanPoint, err)
continue
}
if !link.EligibleToForward() {
rpcsLog.Debugf("Skipping channel %v due to not "+
"being eligible to forward payments",
chanPoint)
continue
}
// To ensure we don't leak unadvertised nodes, we'll
// make sure our counterparty is publicly advertised
// within the network. Otherwise, we'll end up leaking
// information about nodes that intend to stay
// unadvertised, like in the case of a node only having
// private channels.
var remotePub [33]byte
copy(remotePub[:], channel.IdentityPub.SerializeCompressed())
isRemoteNodePublic, err := graph.IsPublicNode(remotePub)
if err != nil {
rpcsLog.Errorf("Unable to determine if node %x "+
"is advertised: %v", remotePub, err)
continue
}
if !isRemoteNodePublic {
rpcsLog.Debugf("Skipping channel %v due to "+
"counterparty %x being unadvertised",
chanPoint, remotePub)
continue
}
// Fetch the policies for each end of the channel.
chanID := channel.ShortChanID().ToUint64()
info, p1, p2, err := graph.FetchChannelEdgesByID(chanID)
if err != nil {
rpcsLog.Errorf("Unable to fetch the routing "+
"policies for the edges of the channel "+
"%v: %v", chanPoint, err)
continue
}
// Now, we'll need to determine which is the correct
// policy for HTLCs being sent from the remote node.
var remotePolicy *channeldb.ChannelEdgePolicy
if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
remotePolicy = p1
} else {
remotePolicy = p2
}
// If for some reason we don't yet have the edge for
// the remote party, then we'll just skip adding this
// channel as a routing hint.
if remotePolicy == nil {
continue
}
// Finally, create the routing hint for this channel and
// add it to our list of route hints.
hint := routing.HopHint{
NodeID: channel.IdentityPub,
ChannelID: chanID,
FeeBaseMSat: uint32(remotePolicy.FeeBaseMSat),
FeeProportionalMillionths: uint32(
remotePolicy.FeeProportionalMillionths,
),
CLTVExpiryDelta: remotePolicy.TimeLockDelta,
}
// Include the route hint in our set of options that
// will be used when creating the invoice.
routeHint := []routing.HopHint{hint}
options = append(options, zpay32.RouteHint(routeHint))
numHints++
}
}
// Create and encode the payment request as a bech32 (zpay32) string.
creationDate := time.Now()
payReq, err := zpay32.NewInvoice(
activeNetParams.Params, rHash, creationDate, options...,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
payReqString, err := payReq.Encode(
zpay32.MessageSigner{
SignCompact: r.server.nodeSigner.SignDigestCompact,
},
)
if err != nil {
return nil, err
}
newInvoice := &channeldb.Invoice{
CreationDate: creationDate,
Memo: []byte(invoice.Memo),
Receipt: invoice.Receipt,
PaymentRequest: []byte(payReqString),
Terms: channeldb.ContractTerm{
Value: amtMSat,
},
}
copy(newInvoice.Terms.PaymentPreimage[:], paymentPreimage[:])
rpcsLog.Tracef("[addinvoice] adding new invoice %v",
newLogClosure(func() string {
return spew.Sdump(newInvoice)
}),
)
// With all sanity checks passed, write the invoice to the database.
addIndex, err := r.server.invoices.AddInvoice(newInvoice, rHash)
if err != nil {
return nil, err
}
return &lnrpc.AddInvoiceResponse{ return &lnrpc.AddInvoiceResponse{
RHash: rHash[:], AddIndex: dbInvoice.AddIndex,
PaymentRequest: payReqString, PaymentRequest: string(dbInvoice.PaymentRequest),
AddIndex: addIndex, RHash: hash[:],
}, nil }, nil
} }

@ -46,6 +46,7 @@ import (
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
"github.com/lightningnetwork/lnd/zpay32"
) )
const ( const (
@ -284,6 +285,14 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
readBufferPool, runtime.NumCPU(), pool.DefaultWorkerTimeout, readBufferPool, runtime.NumCPU(), pool.DefaultWorkerTimeout,
) )
decodeFinalCltvExpiry := func(payReq string) (uint32, error) {
invoice, err := zpay32.Decode(payReq, activeNetParams.Params)
if err != nil {
return 0, err
}
return uint32(invoice.MinFinalCLTVExpiry()), nil
}
s := &server{ s := &server{
chanDB: chanDB, chanDB: chanDB,
cc: cc, cc: cc,
@ -291,7 +300,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
writePool: writePool, writePool: writePool,
readPool: readPool, readPool: readPool,
invoices: invoices.NewRegistry(chanDB, activeNetParams.Params), invoices: invoices.NewRegistry(chanDB, decodeFinalCltvExpiry),
channelNotifier: channelnotifier.New(chanDB), channelNotifier: channelnotifier.New(chanDB),
@ -778,7 +787,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
}, },
DisableChannel: s.chanStatusMgr.RequestDisable, DisableChannel: s.chanStatusMgr.RequestDisable,
Sweeper: s.sweeper, Sweeper: s.sweeper,
SettleInvoice: s.invoices.SettleInvoice, Registry: s.invoices,
NotifyClosedChannel: s.channelNotifier.NotifyClosedChannelEvent, NotifyClosedChannel: s.channelNotifier.NotifyClosedChannelEvent,
}, chanDB) }, chanDB)

@ -6,6 +6,8 @@ import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnrpc/autopilotrpc" "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"
"github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc"
@ -14,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/netann"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
) )
@ -64,8 +67,11 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
networkDir string, macService *macaroons.Service, networkDir string, macService *macaroons.Service,
atpl *autopilot.Manager, atpl *autopilot.Manager,
invoiceRegistry *invoices.InvoiceRegistry, invoiceRegistry *invoices.InvoiceRegistry,
htlcSwitch *htlcswitch.Switch,
activeNetParams *chaincfg.Params, activeNetParams *chaincfg.Params,
chanRouter *routing.ChannelRouter) error { chanRouter *routing.ChannelRouter,
nodeSigner *netann.NodeSigner,
chanDB *channeldb.DB) error {
// First, we'll use reflect to obtain a version of the config struct // First, we'll use reflect to obtain a version of the config struct
// that allows us to programmatically inspect its fields. // that allows us to programmatically inspect its fields.
@ -90,9 +96,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
continue continue
} }
switch cfg := field.Interface().(type) { switch subCfg := field.Interface().(type) {
case *signrpc.Config: case *signrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(subCfg)
subCfgValue.FieldByName("MacService").Set( subCfgValue.FieldByName("MacService").Set(
reflect.ValueOf(macService), reflect.ValueOf(macService),
@ -105,7 +111,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
) )
case *walletrpc.Config: case *walletrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(subCfg)
subCfgValue.FieldByName("NetworkDir").Set( subCfgValue.FieldByName("NetworkDir").Set(
reflect.ValueOf(networkDir), reflect.ValueOf(networkDir),
@ -124,14 +130,14 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
) )
case *autopilotrpc.Config: case *autopilotrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(subCfg)
subCfgValue.FieldByName("Manager").Set( subCfgValue.FieldByName("Manager").Set(
reflect.ValueOf(atpl), reflect.ValueOf(atpl),
) )
case *chainrpc.Config: case *chainrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(subCfg)
subCfgValue.FieldByName("NetworkDir").Set( subCfgValue.FieldByName("NetworkDir").Set(
reflect.ValueOf(networkDir), reflect.ValueOf(networkDir),
@ -144,7 +150,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
) )
case *invoicesrpc.Config: case *invoicesrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(subCfg)
subCfgValue.FieldByName("NetworkDir").Set( subCfgValue.FieldByName("NetworkDir").Set(
reflect.ValueOf(networkDir), reflect.ValueOf(networkDir),
@ -155,9 +161,28 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
subCfgValue.FieldByName("InvoiceRegistry").Set( subCfgValue.FieldByName("InvoiceRegistry").Set(
reflect.ValueOf(invoiceRegistry), reflect.ValueOf(invoiceRegistry),
) )
subCfgValue.FieldByName("IsChannelActive").Set(
reflect.ValueOf(htlcSwitch.HasActiveLink),
)
subCfgValue.FieldByName("ChainParams").Set( subCfgValue.FieldByName("ChainParams").Set(
reflect.ValueOf(activeNetParams), reflect.ValueOf(activeNetParams),
) )
subCfgValue.FieldByName("NodeSigner").Set(
reflect.ValueOf(nodeSigner),
)
subCfgValue.FieldByName("MaxPaymentMSat").Set(
reflect.ValueOf(maxPaymentMSat),
)
defaultDelta := cfg.Bitcoin.TimeLockDelta
if registeredChains.PrimaryChain() == litecoinChain {
defaultDelta = cfg.Litecoin.TimeLockDelta
}
subCfgValue.FieldByName("DefaultCLTVExpiry").Set(
reflect.ValueOf(defaultDelta),
)
subCfgValue.FieldByName("ChanDB").Set(
reflect.ValueOf(chanDB),
)
case *routerrpc.Config: case *routerrpc.Config:
subCfgValue := extractReflectValue(cfg) subCfgValue := extractReflectValue(cfg)

@ -85,7 +85,9 @@ func (p *preimageBeacon) LookupPreimage(
// If we've found the invoice, then we can return the preimage // If we've found the invoice, then we can return the preimage
// directly. // directly.
if err != channeldb.ErrInvoiceNotFound { if err != channeldb.ErrInvoiceNotFound &&
invoice.Terms.PaymentPreimage != channeldb.UnknownPreimage {
return invoice.Terms.PaymentPreimage, true return invoice.Terms.PaymentPreimage, true
} }

31
zpay32/hophint.go Normal file

@ -0,0 +1,31 @@
package zpay32
import "github.com/btcsuite/btcd/btcec"
const (
// DefaultFinalCLTVDelta is the default value to be used as the final
// CLTV delta for a route if one is unspecified.
DefaultFinalCLTVDelta = 9
)
// HopHint is a routing hint that contains the minimum information of a channel
// required for an intermediate hop in a route to forward the payment to the
// next. This should be ideally used for private channels, since they are not
// publicly advertised to the network for routing.
type HopHint struct {
// NodeID is the public key of the node at the start of the channel.
NodeID *btcec.PublicKey
// ChannelID is the unique identifier of the channel.
ChannelID uint64
// FeeBaseMSat is the base fee of the channel in millisatoshis.
FeeBaseMSat uint32
// FeeProportionalMillionths is the fee rate, in millionths of a
// satoshi, for every satoshi sent through the channel.
FeeProportionalMillionths uint32
// CLTVExpiryDelta is the time-lock delta of the channel.
CLTVExpiryDelta uint16
}

@ -13,7 +13,6 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/bech32" "github.com/btcsuite/btcutil/bech32"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
) )
const ( const (
@ -146,7 +145,7 @@ type Invoice struct {
// represent private routes. // represent private routes.
// //
// NOTE: This is optional. // NOTE: This is optional.
RouteHints [][]routing.HopHint RouteHints [][]HopHint
} }
// Amount is a functional option that allows callers of NewInvoice to set the // Amount is a functional option that allows callers of NewInvoice to set the
@ -214,7 +213,7 @@ func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
// RouteHint is a functional option that allows callers of NewInvoice to add // RouteHint is a functional option that allows callers of NewInvoice to add
// one or more hop hints that represent a private route to the destination. // one or more hop hints that represent a private route to the destination.
func RouteHint(routeHint []routing.HopHint) func(*Invoice) { func RouteHint(routeHint []HopHint) func(*Invoice) {
return func(i *Invoice) { return func(i *Invoice) {
i.RouteHints = append(i.RouteHints, routeHint) i.RouteHints = append(i.RouteHints, routeHint)
} }
@ -476,7 +475,7 @@ func (invoice *Invoice) MinFinalCLTVExpiry() uint64 {
return *invoice.minFinalCLTVExpiry return *invoice.minFinalCLTVExpiry
} }
return routing.DefaultFinalCLTVDelta return DefaultFinalCLTVDelta
} }
// validateInvoice does a sanity check of the provided Invoice, making sure it // validateInvoice does a sanity check of the provided Invoice, making sure it
@ -843,7 +842,7 @@ func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, erro
// parseRouteHint converts the data (encoded in base32) into an array containing // parseRouteHint converts the data (encoded in base32) into an array containing
// one or more routing hop hints that represent a single route hint. // one or more routing hop hints that represent a single route hint.
func parseRouteHint(data []byte) ([]routing.HopHint, error) { func parseRouteHint(data []byte) ([]HopHint, error) {
base256Data, err := bech32.ConvertBits(data, 5, 8, false) base256Data, err := bech32.ConvertBits(data, 5, 8, false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -854,10 +853,10 @@ func parseRouteHint(data []byte) ([]routing.HopHint, error) {
"got %d", hopHintLen, len(base256Data)) "got %d", hopHintLen, len(base256Data))
} }
var routeHint []routing.HopHint var routeHint []HopHint
for len(base256Data) > 0 { for len(base256Data) > 0 {
hopHint := routing.HopHint{} hopHint := HopHint{}
hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33], btcec.S256()) hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
if err != nil { if err != nil {
return nil, err return nil, err

@ -12,7 +12,6 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/bech32" "github.com/btcsuite/btcutil/bech32"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
) )
// TestDecodeAmount ensures that the amount string in the hrp of the Invoice // TestDecodeAmount ensures that the amount string in the hrp of the Invoice
@ -738,7 +737,7 @@ func TestParseRouteHint(t *testing.T) {
tests := []struct { tests := []struct {
data []byte data []byte
valid bool valid bool
result []routing.HopHint result []HopHint
}{ }{
{ {
data: []byte{0x0, 0x0, 0x0, 0x0}, data: []byte{0x0, 0x0, 0x0, 0x0},
@ -747,7 +746,7 @@ func TestParseRouteHint(t *testing.T) {
{ {
data: []byte{}, data: []byte{},
valid: true, valid: true,
result: []routing.HopHint{}, result: []HopHint{},
}, },
{ {
data: testSingleHopData, data: testSingleHopData,

@ -17,7 +17,6 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
litecoinCfg "github.com/ltcsuite/ltcd/chaincfg" litecoinCfg "github.com/ltcsuite/ltcd/chaincfg"
) )
@ -53,7 +52,7 @@ var (
testHopHintPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255") testHopHintPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
testHopHintPubkey2, _ = btcec.ParsePubKey(testHopHintPubkeyBytes2, btcec.S256()) testHopHintPubkey2, _ = btcec.ParsePubKey(testHopHintPubkeyBytes2, btcec.S256())
testSingleHop = []routing.HopHint{ testSingleHop = []HopHint{
{ {
NodeID: testHopHintPubkey1, NodeID: testHopHintPubkey1,
ChannelID: 0x0102030405060708, ChannelID: 0x0102030405060708,
@ -62,7 +61,7 @@ var (
CLTVExpiryDelta: 3, CLTVExpiryDelta: 3,
}, },
} }
testDoubleHop = []routing.HopHint{ testDoubleHop = []HopHint{
{ {
NodeID: testHopHintPubkey1, NodeID: testHopHintPubkey1,
ChannelID: 0x0102030405060708, ChannelID: 0x0102030405060708,
@ -414,7 +413,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash, DescriptionHash: &testDescriptionHash,
Destination: testPubKey, Destination: testPubKey,
FallbackAddr: testRustyAddr, FallbackAddr: testRustyAddr,
RouteHints: [][]routing.HopHint{testSingleHop}, RouteHints: [][]HopHint{testSingleHop},
} }
}, },
beforeEncoding: func(i *Invoice) { beforeEncoding: func(i *Invoice) {
@ -437,7 +436,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash, DescriptionHash: &testDescriptionHash,
Destination: testPubKey, Destination: testPubKey,
FallbackAddr: testRustyAddr, FallbackAddr: testRustyAddr,
RouteHints: [][]routing.HopHint{testDoubleHop}, RouteHints: [][]HopHint{testDoubleHop},
} }
}, },
beforeEncoding: func(i *Invoice) { beforeEncoding: func(i *Invoice) {
@ -844,7 +843,7 @@ func compareHashes(a, b *[32]byte) bool {
return bytes.Equal(a[:], b[:]) return bytes.Equal(a[:], b[:])
} }
func compareRouteHints(a, b []routing.HopHint) error { func compareRouteHints(a, b []HopHint) error {
if len(a) != len(b) { if len(a) != len(b) {
return fmt.Errorf("expected len routingInfo %d, got %d", return fmt.Errorf("expected len routingInfo %d, got %d",
len(a), len(b)) len(a), len(b))