Merge pull request #1125 from Roasbeef/streaming-invoice-improvements

channeldb+lnrpc: re-work invoicing API to implement robust streaming notification delivery
This commit is contained in:
Olaoluwa Osuntokun 2018-07-10 20:42:00 -07:00 committed by GitHub
commit 93207608b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1771 additions and 532 deletions

@ -47,6 +47,12 @@ var (
number: 1, number: 1,
migration: migrateNodeAndEdgeUpdateIndex, migration: migrateNodeAndEdgeUpdateIndex,
}, },
{
// The DB version that added the invoice event time
// series.
number: 2,
migration: migrateInvoiceTimeSeries,
},
} }
// Big endian is the preferred byte order, due to cursor scans over // Big endian is the preferred byte order, due to cursor scans over

@ -70,7 +70,7 @@ func TestInvoiceWorkflow(t *testing.T) {
// 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); err != nil {
t.Fatalf("unable to find invoice: %v", err) t.Fatalf("unable to find invoice: %v", err)
} }
@ -82,15 +82,24 @@ func TestInvoiceWorkflow(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to find invoice: %v", err) t.Fatalf("unable to find invoice: %v", err)
} }
if !reflect.DeepEqual(fakeInvoice, dbInvoice) { if !reflect.DeepEqual(*fakeInvoice, dbInvoice) {
t.Fatalf("invoice fetched from db doesn't match original %v vs %v", t.Fatalf("invoice fetched from db doesn't match original %v vs %v",
spew.Sdump(fakeInvoice), spew.Sdump(dbInvoice)) spew.Sdump(fakeInvoice), spew.Sdump(dbInvoice))
} }
// The add index of the invoice retrieved from the database should now
// be fully populated. As this is the first index written to the DB,
// the addIndex should be 1.
if dbInvoice.AddIndex != 1 {
t.Fatalf("wrong add index: expected %v, got %v", 1,
dbInvoice.AddIndex)
}
// Settle the invoice, the version retrieved from the database should // Settle the invoice, the version retrieved from the database should
// 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
if err := db.SettleInvoice(paymentHash); err != nil { payAmt := fakeInvoice.Terms.Value * 2
if _, err := db.SettleInvoice(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)
@ -100,14 +109,24 @@ func TestInvoiceWorkflow(t *testing.T) {
if !dbInvoice2.Terms.Settled { if !dbInvoice2.Terms.Settled {
t.Fatalf("invoice should now be settled but isn't") t.Fatalf("invoice should now be settled but isn't")
} }
if dbInvoice2.SettleDate.IsZero() { if dbInvoice2.SettleDate.IsZero() {
t.Fatalf("invoice should have non-zero SettledDate but isn't") t.Fatalf("invoice should have non-zero SettledDate but isn't")
} }
// Our 2x payment should be reflected, and also the settle index of 1
// should also have been committed for this index.
if dbInvoice2.AmtPaid != payAmt {
t.Fatalf("wrong amt paid: expected %v, got %v", payAmt,
dbInvoice2.AmtPaid)
}
if dbInvoice2.SettleIndex != 1 {
t.Fatalf("wrong settle index: expected %v, got %v", 1,
dbInvoice2.SettleIndex)
}
// 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); 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)
} }
@ -119,18 +138,18 @@ func TestInvoiceWorkflow(t *testing.T) {
t.Fatalf("lookup should have failed, instead %v", err) t.Fatalf("lookup should have failed, instead %v", err)
} }
// Add 100 random invoices. // Add 10 random invoices.
const numInvoices = 10 const numInvoices = 10
amt := lnwire.NewMSatFromSatoshis(1000) amt := lnwire.NewMSatFromSatoshis(1000)
invoices := make([]*Invoice, numInvoices+1) invoices := make([]*Invoice, numInvoices+1)
invoices[0] = dbInvoice2 invoices[0] = &dbInvoice2
for i := 1; i < len(invoices)-1; i++ { for i := 1; i < len(invoices)-1; i++ {
invoice, err := randInvoice(amt) invoice, err := randInvoice(amt)
if err != nil { if err != nil {
t.Fatalf("unable to create invoice: %v", err) t.Fatalf("unable to create invoice: %v", err)
} }
if err := db.AddInvoice(invoice); err != nil { if _, err := db.AddInvoice(invoice); err != nil {
t.Fatalf("unable to add invoice %v", err) t.Fatalf("unable to add invoice %v", err)
} }
@ -148,10 +167,150 @@ func TestInvoiceWorkflow(t *testing.T) {
// order (and the primary key should be incremented with each // order (and the primary key should be incremented with each
// insertion). // insertion).
for i := 0; i < len(invoices)-1; i++ { for i := 0; i < len(invoices)-1; i++ {
if !reflect.DeepEqual(invoices[i], dbInvoices[i]) { if !reflect.DeepEqual(*invoices[i], dbInvoices[i]) {
t.Fatalf("retrieved invoices don't match %v vs %v", t.Fatalf("retrieved invoices don't match %v vs %v",
spew.Sdump(invoices[i]), spew.Sdump(invoices[i]),
spew.Sdump(dbInvoices[i])) spew.Sdump(dbInvoices[i]))
} }
} }
} }
// TestInvoiceTimeSeries tests that newly added invoices invoices, as well as
// settled invoices are added to the database are properly placed in the add
// add or settle index which serves as an event time series.
func TestInvoiceAddTimeSeries(t *testing.T) {
t.Parallel()
db, cleanUp, err := makeTestDB()
defer cleanUp()
if err != nil {
t.Fatalf("unable to make test db: %v", err)
}
// We'll start off by creating 20 random invoices, and inserting them
// into the database.
const numInvoices = 20
amt := lnwire.NewMSatFromSatoshis(1000)
invoices := make([]Invoice, numInvoices)
for i := 0; i < len(invoices); i++ {
invoice, err := randInvoice(amt)
if err != nil {
t.Fatalf("unable to create invoice: %v", err)
}
if _, err := db.AddInvoice(invoice); err != nil {
t.Fatalf("unable to add invoice %v", err)
}
invoices[i] = *invoice
}
// With the invoices constructed, we'll now create a series of queries
// that we'll use to assert expected return values of
// InvoicesAddedSince.
addQueries := []struct {
sinceAddIndex uint64
resp []Invoice
}{
// If we specify a value of zero, we shouldn't get any invoices
// back.
{
sinceAddIndex: 0,
},
// If we specify a value well beyond the number of inserted
// invoices, we shouldn't get any invoices back.
{
sinceAddIndex: 99999999,
},
// Using an index of 1 should result in all values, but the
// first one being returned.
{
sinceAddIndex: 1,
resp: invoices[1:],
},
// If we use an index of 10, then we should retrieve the
// reaming 10 invoices.
{
sinceAddIndex: 10,
resp: invoices[10:],
},
}
for i, query := range addQueries {
resp, err := db.InvoicesAddedSince(query.sinceAddIndex)
if err != nil {
t.Fatalf("unable to query: %v", err)
}
if !reflect.DeepEqual(query.resp, resp) {
t.Fatalf("test #%v: expected %v, got %v", i,
spew.Sdump(query.resp), spew.Sdump(resp))
}
}
// We'll now only settle the latter half of each of those invoices.
for i := 10; i < len(invoices); i++ {
invoice := &invoices[i]
paymentHash := sha256.Sum256(
invoice.Terms.PaymentPreimage[:],
)
_, err := db.SettleInvoice(paymentHash, 0)
if err != nil {
t.Fatalf("unable to settle invoice: %v", err)
}
}
invoices, err = db.FetchAllInvoices(false)
if err != nil {
t.Fatalf("unable to fetch invoices: %v", err)
}
// We'll slice off the first 10 invoices, as we only settled the last
// 10.
invoices = invoices[10:]
// We'll now prepare an additional set of queries to ensure the settle
// time series has properly been maintained in the database.
settleQueries := []struct {
sinceSettleIndex uint64
resp []Invoice
}{
// If we specify a value of zero, we shouldn't get any settled
// invoices back.
{
sinceSettleIndex: 0,
},
// If we specify a value well beyond the number of settled
// invoices, we shouldn't get any invoices back.
{
sinceSettleIndex: 99999999,
},
// Using an index of 1 should result in the final 10 invoices
// being returned, as we only settled those.
{
sinceSettleIndex: 1,
resp: invoices[1:],
},
}
for i, query := range settleQueries {
resp, err := db.InvoicesSettledSince(query.sinceSettleIndex)
if err != nil {
t.Fatalf("unable to query: %v", err)
}
if !reflect.DeepEqual(query.resp, resp) {
t.Fatalf("test #%v: expected %v, got %v", i,
spew.Sdump(query.resp), spew.Sdump(resp))
}
}
}

@ -26,6 +26,8 @@ var (
// index is used to detect duplicates, and also to provide a fast path // index is used to detect duplicates, and also to provide a fast path
// for looking up incoming HTLCs to determine if we're able to settle // for looking up incoming HTLCs to determine if we're able to settle
// them fully. // them fully.
//
// maps: payHash => invoiceIndex
invoiceIndexBucket = []byte("paymenthashes") invoiceIndexBucket = []byte("paymenthashes")
// numInvoicesKey is the name of key which houses the auto-incrementing // numInvoicesKey is the name of key which houses the auto-incrementing
@ -34,6 +36,26 @@ var (
// stored within the invoiceIndexBucket. Within the invoiceBucket // stored within the invoiceIndexBucket. Within the invoiceBucket
// invoices are uniquely identified by the invoice ID. // invoices are uniquely identified by the invoice ID.
numInvoicesKey = []byte("nik") numInvoicesKey = []byte("nik")
// addIndexBucket is an index bucket that we'll use to create a
// monotonically increasing set of add indexes. Each time we add a new
// invoice, this sequence number will be incremented and then populated
// within the new invoice.
//
// In addition to this sequence number, we map:
//
// addIndexNo => invoiceIndex
addIndexBucket = []byte("invoice-add-index")
// settleIndexBucket is an index bucket that we'll use to create a
// monotonically increasing integer for tracking a "settle index". Each
// time an invoice is settled, this sequence number will be incremented
// as populate within the newly settled invoice.
//
// In addition to this sequence number, we map:
//
// settleIndexNo => invoiceIndex
settleIndexBucket = []byte("invoice-settle-index")
) )
const ( const (
@ -61,8 +83,8 @@ type ContractTerm struct {
// extended. // extended.
PaymentPreimage [32]byte PaymentPreimage [32]byte
// Value is the expected amount of milli-satoshis to be paid to an // Value is the expected amount of milli-satoshis to be paid to an HTLC
// HTLC which can be satisfied by the above preimage. // which can be satisfied by the above preimage.
Value lnwire.MilliSatoshi Value lnwire.MilliSatoshi
// Settled indicates if this particular contract term has been fully // Settled indicates if this particular contract term has been fully
@ -79,8 +101,7 @@ type ContractTerm struct {
// invoices are never deleted from the database, instead a bit is toggled // invoices are never deleted from the database, instead a bit is toggled
// denoting the invoice has been fully settled. Within the database, all // denoting the invoice has been fully settled. Within the database, all
// invoices must have a unique payment hash which is generated by taking the // invoices must have a unique payment hash which is generated by taking the
// sha256 of the payment // sha256 of the payment preimage.
// preimage.
type Invoice struct { type Invoice struct {
// Memo is an optional memo to be stored along side an invoice. The // Memo is an optional memo to be stored along side an invoice. The
// memo may contain further details pertaining to the invoice itself, // memo may contain further details pertaining to the invoice itself,
@ -103,13 +124,37 @@ type Invoice struct {
// SettleDate is the exact time the invoice was settled. // SettleDate is the exact time the invoice was settled.
SettleDate time.Time SettleDate time.Time
// Terms are the contractual payment terms of the invoice. Once // Terms are the contractual payment terms of the invoice. Once all the
// all the terms have been satisfied by the payer, then the invoice can // terms have been satisfied by the payer, then the invoice can be
// be considered fully fulfilled. // considered fully fulfilled.
// //
// TODO(roasbeef): later allow for multiple terms to fulfill the final // TODO(roasbeef): later allow for multiple terms to fulfill the final
// invoice: payment fragmentation, etc. // invoice: payment fragmentation, etc.
Terms ContractTerm Terms ContractTerm
// AddIndex is an auto-incrementing integer that acts as a
// monotonically increasing sequence number for all invoices created.
// Clients can then use this field as a "checkpoint" of sorts when
// implementing a streaming RPC to notify consumers of instances where
// an invoice has been added before they re-connected.
//
// NOTE: This index starts at 1.
AddIndex uint64
// SettleIndex is an auto-incrementing integer that acts as a
// monotonically increasing sequence number for all settled invoices.
// Clients can then use this field as a "checkpoint" of sorts when
// implementing a streaming RPC to notify consumers of instances where
// an invoice has been settled before they re-connected.
//
// NOTE: This index starts at 1.
SettleIndex uint64
// AmtPaid is the final amount that we ultimately accepted for pay for
// this invoice. We specify this value independently as it's possible
// that the invoice originally didn't specify an amount, or the sender
// overpaid.
AmtPaid lnwire.MilliSatoshi
} }
func validateInvoice(i *Invoice) error { func validateInvoice(i *Invoice) error {
@ -134,24 +179,36 @@ func validateInvoice(i *Invoice) error {
// has *any* payment hashes which already exists within the database, then the // has *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.
func (d *DB) AddInvoice(i *Invoice) error { func (d *DB) AddInvoice(newInvoice *Invoice) (uint64, error) {
if err := validateInvoice(i); err != nil { if err := validateInvoice(newInvoice); err != nil {
return err return 0, err
} }
return d.Update(func(tx *bolt.Tx) error {
var invoiceAddIndex uint64
err := d.Update(func(tx *bolt.Tx) error {
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket) invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
if err != nil { if err != nil {
return err return err
} }
invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket) invoiceIndex, err := invoices.CreateBucketIfNotExists(
invoiceIndexBucket,
)
if err != nil {
return err
}
addIndex, err := invoices.CreateBucketIfNotExists(
addIndexBucket,
)
if err != nil { if err != nil {
return err return err
} }
// 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(i.Terms.PaymentPreimage[:]) paymentHash := sha256.Sum256(
newInvoice.Terms.PaymentPreimage[:],
)
if invoiceIndex.Get(paymentHash[:]) != nil { if invoiceIndex.Get(paymentHash[:]) != nil {
return ErrDuplicateInvoice return ErrDuplicateInvoice
} }
@ -163,15 +220,96 @@ func (d *DB) AddInvoice(i *Invoice) error {
if invoiceCounter == nil { if invoiceCounter == nil {
var scratch [4]byte var scratch [4]byte
byteOrder.PutUint32(scratch[:], invoiceNum) byteOrder.PutUint32(scratch[:], invoiceNum)
if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil { err := invoiceIndex.Put(numInvoicesKey, scratch[:])
if err != nil {
return nil return nil
} }
} else { } else {
invoiceNum = byteOrder.Uint32(invoiceCounter) invoiceNum = byteOrder.Uint32(invoiceCounter)
} }
return putInvoice(invoices, invoiceIndex, i, invoiceNum) newIndex, err := putInvoice(
invoices, invoiceIndex, addIndex, newInvoice, invoiceNum,
)
if err != nil {
return err
}
invoiceAddIndex = newIndex
return nil
}) })
if err != nil {
return 0, err
}
return invoiceAddIndex, err
}
// InvoicesAddedSince can be used by callers to seek into the event time series
// of all the invoices added in the database. The specified sinceAddIndex
// should be the highest add index that the caller knows of. This method will
// return all invoices with an add index greater than the specified
// sinceAddIndex.
//
// NOTE: The index starts from 1, as a result. We enforce that specifying a
// value below the starting index value is a noop.
func (d *DB) InvoicesAddedSince(sinceAddIndex uint64) ([]Invoice, error) {
var newInvoices []Invoice
// If an index of zero was specified, then in order to maintain
// backwards compat, we won't send out any new invoices.
if sinceAddIndex == 0 {
return newInvoices, nil
}
var startIndex [8]byte
byteOrder.PutUint64(startIndex[:], sinceAddIndex)
err := d.DB.View(func(tx *bolt.Tx) error {
invoices := tx.Bucket(invoiceBucket)
if invoices == nil {
return ErrNoInvoicesCreated
}
addIndex := invoices.Bucket(addIndexBucket)
if addIndex == nil {
return ErrNoInvoicesCreated
}
// We'll now run through each entry in the add index starting
// at our starting index. We'll continue until we reach the
// very end of the current key space.
invoiceCursor := addIndex.Cursor()
// We'll seek to the starting index, then manually advance the
// cursor in order to skip the entry with the since add index.
invoiceCursor.Seek(startIndex[:])
addSeqNo, invoiceKey := invoiceCursor.Next()
for ; addSeqNo != nil && bytes.Compare(addSeqNo, startIndex[:]) > 0; addSeqNo, invoiceKey = invoiceCursor.Next() {
// For each key found, we'll look up the actual
// invoice, then accumulate it into our return value.
invoice, err := fetchInvoice(invoiceKey, invoices)
if err != nil {
return err
}
newInvoices = append(newInvoices, invoice)
}
return nil
})
switch {
// If no invoices have been created, then we'll return the empty set of
// invoices.
case err == ErrNoInvoicesCreated:
case err != nil:
return nil, err
}
return newInvoices, nil
} }
// LookupInvoice attempts to look up an invoice according to its 32 byte // LookupInvoice attempts to look up an invoice according to its 32 byte
@ -180,8 +318,8 @@ func (d *DB) AddInvoice(i *Invoice) error {
// full invoice is returned. Before setting the incoming HTLC, the values // full invoice is returned. Before setting the incoming HTLC, the values
// SHOULD be checked to ensure the payer meets the agreed upon contractual // SHOULD be checked to ensure the payer meets the agreed upon contractual
// terms of the payment. // terms of the payment.
func (d *DB) LookupInvoice(paymentHash [32]byte) (*Invoice, error) { func (d *DB) LookupInvoice(paymentHash [32]byte) (Invoice, error) {
var invoice *Invoice var invoice Invoice
err := d.View(func(tx *bolt.Tx) error { err := d.View(func(tx *bolt.Tx) error {
invoices := tx.Bucket(invoiceBucket) invoices := tx.Bucket(invoiceBucket)
if invoices == nil { if invoices == nil {
@ -210,7 +348,7 @@ func (d *DB) LookupInvoice(paymentHash [32]byte) (*Invoice, error) {
return nil return nil
}) })
if err != nil { if err != nil {
return nil, err return invoice, err
} }
return invoice, nil return invoice, nil
@ -219,8 +357,8 @@ func (d *DB) LookupInvoice(paymentHash [32]byte) (*Invoice, error) {
// FetchAllInvoices returns all invoices currently stored within the database. // FetchAllInvoices returns all invoices currently stored within the database.
// If the pendingOnly param is true, then only unsettled invoices will be // If the pendingOnly param is true, then only unsettled invoices will be
// returned, skipping all invoices that are fully settled. // returned, skipping all invoices that are fully settled.
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]*Invoice, error) { func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
var invoices []*Invoice var invoices []Invoice
err := d.View(func(tx *bolt.Tx) error { err := d.View(func(tx *bolt.Tx) error {
invoiceB := tx.Bucket(invoiceBucket) invoiceB := tx.Bucket(invoiceBucket)
@ -262,13 +400,24 @@ func (d *DB) FetchAllInvoices(pendingOnly bool) ([]*Invoice, error) {
// payment hash as fully settled. If an invoice matching the passed payment // payment hash as fully settled. If an invoice matching the passed payment
// hash doesn't existing within the database, then the action will fail with a // hash doesn't existing within the database, then the action will fail with a
// "not found" error. // "not found" error.
func (d *DB) SettleInvoice(paymentHash [32]byte) error { func (d *DB) SettleInvoice(paymentHash [32]byte,
return d.Update(func(tx *bolt.Tx) error { amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
var settledInvoice *Invoice
err := d.Update(func(tx *bolt.Tx) error {
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket) invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
if err != nil { if err != nil {
return err return err
} }
invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket) invoiceIndex, err := invoices.CreateBucketIfNotExists(
invoiceIndexBucket,
)
if err != nil {
return err
}
settleIndex, err := invoices.CreateBucketIfNotExists(
settleIndexBucket,
)
if err != nil { if err != nil {
return err return err
} }
@ -280,12 +429,86 @@ func (d *DB) SettleInvoice(paymentHash [32]byte) error {
return ErrInvoiceNotFound return ErrInvoiceNotFound
} }
return settleInvoice(invoices, invoiceNum) invoice, err := settleInvoice(
invoices, settleIndex, invoiceNum, amtPaid,
)
if err != nil {
return err
}
settledInvoice = invoice
return nil
}) })
if err != nil {
return nil, err
}
return settledInvoice, nil
} }
func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket, // InvoicesSettledSince can be used by callers to catch up any settled invoices
i *Invoice, invoiceNum uint32) error { // they missed within the settled invoice time series. We'll return all known
// settled invoice that have a settle index higher than the passed
// sinceSettleIndex.
//
// NOTE: The index starts from 1, as a result. We enforce that specifying a
// value below the starting index value is a noop.
func (d *DB) InvoicesSettledSince(sinceSettleIndex uint64) ([]Invoice, error) {
var settledInvoices []Invoice
// If an index of zero was specified, then in order to maintain
// backwards compat, we won't send out any new invoices.
if sinceSettleIndex == 0 {
return settledInvoices, nil
}
var startIndex [8]byte
byteOrder.PutUint64(startIndex[:], sinceSettleIndex)
err := d.DB.View(func(tx *bolt.Tx) error {
invoices := tx.Bucket(invoiceBucket)
if invoices == nil {
return ErrNoInvoicesCreated
}
settleIndex := invoices.Bucket(settleIndexBucket)
if settleIndex == nil {
return ErrNoInvoicesCreated
}
// We'll now run through each entry in the add index starting
// at our starting index. We'll continue until we reach the
// very end of the current key space.
invoiceCursor := settleIndex.Cursor()
// We'll seek to the starting index, then manually advance the
// cursor in order to skip the entry with the since add index.
invoiceCursor.Seek(startIndex[:])
seqNo, invoiceKey := invoiceCursor.Next()
for ; seqNo != nil && bytes.Compare(seqNo, startIndex[:]) > 0; seqNo, invoiceKey = invoiceCursor.Next() {
// For each key found, we'll look up the actual
// invoice, then accumulate it into our return value.
invoice, err := fetchInvoice(invoiceKey, invoices)
if err != nil {
return err
}
settledInvoices = append(settledInvoices, invoice)
}
return nil
})
if err != nil {
return nil, err
}
return settledInvoices, nil
}
func putInvoice(invoices, invoiceIndex, addIndex *bolt.Bucket,
i *Invoice, invoiceNum uint32) (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.
@ -298,24 +521,48 @@ func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket,
invoiceCounter := invoiceNum + 1 invoiceCounter := invoiceNum + 1
byteOrder.PutUint32(scratch[:], invoiceCounter) byteOrder.PutUint32(scratch[:], invoiceCounter)
if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil { if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
return err return 0, err
} }
// 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[:]) paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
if err := invoiceIndex.Put(paymentHash[:], invoiceKey[:]); err != nil { err := invoiceIndex.Put(paymentHash[:], invoiceKey[:])
return err if err != nil {
return 0, err
} }
// Next, we'll obtain the next add invoice index (sequence
// number), so we can properly place this invoice within this
// event stream.
nextAddSeqNo, err := addIndex.NextSequence()
if err != nil {
return 0, err
}
// With the next sequence obtained, we'll updating the event series in
// the add index bucket to map this current add counter to the index of
// this new invoice.
var seqNoBytes [8]byte
byteOrder.PutUint64(seqNoBytes[:], nextAddSeqNo)
if err := addIndex.Put(seqNoBytes[:], invoiceKey[:]); err != nil {
return 0, err
}
i.AddIndex = nextAddSeqNo
// Finally, serialize the invoice itself to be written to the disk. // Finally, serialize the invoice itself to be written to the disk.
var buf bytes.Buffer var buf bytes.Buffer
if err := serializeInvoice(&buf, i); err != nil { if err := serializeInvoice(&buf, i); err != nil {
return nil return 0, nil
} }
return invoices.Put(invoiceKey[:], buf.Bytes()) if err := invoices.Put(invoiceKey[:], buf.Bytes()); err != nil {
return 0, err
}
return nextAddSeqNo, nil
} }
func serializeInvoice(w io.Writer, i *Invoice) error { func serializeInvoice(w io.Writer, i *Invoice) error {
@ -361,13 +608,23 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
return err return err
} }
if err := binary.Write(w, byteOrder, i.AddIndex); err != nil {
return err
}
if err := binary.Write(w, byteOrder, i.SettleIndex); err != nil {
return err
}
if err := binary.Write(w, byteOrder, int64(i.AmtPaid)); err != nil {
return err
}
return nil return nil
} }
func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (*Invoice, error) { func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (Invoice, error) {
invoiceBytes := invoices.Get(invoiceNum) invoiceBytes := invoices.Get(invoiceNum)
if invoiceBytes == nil { if invoiceBytes == nil {
return nil, ErrInvoiceNotFound return Invoice{}, ErrInvoiceNotFound
} }
invoiceReader := bytes.NewReader(invoiceBytes) invoiceReader := bytes.NewReader(invoiceBytes)
@ -375,76 +632,108 @@ func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (*Invoice, error) {
return deserializeInvoice(invoiceReader) return deserializeInvoice(invoiceReader)
} }
func deserializeInvoice(r io.Reader) (*Invoice, error) { func deserializeInvoice(r io.Reader) (Invoice, error) {
var err error var err error
invoice := &Invoice{} invoice := Invoice{}
// TODO(roasbeef): use read full everywhere // TODO(roasbeef): use read full everywhere
invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "") invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
if err != nil { if err != nil {
return nil, err return invoice, err
} }
invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "") invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
if err != nil { if err != nil {
return nil, err return invoice, err
} }
invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "") invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
if err != nil { if err != nil {
return nil, err return invoice, err
} }
birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth") birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
if err != nil { if err != nil {
return nil, err return invoice, err
} }
if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil { if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
return nil, err return invoice, err
} }
settledBytes, err := wire.ReadVarBytes(r, 0, 300, "settled") settledBytes, err := wire.ReadVarBytes(r, 0, 300, "settled")
if err != nil { if err != nil {
return nil, err return invoice, err
} }
if err := invoice.SettleDate.UnmarshalBinary(settledBytes); err != nil { if err := invoice.SettleDate.UnmarshalBinary(settledBytes); err != nil {
return nil, err return invoice, err
} }
if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil { if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
return nil, err return invoice, err
} }
var scratch [8]byte var scratch [8]byte
if _, err := io.ReadFull(r, scratch[:]); err != nil { if _, err := io.ReadFull(r, scratch[:]); err != nil {
return nil, err return invoice, err
} }
invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:])) invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil { if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil {
return nil, err return invoice, err
}
if err := binary.Read(r, byteOrder, &invoice.AddIndex); err != nil {
return invoice, err
}
if err := binary.Read(r, byteOrder, &invoice.SettleIndex); err != nil {
return invoice, err
}
if err := binary.Read(r, byteOrder, &invoice.AmtPaid); err != nil {
return invoice, err
} }
return invoice, nil return invoice, nil
} }
func settleInvoice(invoices *bolt.Bucket, invoiceNum []byte) error { func settleInvoice(invoices, settleIndex *bolt.Bucket, invoiceNum []byte,
amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
invoice, err := fetchInvoice(invoiceNum, invoices) invoice, err := fetchInvoice(invoiceNum, invoices)
if err != nil { if err != nil {
return err return nil, err
} }
// Add idempotency to duplicate settles, return here to avoid // Add idempotency to duplicate settles, return here to avoid
// overwriting the previous info. // overwriting the previous info.
if invoice.Terms.Settled { if invoice.Terms.Settled {
return nil return nil, nil
} }
// 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
// proper location within our time series.
nextSettleSeqNo, err := settleIndex.NextSequence()
if err != nil {
return nil, err
}
var seqNoBytes [8]byte
byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo)
if err := settleIndex.Put(seqNoBytes[:], invoiceNum); err != nil {
return nil, err
}
invoice.AmtPaid = amtPaid
invoice.Terms.Settled = true invoice.Terms.Settled = true
invoice.SettleDate = time.Now() invoice.SettleDate = time.Now()
invoice.SettleIndex = nextSettleSeqNo
var buf bytes.Buffer var buf bytes.Buffer
if err := serializeInvoice(&buf, invoice); err != nil { if err := serializeInvoice(&buf, &invoice); err != nil {
return nil return nil, err
} }
return invoices.Put(invoiceNum[:], buf.Bytes()) if err := invoices.Put(invoiceNum[:], buf.Bytes()); err != nil {
return nil, err
}
return &invoice, nil
} }

@ -112,3 +112,122 @@ func migrateNodeAndEdgeUpdateIndex(tx *bolt.Tx) error {
return nil return nil
} }
// migrateInvoiceTimeSeries is a database migration that assigns all existing
// invoices an index in the add and/or the settle index. Additionally, all
// existing invoices will have their bytes padded out in order to encode the
// add+settle index as well as the amount paid.
func migrateInvoiceTimeSeries(tx *bolt.Tx) error {
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
if err != nil {
return err
}
addIndex, err := invoices.CreateBucketIfNotExists(
addIndexBucket,
)
if err != nil {
return err
}
settleIndex, err := invoices.CreateBucketIfNotExists(
settleIndexBucket,
)
if err != nil {
return err
}
log.Infof("Migrating invoice database to new time series format")
// Now that we have all the buckets we need, we'll run through each
// invoice in the database, and update it to reflect the new format
// expected post migration.
err = invoices.ForEach(func(invoiceNum, invoiceBytes []byte) error {
// If this is a sub bucket, then we'll skip it.
if invoiceBytes == nil {
return nil
}
// First, we'll make a copy of the encoded invoice bytes.
invoiceBytesCopy := make([]byte, len(invoiceBytes))
copy(invoiceBytesCopy, invoiceBytes)
// With the bytes copied over, we'll append 24 additional
// bytes. We do this so we can decode the invoice under the new
// serialization format.
padding := bytes.Repeat([]byte{0}, 24)
invoiceBytesCopy = append(invoiceBytesCopy, padding...)
invoiceReader := bytes.NewReader(invoiceBytesCopy)
invoice, err := deserializeInvoice(invoiceReader)
if err != nil {
return fmt.Errorf("unable to decode invoice: %v", err)
}
// Now that we have the fully decoded invoice, we can update
// the various indexes that we're added, and finally the
// invoice itself before re-inserting it.
// First, we'll get the new sequence in the addIndex in order
// to create the proper mapping.
nextAddSeqNo, err := addIndex.NextSequence()
if err != nil {
return err
}
var seqNoBytes [8]byte
byteOrder.PutUint64(seqNoBytes[:], nextAddSeqNo)
err = addIndex.Put(seqNoBytes[:], invoiceNum[:])
if err != nil {
return err
}
log.Tracef("Adding invoice (preimage=%v, add_index=%v) to add "+
"time series", invoice.Terms.PaymentPreimage[:],
nextAddSeqNo)
// Next, we'll check if the invoice has been settled or not. If
// so, then we'll also add it to the settle index.
var nextSettleSeqNo uint64
if invoice.Terms.Settled {
nextSettleSeqNo, err = settleIndex.NextSequence()
if err != nil {
return err
}
var seqNoBytes [8]byte
byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo)
err := settleIndex.Put(seqNoBytes[:], invoiceNum)
if err != nil {
return err
}
invoice.AmtPaid = invoice.Terms.Value
log.Tracef("Adding invoice (preimage=%v, "+
"settle_index=%v) to add time series",
invoice.Terms.PaymentPreimage[:],
nextSettleSeqNo)
}
// Finally, we'll update the invoice itself with the new
// indexing information as well as the amount paid if it has
// been settled or not.
invoice.AddIndex = nextAddSeqNo
invoice.SettleIndex = nextSettleSeqNo
// We've fully migrated an invoice, so we'll now update the
// invoice in-place.
var b bytes.Buffer
if err := serializeInvoice(&b, &invoice); err != nil {
return err
}
return invoices.Put(invoiceNum, b.Bytes())
})
if err != nil {
return err
}
log.Infof("Migration to invoice time series index complete!")
return nil
}

@ -177,7 +177,7 @@ func deserializeOutgoingPayment(r io.Reader) (*OutgoingPayment, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.Invoice = *inv p.Invoice = inv
if _, err := r.Read(scratch[:]); err != nil { if _, err := r.Read(scratch[:]); err != nil {
return nil, err return nil, err

@ -2195,11 +2195,13 @@ func addInvoice(ctx *cli.Context) error {
} }
printJSON(struct { printJSON(struct {
RHash string `json:"r_hash"` RHash string `json:"r_hash"`
PayReq string `json:"pay_req"` PayReq string `json:"pay_req"`
AddIndex uint64 `json:"add_index"`
}{ }{
RHash: hex.EncodeToString(resp.RHash), RHash: hex.EncodeToString(resp.RHash),
PayReq: resp.PaymentRequest, PayReq: resp.PaymentRequest,
AddIndex: resp.AddIndex,
}) })
return nil return nil

@ -18,7 +18,7 @@ type InvoiceDatabase interface {
// SettleInvoice attempts to mark an invoice corresponding to the // SettleInvoice attempts to mark an invoice corresponding to the
// passed payment hash as fully settled. // passed payment hash as fully settled.
SettleInvoice(chainhash.Hash) error SettleInvoice(payHash chainhash.Hash, paidAmount lnwire.MilliSatoshi) error
} }
// ChannelLink is an interface which represents the subsystem for managing the // ChannelLink is an interface which represents the subsystem for managing the

@ -2298,8 +2298,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
} }
preimage := invoice.Terms.PaymentPreimage preimage := invoice.Terms.PaymentPreimage
err = l.channel.SettleHTLC(preimage, err = l.channel.SettleHTLC(
pd.HtlcIndex, pd.SourceRef, nil, nil) 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) "unable to settle htlc: %v", err)
@ -2307,8 +2308,11 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
} }
// Notify the invoiceRegistry of the invoices we just // Notify the invoiceRegistry of the invoices we just
// settled with this latest commitment update. // settled (with the amount accepted at settle time)
err = l.cfg.Registry.SettleInvoice(invoiceHash) // with this latest commitment update.
err = l.cfg.Registry.SettleInvoice(
invoiceHash, pd.Amount,
)
if err != nil { if err != nil {
l.fail(LinkFailureError{code: ErrInternalError}, l.fail(LinkFailureError{code: ErrInternalError},
"unable to settle invoice: %v", err) "unable to settle invoice: %v", err)

@ -3657,6 +3657,13 @@ func TestChannelLinkAcceptOverpay(t *testing.T) {
t.Fatalf("channel bandwidth incorrect: expected %v, got %v", t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
expectedCarolBandwidth, n.carolChannelLink.Bandwidth()) expectedCarolBandwidth, n.carolChannelLink.Bandwidth())
} }
// Finally, we'll ensure that the amount we paid is properly reflected
// in the stored invoice.
if invoice.AmtPaid != amount {
t.Fatalf("expected amt paid to be %v, is instead %v", amount,
invoice.AmtPaid)
}
} }
// chanRestoreFunc is a method signature for functions that can reload both // chanRestoreFunc is a method signature for functions that can reload both

@ -673,7 +673,9 @@ func (i *mockInvoiceRegistry) LookupInvoice(rHash chainhash.Hash) (channeldb.Inv
return invoice, i.finalDelta, nil return invoice, i.finalDelta, nil
} }
func (i *mockInvoiceRegistry) SettleInvoice(rhash chainhash.Hash) error { func (i *mockInvoiceRegistry) SettleInvoice(rhash chainhash.Hash,
amt lnwire.MilliSatoshi) error {
i.Lock() i.Lock()
defer i.Unlock() defer i.Unlock()
@ -687,6 +689,7 @@ func (i *mockInvoiceRegistry) SettleInvoice(rhash chainhash.Hash) error {
} }
invoice.Terms.Settled = true invoice.Terms.Settled = true
invoice.AmtPaid = amt
i.invoices[rhash] = invoice i.invoices[rhash] = invoice
return nil return nil

@ -3,10 +3,13 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"fmt"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
"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" "github.com/lightningnetwork/lnd/zpay32"
@ -37,10 +40,17 @@ type invoiceRegistry struct {
nextClientID uint32 nextClientID uint32
notificationClients map[uint32]*invoiceSubscription notificationClients map[uint32]*invoiceSubscription
newSubscriptions chan *invoiceSubscription
subscriptionCancels chan uint32
invoiceEvents chan *invoiceEvent
// debugInvoices is a map which stores special "debug" invoices which // debugInvoices is a map which stores special "debug" invoices which
// should be only created/used when manual tests require an invoice // should be only created/used when manual tests require an invoice
// that *all* nodes are able to fully settle. // that *all* nodes are able to fully settle.
debugInvoices map[chainhash.Hash]*channeldb.Invoice debugInvoices map[chainhash.Hash]*channeldb.Invoice
wg sync.WaitGroup
quit chan struct{}
} }
// newInvoiceRegistry creates a new invoice registry. The invoice registry // newInvoiceRegistry creates a new invoice registry. The invoice registry
@ -52,9 +62,194 @@ func newInvoiceRegistry(cdb *channeldb.DB) *invoiceRegistry {
cdb: cdb, cdb: cdb,
debugInvoices: make(map[chainhash.Hash]*channeldb.Invoice), debugInvoices: make(map[chainhash.Hash]*channeldb.Invoice),
notificationClients: make(map[uint32]*invoiceSubscription), notificationClients: make(map[uint32]*invoiceSubscription),
newSubscriptions: make(chan *invoiceSubscription),
subscriptionCancels: make(chan uint32),
invoiceEvents: make(chan *invoiceEvent),
quit: make(chan struct{}),
} }
} }
// Start starts the registry and all goroutines it needs to carry out its task.
func (i *invoiceRegistry) Start() error {
i.wg.Add(1)
go i.invoiceEventNotifier()
return nil
}
// Stop signals the registry for a graceful shutdown.
func (i *invoiceRegistry) Stop() {
close(i.quit)
i.wg.Wait()
}
// invoiceEvent represents a new event that has modified on invoice on disk.
// Only two event types are currently supported: newly created invoices, and
// instance where invoices are settled.
type invoiceEvent struct {
isSettle bool
invoice *channeldb.Invoice
}
// invoiceEventNotifier is the dedicated goroutine responsible for accepting
// new notification subscriptions, cancelling old subscriptions, and
// dispatching new invoice events.
func (i *invoiceRegistry) invoiceEventNotifier() {
defer i.wg.Done()
for {
select {
// A new invoice subscription has just arrived! We'll query for
// any backlog notifications, then add it to the set of
// clients.
case newClient := <-i.newSubscriptions:
// Before we add the client to our set of active
// clients, we'll first attempt to deliver any backlog
// invoice events.
err := i.deliverBacklogEvents(newClient)
if err != nil {
ltndLog.Errorf("unable to deliver backlog invoice "+
"notifications: %v", err)
}
ltndLog.Infof("New invoice subscription "+
"client: id=%v", newClient.id)
// With the backlog notifications delivered (if any),
// we'll add this to our active subscriptions and
// continue.
i.notificationClients[newClient.id] = newClient
// A client no longer wishes to receive invoice notifications.
// So we'll remove them from the set of active clients.
case clientID := <-i.subscriptionCancels:
ltndLog.Infof("Cancelling invoice subscription for "+
"client=%v", clientID)
delete(i.notificationClients, clientID)
// A sub-systems has just modified the invoice state, so we'll
// dispatch notifications to all registered clients.
case event := <-i.invoiceEvents:
for clientID, client := range i.notificationClients {
// Before we dispatch this event, we'll check
// to ensure that this client hasn't already
// received this notification in order to
// ensure we don't duplicate any events.
invoice := event.invoice
switch {
// If we've already sent this settle event to
// the client, then we can skip this.
case event.isSettle &&
client.settleIndex >= invoice.SettleIndex:
continue
// Similarly, if we've already sent this add to
// the client then we can skip this one.
case !event.isSettle &&
client.addIndex >= invoice.AddIndex:
continue
// These two states should never happen, but we
// log them just in case so we can detect this
// instance.
case !event.isSettle &&
client.addIndex+1 != invoice.AddIndex:
ltndLog.Warnf("client=%v for invoice "+
"notifications missed an update, "+
"add_index=%v, new add event index=%v",
clientID, client.addIndex,
invoice.AddIndex)
case event.isSettle &&
client.settleIndex+1 != invoice.SettleIndex:
ltndLog.Warnf("client=%v for invoice "+
"notifications missed an update, "+
"settle_index=%v, new settle event index=%v",
clientID, client.settleIndex,
invoice.SettleIndex)
}
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: event.isSettle,
invoice: invoice,
}:
case <-i.quit:
return
}
// Each time we send a notification to a
// client, we'll record the latest add/settle
// index it has. We'll use this to ensure we
// don't send a notification twice, which can
// happen if a new event is added while we're
// catching up a new client.
if event.isSettle {
client.settleIndex = invoice.SettleIndex
} else {
client.addIndex = invoice.AddIndex
}
}
case <-i.quit:
return
}
}
}
// deliverBacklogEvents will attempts to query the invoice database for any
// notifications that the client has missed since it reconnected last.
func (i *invoiceRegistry) deliverBacklogEvents(client *invoiceSubscription) error {
// First, we'll query the database to see if based on the provided
// addIndex and settledIndex we need to deliver any backlog
// notifications.
addEvents, err := i.cdb.InvoicesAddedSince(client.addIndex)
if err != nil {
return err
}
settleEvents, err := i.cdb.InvoicesSettledSince(client.settleIndex)
if err != nil {
return err
}
// If we have any to deliver, then we'll append them to the end of the
// notification queue in order to catch up the client before delivering
// any new notifications.
for _, addEvent := range addEvents {
// We re-bind the loop variable to ensure we don't hold onto
// the loop reference causing is to point to the same item.
addEvent := addEvent
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: false,
invoice: &addEvent,
}:
case <-i.quit:
return fmt.Errorf("registry shutting down")
}
}
for _, settleEvent := range settleEvents {
// We re-bind the loop variable to ensure we don't hold onto
// the loop reference causing is to point to the same item.
settleEvent := settleEvent
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: true,
invoice: &settleEvent,
}:
case <-i.quit:
return fmt.Errorf("registry shutting down")
}
}
return nil
}
// AddDebugInvoice adds a debug invoice for the specified amount, identified // AddDebugInvoice adds a debug invoice for the specified amount, identified
// by the passed preimage. Once this invoice is added, subsystems within the // by the passed preimage. Once this invoice is added, subsystems within the
// daemon add/forward HTLCs that are able to obtain the proper preimage // daemon add/forward HTLCs that are able to obtain the proper preimage
@ -82,18 +277,28 @@ func (i *invoiceRegistry) AddDebugInvoice(amt btcutil.Amount, preimage chainhash
// AddInvoice adds a regular invoice for the specified amount, identified by // AddInvoice adds a regular invoice for the specified amount, identified by
// the passed preimage. Additionally, any memo or receipt data provided will // the passed preimage. Additionally, any memo or receipt data provided will
// also be stored on-disk. Once this invoice is added, subsystems within the // also be stored on-disk. Once this invoice is added, subsystems within the
// daemon add/forward HTLCs are able to obtain the proper preimage required // daemon add/forward HTLCs are able to obtain the proper preimage required for
// for redemption in the case that we're the final destination. // redemption in the case that we're the final destination. We also return the
func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error { // addIndex of the newly created invoice which monotonically increases for each
// new invoice added.
func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) (uint64, error) {
i.Lock()
defer i.Unlock()
ltndLog.Debugf("Adding invoice %v", newLogClosure(func() string { ltndLog.Debugf("Adding invoice %v", newLogClosure(func() string {
return spew.Sdump(invoice) return spew.Sdump(invoice)
})) }))
// TODO(roasbeef): also check in memory for quick lookups/settles? addIndex, err := i.cdb.AddInvoice(invoice)
return i.cdb.AddInvoice(invoice) if err != nil {
return 0, err
}
// TODO(roasbeef): re-enable? // Now that we've added the invoice, we'll send dispatch a message to
//go i.notifyClients(invoice, false) // notify the clients of this new invoice.
i.notifyClients(invoice, false)
return addIndex, nil
} }
// LookupInvoice looks up an invoice by its payment hash (R-Hash), if found // LookupInvoice looks up an invoice by its payment hash (R-Hash), if found
@ -107,12 +312,12 @@ func (i *invoiceRegistry) LookupInvoice(rHash chainhash.Hash) (channeldb.Invoice
// 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.
i.RLock() i.RLock()
invoice, ok := i.debugInvoices[rHash] debugInv, ok := i.debugInvoices[rHash]
i.RUnlock() i.RUnlock()
// If found, then simply return the invoice directly. // If found, then simply return the invoice directly.
if ok { if ok {
return *invoice, 0, nil return *debugInv, 0, nil
} }
// Otherwise, we'll check the database to see if there's an existing // Otherwise, we'll check the database to see if there's an existing
@ -129,46 +334,38 @@ func (i *invoiceRegistry) LookupInvoice(rHash chainhash.Hash) (channeldb.Invoice
return channeldb.Invoice{}, 0, err return channeldb.Invoice{}, 0, err
} }
return *invoice, uint32(payReq.MinFinalCLTVExpiry()), nil return invoice, uint32(payReq.MinFinalCLTVExpiry()), nil
} }
// SettleInvoice attempts to mark an invoice as settled. If the invoice is a // SettleInvoice 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.
func (i *invoiceRegistry) SettleInvoice(rHash chainhash.Hash) error { func (i *invoiceRegistry) SettleInvoice(rHash chainhash.Hash,
amtPaid lnwire.MilliSatoshi) error {
i.Lock()
defer i.Unlock()
ltndLog.Debugf("Settling invoice %x", rHash[:]) ltndLog.Debugf("Settling invoice %x", rHash[:])
// 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.
i.RLock()
if _, ok := i.debugInvoices[rHash]; ok { if _, ok := i.debugInvoices[rHash]; ok {
// Debug invoices are never fully settled, so we simply return // Debug invoices are never fully settled, so we simply return
// immediately in this case. // immediately in this case.
i.RUnlock()
return nil return nil
} }
i.RUnlock()
// 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).
if err := i.cdb.SettleInvoice(rHash); err != nil { invoice, err := i.cdb.SettleInvoice(rHash, amtPaid)
if err != nil {
return err return err
} }
// Launch a new goroutine to notify any/all registered invoice ltndLog.Infof("Payment received: %v", spew.Sdump(invoice))
// notification clients.
go func() {
invoice, err := i.cdb.LookupInvoice(rHash)
if err != nil {
ltndLog.Errorf("unable to find invoice: %v", err)
return
}
ltndLog.Infof("Payment received: %v", spew.Sdump(invoice)) i.notifyClients(invoice, true)
i.notifyClients(invoice, true)
}()
return nil return nil
} }
@ -176,20 +373,14 @@ func (i *invoiceRegistry) SettleInvoice(rHash chainhash.Hash) error {
// notifyClients notifies all currently registered invoice notification clients // notifyClients notifies all currently registered invoice notification clients
// of a newly added/settled invoice. // of a newly added/settled invoice.
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) { func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) {
i.clientMtx.Lock() event := &invoiceEvent{
defer i.clientMtx.Unlock() isSettle: settle,
invoice: invoice,
}
for _, client := range i.notificationClients { select {
var eventChan chan *channeldb.Invoice case i.invoiceEvents <- event:
if settle { case <-i.quit:
eventChan = client.SettledInvoices
} else {
eventChan = client.NewInvoices
}
go func() {
eventChan <- invoice
}()
} }
} }
@ -199,36 +390,127 @@ func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool)
// invoice, a copy of the invoice will be sent over the SettledInvoices // invoice, a copy of the invoice will be sent over the SettledInvoices
// channel. // channel.
type invoiceSubscription struct { type invoiceSubscription struct {
NewInvoices chan *channeldb.Invoice cancelled uint32 // To be used atomically.
// NewInvoices is a channel that we'll use to send all newly created
// invoices with an invoice index greater than the specified
// StartingInvoiceIndex field.
NewInvoices chan *channeldb.Invoice
// SettledInvoices is a channel that we'll use to send all setted
// invoices with an invoices index greater than the specified
// StartingInvoiceIndex field.
SettledInvoices chan *channeldb.Invoice SettledInvoices chan *channeldb.Invoice
// addIndex is the highest add index the caller knows of. We'll use
// this information to send out an event backlog to the notifications
// subscriber. Any new add events with an index greater than this will
// be dispatched before any new notifications are sent out.
addIndex uint64
// settleIndex is the highest settle index the caller knows of. We'll
// use this information to send out an event backlog to the
// notifications subscriber. Any new settle events with an index
// greater than this will be dispatched before any new notifications
// are sent out.
settleIndex uint64
ntfnQueue *chainntnfs.ConcurrentQueue
id uint32
inv *invoiceRegistry inv *invoiceRegistry
id uint32
cancelChan chan struct{}
wg sync.WaitGroup
} }
// Cancel unregisters the invoiceSubscription, freeing any previously allocated // Cancel unregisters the invoiceSubscription, freeing any previously allocated
// resources. // resources.
func (i *invoiceSubscription) Cancel() { func (i *invoiceSubscription) Cancel() {
i.inv.clientMtx.Lock() if !atomic.CompareAndSwapUint32(&i.cancelled, 0, 1) {
delete(i.inv.notificationClients, i.id) return
i.inv.clientMtx.Unlock() }
select {
case i.inv.subscriptionCancels <- i.id:
case <-i.inv.quit:
}
i.ntfnQueue.Stop()
close(i.cancelChan)
i.wg.Wait()
} }
// SubscribeNotifications returns an invoiceSubscription which allows the // SubscribeNotifications returns an invoiceSubscription which allows the
// caller to receive async notifications when any invoices are settled or // caller to receive async notifications when any invoices are settled or
// added. // added. The invoiceIndex parameter is a streaming "checkpoint". We'll start
func (i *invoiceRegistry) SubscribeNotifications() *invoiceSubscription { // by first sending out all new events with an invoice index _greater_ than
// this value. Afterwards, we'll send out real-time notifications.
func (i *invoiceRegistry) SubscribeNotifications(addIndex, settleIndex uint64) *invoiceSubscription {
client := &invoiceSubscription{ client := &invoiceSubscription{
NewInvoices: make(chan *channeldb.Invoice), NewInvoices: make(chan *channeldb.Invoice),
SettledInvoices: make(chan *channeldb.Invoice), SettledInvoices: make(chan *channeldb.Invoice),
addIndex: addIndex,
settleIndex: settleIndex,
inv: i, inv: i,
ntfnQueue: chainntnfs.NewConcurrentQueue(20),
cancelChan: make(chan struct{}),
} }
client.ntfnQueue.Start()
i.clientMtx.Lock() i.clientMtx.Lock()
i.notificationClients[i.nextClientID] = client
client.id = i.nextClientID client.id = i.nextClientID
i.nextClientID++ i.nextClientID++
i.clientMtx.Unlock() i.clientMtx.Unlock()
// Before we register this new invoice subscription, we'll launch a new
// goroutine that will proxy all notifications appended to the end of
// the concurrent queue to the two client-side channels the caller will
// feed off of.
i.wg.Add(1)
go func() {
defer i.wg.Done()
for {
select {
// A new invoice event has been sent by the
// invoiceRegistry! We'll figure out if this is an add
// event or a settle event, then dispatch the event to
// the client.
case ntfn := <-client.ntfnQueue.ChanOut():
invoiceEvent := ntfn.(*invoiceEvent)
targetChan := client.NewInvoices
if invoiceEvent.isSettle {
targetChan = client.SettledInvoices
}
select {
case targetChan <- invoiceEvent.invoice:
case <-client.cancelChan:
return
case <-i.quit:
return
}
case <-client.cancelChan:
return
case <-i.quit:
return
}
}
}()
select {
case i.newSubscriptions <- client:
case <-i.quit:
}
return client return client
} }

@ -19,6 +19,7 @@ import (
"reflect" "reflect"
"crypto/rand" "crypto/rand"
"crypto/sha256"
prand "math/rand" prand "math/rand"
"github.com/btcsuite/btclog" "github.com/btcsuite/btclog"
@ -3452,10 +3453,11 @@ func testSendToRouteErrorPropagation(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("alice didn't advertise her channel: %v", err) t.Fatalf("alice didn't advertise her channel: %v", err)
} }
// Create a new nodes (Carol and Charlie), load her with some funds, then establish // Create a new nodes (Carol and Charlie), load her with some funds,
// a connection between Carol and Charlie with a channel that has // then establish a connection between Carol and Charlie with a channel
// identical capacity to the one created above.Then we will get route via queryroutes call // that has identical capacity to the one created above.Then we will
// which will be fake route for Alice -> Bob graph. // get route via queryroutes call which will be fake route for Alice ->
// Bob graph.
// //
// The network topology should now look like: Alice -> Bob; Carol -> Charlie. // The network topology should now look like: Alice -> Bob; Carol -> Charlie.
carol, err := net.NewNode("Carol", nil) carol, err := net.NewNode("Carol", nil)
@ -4298,15 +4300,18 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
if err != nil { if err != nil {
t.Fatalf("unable to add invoice: %v", err) t.Fatalf("unable to add invoice: %v", err)
} }
lastAddIndex := invoiceResp.AddIndex
// Create a new invoice subscription client for Bob, the notification // Create a new invoice subscription client for Bob, the notification
// should be dispatched shortly below. // should be dispatched shortly below.
req := &lnrpc.InvoiceSubscription{} req := &lnrpc.InvoiceSubscription{}
bobInvoiceSubscription, err := net.Bob.SubscribeInvoices(ctxb, req) ctx, cancelInvoiceSubscription := context.WithCancel(context.Background())
bobInvoiceSubscription, err := net.Bob.SubscribeInvoices(ctx, req)
if err != nil { if err != nil {
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err) t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
} }
var settleIndex uint64
quit := make(chan struct{}) quit := make(chan struct{})
updateSent := make(chan struct{}) updateSent := make(chan struct{})
go func() { go func() {
@ -4336,6 +4341,12 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
invoice.RPreimage, invoiceUpdate.RPreimage) invoice.RPreimage, invoiceUpdate.RPreimage)
} }
if invoiceUpdate.SettleIndex == 0 {
t.Fatalf("invoice should have settle index")
}
settleIndex = invoiceUpdate.SettleIndex
close(updateSent) close(updateSent)
}() }()
@ -4373,6 +4384,126 @@ func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
case <-updateSent: // Fall through on success case <-updateSent: // Fall through on success
} }
// With the base case working, we'll now cancel Bob's current
// subscription in order to exercise the backlog fill behavior.
cancelInvoiceSubscription()
// We'll now add 3 more invoices to Bob's invoice registry.
const numInvoices = 3
newInvoices := make([]*lnrpc.Invoice, numInvoices)
payReqs := make([]string, numInvoices)
for i := 0; i < numInvoices; i++ {
preimage := bytes.Repeat([]byte{byte(90 + 1 + i)}, 32)
invoice := &lnrpc.Invoice{
Memo: "testing",
RPreimage: preimage,
Value: paymentAmt,
}
resp, err := net.Bob.AddInvoice(ctxb, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}
newInvoices[i] = invoice
payReqs[i] = resp.PaymentRequest
}
// Now that the set of invoices has been added, we'll re-register for
// streaming invoice notifications for Bob, this time specifying the
// add invoice of the last prior invoice.
req = &lnrpc.InvoiceSubscription{
AddIndex: lastAddIndex,
}
ctx, cancelInvoiceSubscription = context.WithCancel(context.Background())
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
if err != nil {
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
}
// Since we specified a value of the prior add index above, we should
// now immediately get the invoices we just added as we should get the
// backlog of notifications.
for i := 0; i < numInvoices; i++ {
invoiceUpdate, err := bobInvoiceSubscription.Recv()
if err != nil {
t.Fatalf("unable to receive subscription")
}
// We should now get the ith invoice we added, as they should
// be returned in order.
if invoiceUpdate.Settled {
t.Fatalf("should have only received add events")
}
originalInvoice := newInvoices[i]
rHash := sha256.Sum256(originalInvoice.RPreimage[:])
if !bytes.Equal(invoiceUpdate.RHash, rHash[:]) {
t.Fatalf("invoices have mismatched payment hashes: "+
"expected %x, got %x", rHash[:],
invoiceUpdate.RHash)
}
}
cancelInvoiceSubscription()
// We'll now have Bob settle out the remainder of these invoices so we
// can test that all settled invoices are properly notified.
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = completePaymentRequests(
ctxt, net.Alice, payReqs, true,
)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
// With the set of invoices paid, we'll now cancel the old
// subscription, and create a new one for Bob, this time using the
// settle index to obtain the backlog of settled invoices.
req = &lnrpc.InvoiceSubscription{
SettleIndex: settleIndex,
}
ctx, cancelInvoiceSubscription = context.WithCancel(context.Background())
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
if err != nil {
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
}
defer cancelInvoiceSubscription()
// As we specified the index of the past settle index, we should now
// receive notifications for the three HTLCs that we just settled. As
// the order that the HTLCs will be settled in is partially randomized,
// we'll use a map to assert that the proper set has been settled.
settledInvoices := make(map[[32]byte]struct{})
for _, invoice := range newInvoices {
rHash := sha256.Sum256(invoice.RPreimage[:])
settledInvoices[rHash] = struct{}{}
}
for i := 0; i < numInvoices; i++ {
invoiceUpdate, err := bobInvoiceSubscription.Recv()
if err != nil {
t.Fatalf("unable to receive subscription")
}
// We should now get the ith invoice we added, as they should
// be returned in order.
if !invoiceUpdate.Settled {
t.Fatalf("should have only received settle events")
}
var rHash [32]byte
copy(rHash[:], invoiceUpdate.RHash)
if _, ok := settledInvoices[rHash]; !ok {
t.Fatalf("unknown invoice settled: %x", rHash)
}
delete(settledInvoices, rHash)
}
// At this point, all the invoices should be fully settled.
if len(settledInvoices) != 0 {
t.Fatalf("not all invoices settled")
}
ctxt, _ = context.WithTimeout(ctxb, timeout) ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
} }
@ -5986,7 +6117,8 @@ func subscribeGraphNotifications(t *harnessTest, ctxb context.Context,
// We'll first start by establishing a notification client which will // We'll first start by establishing a notification client which will
// send us notifications upon detected changes in the channel graph. // send us notifications upon detected changes in the channel graph.
req := &lnrpc.GraphTopologySubscription{} req := &lnrpc.GraphTopologySubscription{}
topologyClient, err := node.SubscribeChannelGraph(ctxb, req) ctx, cancelFunc := context.WithCancel(context.Background())
topologyClient, err := node.SubscribeChannelGraph(ctx, req)
if err != nil { if err != nil {
t.Fatalf("unable to create topology client: %v", err) t.Fatalf("unable to create topology client: %v", err)
} }
@ -5997,6 +6129,8 @@ func subscribeGraphNotifications(t *harnessTest, ctxb context.Context,
graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20) graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20)
go func() { go func() {
for { for {
defer cancelFunc()
select { select {
case <-quit: case <-quit:
return return

@ -518,7 +518,9 @@ func (m *FeeLimit) String() string { return proto.CompactTextString(m
func (*FeeLimit) ProtoMessage() {} func (*FeeLimit) ProtoMessage() {}
func (*FeeLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } func (*FeeLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
type isFeeLimit_Limit interface{ isFeeLimit_Limit() } type isFeeLimit_Limit interface {
isFeeLimit_Limit()
}
type FeeLimit_Fixed struct { type FeeLimit_Fixed struct {
Fixed int64 `protobuf:"varint,1,opt,name=fixed,oneof"` Fixed int64 `protobuf:"varint,1,opt,name=fixed,oneof"`
@ -785,7 +787,9 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri
func (*ChannelPoint) ProtoMessage() {} func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
type isChannelPoint_FundingTxid interface{ isChannelPoint_FundingTxid() } type isChannelPoint_FundingTxid interface {
isChannelPoint_FundingTxid()
}
type ChannelPoint_FundingTxidBytes struct { type ChannelPoint_FundingTxidBytes struct {
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"` FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
@ -2034,7 +2038,9 @@ func (m *CloseStatusUpdate) String() string { return proto.CompactTex
func (*CloseStatusUpdate) ProtoMessage() {} func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} }
type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() } type isCloseStatusUpdate_Update interface {
isCloseStatusUpdate_Update()
}
type CloseStatusUpdate_ClosePending struct { type CloseStatusUpdate_ClosePending struct {
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"` ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
@ -2297,7 +2303,9 @@ func (m *OpenStatusUpdate) String() string { return proto.CompactText
func (*OpenStatusUpdate) ProtoMessage() {} func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} } func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} }
type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() } type isOpenStatusUpdate_Update interface {
isOpenStatusUpdate_Update()
}
type OpenStatusUpdate_ChanPending struct { type OpenStatusUpdate_ChanPending struct {
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"` ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
@ -3834,6 +3842,26 @@ type Invoice struct {
RouteHints []*RouteHint `protobuf:"bytes,14,rep,name=route_hints" json:"route_hints,omitempty"` RouteHints []*RouteHint `protobuf:"bytes,14,rep,name=route_hints" json:"route_hints,omitempty"`
// / Whether this invoice should include routing hints for private channels. // / Whether this invoice should include routing hints for private channels.
Private bool `protobuf:"varint,15,opt,name=private" json:"private,omitempty"` Private bool `protobuf:"varint,15,opt,name=private" json:"private,omitempty"`
// *
// The "add" index of this invoice. Each newly created invoice will increment
// this index making it monotonically increasing. Callers to the
// SubscribeInvoices call can use this to instantly get notified of all added
// invoices with an add_index greater than this one.
AddIndex uint64 `protobuf:"varint,16,opt,name=add_index" json:"add_index,omitempty"`
// *
// The "settle" index of this invoice. Each newly settled invoice will
// increment this index making it monotonically increasing. Callers to the
// SubscribeInvoices call can use this to instantly get notified of all
// settled invoices with an settle_index greater than this one.
SettleIndex uint64 `protobuf:"varint,17,opt,name=settle_index" json:"settle_index,omitempty"`
// *
// The amount that was accepted for this invoice. This will ONLY be set if
// this invoice has been settled. We provide this field as if the invoice was
// created with a zero value, then we need to record what amount was
// ultimately accepted. Additionally, it's possible that the sender paid MORE
// that was specified in the original invoice. So we'll record that here as
// well.
AmtPaid int64 `protobuf:"varint,18,opt,name=amt_paid" json:"amt_paid,omitempty"`
} }
func (m *Invoice) Reset() { *m = Invoice{} } func (m *Invoice) Reset() { *m = Invoice{} }
@ -3946,6 +3974,27 @@ func (m *Invoice) GetPrivate() bool {
return false return false
} }
func (m *Invoice) GetAddIndex() uint64 {
if m != nil {
return m.AddIndex
}
return 0
}
func (m *Invoice) GetSettleIndex() uint64 {
if m != nil {
return m.SettleIndex
}
return 0
}
func (m *Invoice) GetAmtPaid() int64 {
if m != nil {
return m.AmtPaid
}
return 0
}
type AddInvoiceResponse struct { type AddInvoiceResponse struct {
RHash []byte `protobuf:"bytes,1,opt,name=r_hash,proto3" json:"r_hash,omitempty"` RHash []byte `protobuf:"bytes,1,opt,name=r_hash,proto3" json:"r_hash,omitempty"`
// * // *
@ -3953,6 +4002,12 @@ type AddInvoiceResponse struct {
// details of the invoice, the sender has all the data necessary to send a // details of the invoice, the sender has all the data necessary to send a
// payment to the recipient. // payment to the recipient.
PaymentRequest string `protobuf:"bytes,2,opt,name=payment_request" json:"payment_request,omitempty"` PaymentRequest string `protobuf:"bytes,2,opt,name=payment_request" json:"payment_request,omitempty"`
// *
// The "add" index of this invoice. Each newly created invoice will increment
// this index making it monotonically increasing. Callers to the
// SubscribeInvoices call can use this to instantly get notified of all added
// invoices with an add_index greater than this one.
AddIndex uint64 `protobuf:"varint,16,opt,name=add_index" json:"add_index,omitempty"`
} }
func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} } func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} }
@ -3974,6 +4029,13 @@ func (m *AddInvoiceResponse) GetPaymentRequest() string {
return "" return ""
} }
func (m *AddInvoiceResponse) GetAddIndex() uint64 {
if m != nil {
return m.AddIndex
}
return 0
}
type PaymentHash struct { type PaymentHash struct {
// * // *
// The hex-encoded payment hash of the invoice to be looked up. The passed // The hex-encoded payment hash of the invoice to be looked up. The passed
@ -4036,6 +4098,18 @@ func (m *ListInvoiceResponse) GetInvoices() []*Invoice {
} }
type InvoiceSubscription struct { type InvoiceSubscription struct {
// *
// If specified (non-zero), then we'll first start by sending out
// notifications for all added indexes with an add_index greater than this
// value. This allows callers to catch up on any events they missed while they
// weren't connected to the streaming RPC.
AddIndex uint64 `protobuf:"varint,1,opt,name=add_index" json:"add_index,omitempty"`
// *
// If specified (non-zero), then we'll first start by sending out
// notifications for all settled indexes with an settle_index greater than
// this value. This allows callers to catch up on any events they missed while
// they weren't connected to the streaming RPC.
SettleIndex uint64 `protobuf:"varint,2,opt,name=settle_index" json:"settle_index,omitempty"`
} }
func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} } func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} }
@ -4043,6 +4117,20 @@ func (m *InvoiceSubscription) String() string { return proto.CompactT
func (*InvoiceSubscription) ProtoMessage() {} func (*InvoiceSubscription) ProtoMessage() {}
func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} } func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} }
func (m *InvoiceSubscription) GetAddIndex() uint64 {
if m != nil {
return m.AddIndex
}
return 0
}
func (m *InvoiceSubscription) GetSettleIndex() uint64 {
if m != nil {
return m.SettleIndex
}
return 0
}
type Payment struct { type Payment struct {
// / The payment hash // / The payment hash
PaymentHash string `protobuf:"bytes,1,opt,name=payment_hash" json:"payment_hash,omitempty"` PaymentHash string `protobuf:"bytes,1,opt,name=payment_hash" json:"payment_hash,omitempty"`
@ -4405,7 +4493,9 @@ func (m *PolicyUpdateRequest) String() string { return proto.CompactT
func (*PolicyUpdateRequest) ProtoMessage() {} func (*PolicyUpdateRequest) ProtoMessage() {}
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{101} } func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{101} }
type isPolicyUpdateRequest_Scope interface{ isPolicyUpdateRequest_Scope() } type isPolicyUpdateRequest_Scope interface {
isPolicyUpdateRequest_Scope()
}
type PolicyUpdateRequest_Global struct { type PolicyUpdateRequest_Global struct {
Global bool `protobuf:"varint,1,opt,name=global,oneof"` Global bool `protobuf:"varint,1,opt,name=global,oneof"`
@ -5158,7 +5248,14 @@ type LightningClient interface {
LookupInvoice(ctx context.Context, in *PaymentHash, opts ...grpc.CallOption) (*Invoice, error) LookupInvoice(ctx context.Context, in *PaymentHash, opts ...grpc.CallOption) (*Invoice, error)
// * // *
// SubscribeInvoices returns a uni-directional stream (sever -> client) for // SubscribeInvoices returns a uni-directional stream (sever -> client) for
// notifying the client of newly added/settled invoices. // notifying the client of newly added/settled invoices. The caller can
// optionally specify the add_index and/or the settle_index. If the add_index
// is specified, then we'll first start by sending add invoice events for all
// invoices with an add_index greater than the specified value. If the
// settle_index is specified, the next, we'll send out all settle events for
// invoices with a settle_index greater than the specified value. One or both
// of these fields can be set. If no fields are set, then we'll only send out
// the latest add/settle events.
SubscribeInvoices(ctx context.Context, in *InvoiceSubscription, opts ...grpc.CallOption) (Lightning_SubscribeInvoicesClient, error) SubscribeInvoices(ctx context.Context, in *InvoiceSubscription, opts ...grpc.CallOption) (Lightning_SubscribeInvoicesClient, error)
// * lncli: `decodepayreq` // * lncli: `decodepayreq`
// DecodePayReq takes an encoded payment request string and attempts to decode // DecodePayReq takes an encoded payment request string and attempts to decode
@ -5928,7 +6025,14 @@ type LightningServer interface {
LookupInvoice(context.Context, *PaymentHash) (*Invoice, error) LookupInvoice(context.Context, *PaymentHash) (*Invoice, error)
// * // *
// SubscribeInvoices returns a uni-directional stream (sever -> client) for // SubscribeInvoices returns a uni-directional stream (sever -> client) for
// notifying the client of newly added/settled invoices. // notifying the client of newly added/settled invoices. The caller can
// optionally specify the add_index and/or the settle_index. If the add_index
// is specified, then we'll first start by sending add invoice events for all
// invoices with an add_index greater than the specified value. If the
// settle_index is specified, the next, we'll send out all settle events for
// invoices with a settle_index greater than the specified value. One or both
// of these fields can be set. If no fields are set, then we'll only send out
// the latest add/settle events.
SubscribeInvoices(*InvoiceSubscription, Lightning_SubscribeInvoicesServer) error SubscribeInvoices(*InvoiceSubscription, Lightning_SubscribeInvoicesServer) error
// * lncli: `decodepayreq` // * lncli: `decodepayreq`
// DecodePayReq takes an encoded payment request string and attempts to decode // DecodePayReq takes an encoded payment request string and attempts to decode
@ -6991,391 +7095,394 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 6171 bytes of a gzipped FileDescriptorProto // 6217 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0x4f, 0x8c, 0x1c, 0xd9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0x4d, 0x6c, 0x1c, 0xc9,
0x59, 0x77, 0x75, 0xf7, 0xfc, 0xe9, 0xaf, 0x7b, 0x7a, 0x7a, 0xde, 0x8c, 0xc7, 0xed, 0xde, 0x5d, 0x75, 0x56, 0x0f, 0x87, 0x3f, 0xf3, 0x66, 0x48, 0x0e, 0x8b, 0x14, 0x35, 0x9a, 0xdd, 0xd5, 0x6a,
0xaf, 0xb7, 0x62, 0xad, 0x9d, 0x61, 0xb1, 0xbd, 0x93, 0x64, 0xd9, 0xec, 0x42, 0x82, 0x3d, 0x1e, 0xdb, 0xc2, 0x4a, 0x66, 0x36, 0x92, 0x96, 0xb6, 0x37, 0xeb, 0xdd, 0xc4, 0x8e, 0x44, 0x52, 0xa2,
0x7b, 0x9c, 0xcc, 0xda, 0x93, 0x1a, 0x3b, 0x86, 0x04, 0xd4, 0xa9, 0xe9, 0x7e, 0xd3, 0x53, 0x71, 0x6c, 0xae, 0x44, 0x37, 0x25, 0x2b, 0xb1, 0x13, 0x8c, 0x9b, 0x33, 0xc5, 0x61, 0x5b, 0x33, 0xdd,
0x77, 0x55, 0xa7, 0xaa, 0x7a, 0xc6, 0x9d, 0xc5, 0x52, 0xf8, 0x23, 0x4e, 0x44, 0x08, 0xc1, 0x25, 0xed, 0xee, 0x1e, 0x52, 0xe3, 0x8d, 0x00, 0xe7, 0x07, 0x39, 0xc5, 0x08, 0x82, 0xe4, 0xe2, 0x00,
0x48, 0x08, 0x29, 0x48, 0x28, 0x1c, 0x38, 0xc2, 0x25, 0x20, 0x71, 0xe0, 0x84, 0x84, 0x38, 0xe4, 0x41, 0x00, 0x07, 0x08, 0x9c, 0x43, 0x8e, 0xc9, 0xc5, 0x09, 0x90, 0x43, 0x2e, 0x09, 0x10, 0xe4,
0x14, 0x71, 0x04, 0x0e, 0x10, 0x71, 0x41, 0xe2, 0x8a, 0xd0, 0xf7, 0xbd, 0x3f, 0xf5, 0x5e, 0x55, 0xe0, 0x93, 0x91, 0x63, 0x92, 0x43, 0x12, 0xe4, 0x12, 0x20, 0xd7, 0x20, 0x78, 0xaf, 0x7e, 0xba,
0xb5, 0xed, 0xfc, 0x81, 0x5b, 0xbf, 0xdf, 0xfb, 0xea, 0xfd, 0xfd, 0xfe, 0xbd, 0xef, 0x7d, 0xaf, 0xaa, 0xbb, 0x29, 0xca, 0x3f, 0xc9, 0x6d, 0xea, 0xab, 0xd7, 0xf5, 0xfb, 0xfe, 0xea, 0xd5, 0xab,
0xa1, 0x1e, 0x4f, 0xfa, 0xd7, 0x27, 0x71, 0x94, 0x46, 0x6c, 0x61, 0x14, 0xc6, 0x93, 0x7e, 0xf7, 0x81, 0x46, 0x12, 0xf7, 0x6f, 0xc6, 0x49, 0x94, 0x45, 0x6c, 0x76, 0x14, 0x26, 0x71, 0xbf, 0xfb,
0xf5, 0x61, 0x14, 0x0d, 0x47, 0xfc, 0x86, 0x3f, 0x09, 0x6e, 0xf8, 0x61, 0x18, 0xa5, 0x7e, 0x1a, 0xfa, 0x30, 0x8a, 0x86, 0x23, 0x7e, 0xcb, 0x8f, 0x83, 0x5b, 0x7e, 0x18, 0x46, 0x99, 0x9f, 0x05,
0x44, 0x61, 0x22, 0x88, 0xdc, 0xaf, 0x41, 0xeb, 0x1e, 0x0f, 0x0f, 0x39, 0x1f, 0x78, 0xfc, 0x1b, 0x51, 0x98, 0x0a, 0x22, 0xf7, 0x6b, 0xb0, 0x74, 0x9f, 0x87, 0x07, 0x9c, 0x0f, 0x3c, 0xfe, 0x8d,
0x53, 0x9e, 0xa4, 0xec, 0xe7, 0x60, 0xcd, 0xe7, 0xdf, 0xe4, 0x7c, 0xd0, 0x9b, 0xf8, 0x49, 0x32, 0x09, 0x4f, 0x33, 0xf6, 0x33, 0xb0, 0xe2, 0xf3, 0x6f, 0x72, 0x3e, 0xe8, 0xc5, 0x7e, 0x9a, 0xc6,
0x39, 0x89, 0xfd, 0x84, 0x77, 0x9c, 0xcb, 0xce, 0xb5, 0xa6, 0xd7, 0x16, 0x15, 0x07, 0x1a, 0x67, 0xc7, 0x89, 0x9f, 0xf2, 0x8e, 0x73, 0xd5, 0xb9, 0xd1, 0xf2, 0xda, 0xa2, 0x62, 0x5f, 0xe3, 0xec,
0x6f, 0x41, 0x33, 0x41, 0x52, 0x1e, 0xa6, 0x71, 0x34, 0x99, 0x75, 0x2a, 0x44, 0xd7, 0x40, 0x6c, 0x2d, 0x68, 0xa5, 0x48, 0xca, 0xc3, 0x2c, 0x89, 0xe2, 0x69, 0xa7, 0x46, 0x74, 0x4d, 0xc4, 0x76,
0x57, 0x40, 0xee, 0x08, 0x56, 0x75, 0x0f, 0xc9, 0x24, 0x0a, 0x13, 0xce, 0x6e, 0xc2, 0x46, 0x3f, 0x04, 0xe4, 0x8e, 0x60, 0x59, 0xf7, 0x90, 0xc6, 0x51, 0x98, 0x72, 0x76, 0x1b, 0xd6, 0xfa, 0x41,
0x98, 0x9c, 0xf0, 0xb8, 0x47, 0x1f, 0x8f, 0x43, 0x3e, 0x8e, 0xc2, 0xa0, 0xdf, 0x71, 0x2e, 0x57, 0x7c, 0xcc, 0x93, 0x1e, 0x7d, 0x3c, 0x0e, 0xf9, 0x38, 0x0a, 0x83, 0x7e, 0xc7, 0xb9, 0x3a, 0x73,
0xaf, 0xd5, 0x3d, 0x26, 0xea, 0xf0, 0x8b, 0x8f, 0x64, 0x0d, 0xbb, 0x0a, 0xab, 0x3c, 0x14, 0x38, 0xa3, 0xe1, 0x31, 0x51, 0x87, 0x5f, 0x7c, 0x24, 0x6b, 0xd8, 0x75, 0x58, 0xe6, 0xa1, 0xc0, 0xf9,
0x1f, 0xd0, 0x57, 0xb2, 0xab, 0x56, 0x06, 0xe3, 0x07, 0xee, 0xdf, 0x3b, 0xb0, 0x76, 0x3f, 0x0c, 0x80, 0xbe, 0x92, 0x5d, 0x2d, 0xe5, 0x30, 0x7e, 0xe0, 0xfe, 0xad, 0x03, 0x2b, 0x0f, 0xc2, 0x20,
0xd2, 0x27, 0xfe, 0x68, 0xc4, 0x53, 0x35, 0xa7, 0xab, 0xb0, 0x7a, 0x46, 0x00, 0xcd, 0xe9, 0x2c, 0x7b, 0xea, 0x8f, 0x46, 0x3c, 0x53, 0x73, 0xba, 0x0e, 0xcb, 0xa7, 0x04, 0xd0, 0x9c, 0x4e, 0xa3,
0x8a, 0x07, 0x72, 0x46, 0x2d, 0x01, 0x1f, 0x48, 0x74, 0xee, 0xc8, 0x2a, 0x73, 0x47, 0x56, 0xba, 0x64, 0x20, 0x67, 0xb4, 0x24, 0xe0, 0x7d, 0x89, 0x9e, 0x39, 0xb2, 0xda, 0x99, 0x23, 0xab, 0x5c,
0x5c, 0xd5, 0x39, 0xcb, 0x75, 0x15, 0x56, 0x63, 0xde, 0x8f, 0x4e, 0x79, 0x3c, 0xeb, 0x9d, 0x05, 0xae, 0x99, 0x33, 0x96, 0xeb, 0x3a, 0x2c, 0x27, 0xbc, 0x1f, 0x9d, 0xf0, 0x64, 0xda, 0x3b, 0x0d,
0xe1, 0x20, 0x3a, 0xeb, 0xd4, 0x2e, 0x3b, 0xd7, 0x16, 0xbc, 0x96, 0x82, 0x9f, 0x10, 0xea, 0x6e, 0xc2, 0x41, 0x74, 0xda, 0xa9, 0x5f, 0x75, 0x6e, 0xcc, 0x7a, 0x4b, 0x0a, 0x7e, 0x4a, 0xa8, 0xbb,
0x00, 0x33, 0x67, 0x21, 0xd6, 0xcd, 0x1d, 0xc2, 0xfa, 0xe3, 0x70, 0x14, 0xf5, 0x9f, 0xfe, 0x84, 0x06, 0xcc, 0x9c, 0x85, 0x58, 0x37, 0x77, 0x08, 0xab, 0x4f, 0xc2, 0x51, 0xd4, 0x7f, 0xf6, 0x63,
0xb3, 0x2b, 0xe9, 0xbe, 0x52, 0xda, 0xfd, 0x26, 0x6c, 0xd8, 0x1d, 0xc9, 0x01, 0x70, 0x38, 0xbf, 0xce, 0xae, 0xa2, 0xfb, 0x5a, 0x65, 0xf7, 0xeb, 0xb0, 0x66, 0x77, 0x24, 0x07, 0xc0, 0xe1, 0xe2,
0x73, 0xe2, 0x87, 0x43, 0xae, 0x9a, 0x54, 0x43, 0xf8, 0x24, 0xb4, 0xfb, 0xd3, 0x38, 0xe6, 0x61, 0xd6, 0xb1, 0x1f, 0x0e, 0xb9, 0x6a, 0x52, 0x0d, 0xe1, 0x93, 0xd0, 0xee, 0x4f, 0x92, 0x84, 0x87,
0x61, 0x0c, 0xab, 0x12, 0xd7, 0x83, 0x78, 0x0b, 0x9a, 0x21, 0x3f, 0xcb, 0xc8, 0x24, 0xcb, 0x84, 0xa5, 0x31, 0x2c, 0x4b, 0x5c, 0x0f, 0xe2, 0x2d, 0x68, 0x85, 0xfc, 0x34, 0x27, 0x93, 0x2c, 0x13,
0xfc, 0x4c, 0x91, 0xb8, 0x1d, 0xd8, 0xcc, 0x77, 0x23, 0x07, 0xf0, 0x9d, 0x0a, 0x34, 0x1e, 0xc5, 0xf2, 0x53, 0x45, 0xe2, 0x76, 0x60, 0xbd, 0xd8, 0x8d, 0x1c, 0xc0, 0x77, 0x6a, 0xd0, 0x7c, 0x9c,
0x7e, 0x98, 0xf8, 0x7d, 0xe4, 0x62, 0xd6, 0x81, 0xa5, 0xf4, 0x59, 0xef, 0xc4, 0x4f, 0x4e, 0xa8, 0xf8, 0x61, 0xea, 0xf7, 0x91, 0x8b, 0x59, 0x07, 0xe6, 0xb3, 0xe7, 0xbd, 0x63, 0x3f, 0x3d, 0xa6,
0xbb, 0xba, 0xa7, 0x8a, 0x6c, 0x13, 0x16, 0xfd, 0x71, 0x34, 0x0d, 0x53, 0xea, 0xa0, 0xea, 0xc9, 0xee, 0x1a, 0x9e, 0x2a, 0xb2, 0x75, 0x98, 0xf3, 0xc7, 0xd1, 0x24, 0xcc, 0xa8, 0x83, 0x19, 0x4f,
0x12, 0x7b, 0x07, 0xd6, 0xc2, 0xe9, 0xb8, 0xd7, 0x8f, 0xc2, 0xe3, 0x20, 0x1e, 0x0b, 0x59, 0xa0, 0x96, 0xd8, 0x3b, 0xb0, 0x12, 0x4e, 0xc6, 0xbd, 0x7e, 0x14, 0x1e, 0x05, 0xc9, 0x58, 0xc8, 0x02,
0xfd, 0x5a, 0xf0, 0x8a, 0x15, 0xec, 0x12, 0xc0, 0x11, 0xae, 0x83, 0xe8, 0xa2, 0x46, 0x5d, 0x18, 0xed, 0xd7, 0xac, 0x57, 0xae, 0x60, 0x57, 0x00, 0x0e, 0x71, 0x1d, 0x44, 0x17, 0x75, 0xea, 0xc2,
0x08, 0x73, 0xa1, 0x29, 0x4b, 0x3c, 0x18, 0x9e, 0xa4, 0x9d, 0x05, 0x6a, 0xc8, 0xc2, 0xb0, 0x8d, 0x40, 0x98, 0x0b, 0x2d, 0x59, 0xe2, 0xc1, 0xf0, 0x38, 0xeb, 0xcc, 0x52, 0x43, 0x16, 0x86, 0x6d,
0x34, 0x18, 0xf3, 0x5e, 0x92, 0xfa, 0xe3, 0x49, 0x67, 0x91, 0x46, 0x63, 0x20, 0x54, 0x1f, 0xa5, 0x64, 0xc1, 0x98, 0xf7, 0xd2, 0xcc, 0x1f, 0xc7, 0x9d, 0x39, 0x1a, 0x8d, 0x81, 0x50, 0x7d, 0x94,
0xfe, 0xa8, 0x77, 0xcc, 0x79, 0xd2, 0x59, 0x92, 0xf5, 0x1a, 0x61, 0x6f, 0x43, 0x6b, 0xc0, 0x93, 0xf9, 0xa3, 0xde, 0x11, 0xe7, 0x69, 0x67, 0x5e, 0xd6, 0x6b, 0x84, 0xbd, 0x0d, 0x4b, 0x03, 0x9e,
0xb4, 0xe7, 0x0f, 0x06, 0x31, 0x4f, 0x12, 0x9e, 0x74, 0x96, 0x89, 0x1b, 0x73, 0x28, 0xae, 0xda, 0x66, 0x3d, 0x7f, 0x30, 0x48, 0x78, 0x9a, 0xf2, 0xb4, 0xb3, 0x40, 0xdc, 0x58, 0x40, 0x71, 0xd5,
0x3d, 0x9e, 0x1a, 0xab, 0x93, 0xc8, 0xdd, 0x71, 0xf7, 0x81, 0x19, 0xf0, 0x1d, 0x9e, 0xfa, 0xc1, 0xee, 0xf3, 0xcc, 0x58, 0x9d, 0x54, 0xee, 0x8e, 0xbb, 0x07, 0xcc, 0x80, 0xb7, 0x79, 0xe6, 0x07,
0x28, 0x61, 0xef, 0x41, 0x33, 0x35, 0x88, 0x49, 0xfa, 0x1a, 0xdb, 0xec, 0x3a, 0xa9, 0x8d, 0xeb, 0xa3, 0x94, 0xbd, 0x07, 0xad, 0xcc, 0x20, 0x26, 0xe9, 0x6b, 0x6e, 0xb2, 0x9b, 0xa4, 0x36, 0x6e,
0xc6, 0x07, 0x9e, 0x45, 0xe7, 0xde, 0x83, 0xe5, 0xbb, 0x9c, 0xef, 0x07, 0xe3, 0x20, 0x65, 0x9b, 0x1a, 0x1f, 0x78, 0x16, 0x9d, 0x7b, 0x1f, 0x16, 0xee, 0x71, 0xbe, 0x17, 0x8c, 0x83, 0x8c, 0xad,
0xb0, 0x70, 0x1c, 0x3c, 0xe3, 0x62, 0xb3, 0xab, 0x7b, 0xe7, 0x3c, 0x51, 0x64, 0x5d, 0x58, 0x9a, 0xc3, 0xec, 0x51, 0xf0, 0x9c, 0x8b, 0xcd, 0x9e, 0xd9, 0xbd, 0xe0, 0x89, 0x22, 0xeb, 0xc2, 0x7c,
0xf0, 0xb8, 0xcf, 0xd5, 0xf2, 0xef, 0x9d, 0xf3, 0x14, 0x70, 0x7b, 0x09, 0x16, 0x46, 0xf8, 0xb1, 0xcc, 0x93, 0x3e, 0x57, 0xcb, 0xbf, 0x7b, 0xc1, 0x53, 0xc0, 0xdd, 0x79, 0x98, 0x1d, 0xe1, 0xc7,
0xfb, 0xbd, 0x0a, 0x34, 0x0e, 0x79, 0xa8, 0x99, 0x88, 0x41, 0x0d, 0xa7, 0x24, 0x19, 0x87, 0x7e, 0xee, 0xf7, 0x6a, 0xd0, 0x3c, 0xe0, 0xa1, 0x66, 0x22, 0x06, 0x75, 0x9c, 0x92, 0x64, 0x1c, 0xfa,
0xb3, 0x37, 0xa1, 0x41, 0xd3, 0x4c, 0xd2, 0x38, 0x08, 0x87, 0xd4, 0x58, 0xdd, 0x03, 0x84, 0x0e, 0xcd, 0xde, 0x84, 0x26, 0x4d, 0x33, 0xcd, 0x92, 0x20, 0x1c, 0x52, 0x63, 0x0d, 0x0f, 0x10, 0x3a,
0x09, 0x61, 0x6d, 0xa8, 0xfa, 0xe3, 0x94, 0x76, 0xb0, 0xea, 0xe1, 0x4f, 0x64, 0xb0, 0x89, 0x3f, 0x20, 0x84, 0xb5, 0x61, 0xc6, 0x1f, 0x67, 0xb4, 0x83, 0x33, 0x1e, 0xfe, 0x44, 0x06, 0x8b, 0xfd,
0x1b, 0x23, 0x2f, 0xea, 0x5d, 0x6b, 0x7a, 0x0d, 0x89, 0xed, 0xe1, 0xb6, 0x5d, 0x87, 0x75, 0x93, 0xe9, 0x18, 0x79, 0x51, 0xef, 0x5a, 0xcb, 0x6b, 0x4a, 0x6c, 0x17, 0xb7, 0xed, 0x26, 0xac, 0x9a,
0x44, 0xb5, 0xbe, 0x40, 0xad, 0xaf, 0x19, 0x94, 0xb2, 0x93, 0xab, 0xb0, 0xaa, 0xe8, 0x63, 0x31, 0x24, 0xaa, 0xf5, 0x59, 0x6a, 0x7d, 0xc5, 0xa0, 0x94, 0x9d, 0x5c, 0x87, 0x65, 0x45, 0x9f, 0x88,
0x58, 0xda, 0xc7, 0xba, 0xd7, 0x92, 0xb0, 0x9a, 0xc2, 0x35, 0x68, 0x1f, 0x07, 0xa1, 0x3f, 0xea, 0xc1, 0xd2, 0x3e, 0x36, 0xbc, 0x25, 0x09, 0xab, 0x29, 0xdc, 0x80, 0xf6, 0x51, 0x10, 0xfa, 0xa3,
0xf5, 0x47, 0xe9, 0x69, 0x6f, 0xc0, 0x47, 0xa9, 0x4f, 0x3b, 0xba, 0xe0, 0xb5, 0x08, 0xdf, 0x19, 0x5e, 0x7f, 0x94, 0x9d, 0xf4, 0x06, 0x7c, 0x94, 0xf9, 0xb4, 0xa3, 0xb3, 0xde, 0x12, 0xe1, 0x5b,
0xa5, 0xa7, 0x77, 0x10, 0x65, 0xef, 0x40, 0xfd, 0x98, 0xf3, 0x1e, 0xad, 0x44, 0x67, 0xf9, 0xb2, 0xa3, 0xec, 0x64, 0x1b, 0x51, 0xf6, 0x0e, 0x34, 0x8e, 0x38, 0xef, 0xd1, 0x4a, 0x74, 0x16, 0xae,
0x73, 0xad, 0xb1, 0xbd, 0x2a, 0x97, 0x5e, 0xad, 0xae, 0xb7, 0x7c, 0x2c, 0x7f, 0xb9, 0x7f, 0xe4, 0x3a, 0x37, 0x9a, 0x9b, 0xcb, 0x72, 0xe9, 0xd5, 0xea, 0x7a, 0x0b, 0x47, 0xf2, 0x97, 0xfb, 0x07,
0x40, 0x53, 0x2c, 0x95, 0x54, 0xa1, 0x57, 0x60, 0x45, 0x8d, 0x88, 0xc7, 0x71, 0x14, 0x4b, 0xf6, 0x0e, 0xb4, 0xc4, 0x52, 0x49, 0x15, 0x7a, 0x0d, 0x16, 0xd5, 0x88, 0x78, 0x92, 0x44, 0x89, 0x64,
0xb7, 0x41, 0xb6, 0x05, 0x6d, 0x05, 0x4c, 0x62, 0x1e, 0x8c, 0xfd, 0x21, 0x97, 0xf2, 0x56, 0xc0, 0x7f, 0x1b, 0x64, 0x1b, 0xd0, 0x56, 0x40, 0x9c, 0xf0, 0x60, 0xec, 0x0f, 0xb9, 0x94, 0xb7, 0x12,
0xd9, 0x76, 0xd6, 0x62, 0x1c, 0x4d, 0x53, 0xa1, 0xc4, 0x1a, 0xdb, 0x4d, 0x39, 0x28, 0x0f, 0x31, 0xce, 0x36, 0xf3, 0x16, 0x93, 0x68, 0x92, 0x09, 0x25, 0xd6, 0xdc, 0x6c, 0xc9, 0x41, 0x79, 0x88,
0xcf, 0x26, 0x71, 0xbf, 0xed, 0x00, 0xc3, 0x61, 0x3d, 0x8a, 0x44, 0xb5, 0x5c, 0x85, 0xfc, 0x0e, 0x79, 0x36, 0x89, 0xfb, 0x6d, 0x07, 0x18, 0x0e, 0xeb, 0x71, 0x24, 0xaa, 0xe5, 0x2a, 0x14, 0x77,
0x38, 0xaf, 0xbc, 0x03, 0x95, 0x79, 0x3b, 0x70, 0x05, 0x16, 0xa9, 0x4b, 0x94, 0xd5, 0x6a, 0x61, 0xc0, 0x79, 0xe5, 0x1d, 0xa8, 0x9d, 0xb5, 0x03, 0xd7, 0x60, 0x8e, 0xba, 0x44, 0x59, 0x9d, 0x29,
0x58, 0xb2, 0xce, 0xfd, 0xae, 0x03, 0x4d, 0xd4, 0x1c, 0x21, 0x1f, 0x1d, 0x44, 0x41, 0x98, 0xb2, 0x0d, 0x4b, 0xd6, 0xb9, 0xdf, 0x75, 0xa0, 0x85, 0x9a, 0x23, 0xe4, 0xa3, 0xfd, 0x28, 0x08, 0x33,
0x9b, 0xc0, 0x8e, 0xa7, 0xe1, 0x20, 0x08, 0x87, 0xbd, 0xf4, 0x59, 0x30, 0xe8, 0x1d, 0xcd, 0xb0, 0x76, 0x1b, 0xd8, 0xd1, 0x24, 0x1c, 0x04, 0xe1, 0xb0, 0x97, 0x3d, 0x0f, 0x06, 0xbd, 0xc3, 0x29,
0x09, 0x1a, 0xcf, 0xde, 0x39, 0xaf, 0xa4, 0x8e, 0xbd, 0x03, 0x6d, 0x0b, 0x4d, 0xd2, 0x58, 0x8c, 0x36, 0x41, 0xe3, 0xd9, 0xbd, 0xe0, 0x55, 0xd4, 0xb1, 0x77, 0xa0, 0x6d, 0xa1, 0x69, 0x96, 0x88,
0x6a, 0xef, 0x9c, 0x57, 0xa8, 0x41, 0xf9, 0x8f, 0xa6, 0xe9, 0x64, 0x9a, 0xf6, 0x82, 0x70, 0xc0, 0x51, 0xed, 0x5e, 0xf0, 0x4a, 0x35, 0x28, 0xff, 0xd1, 0x24, 0x8b, 0x27, 0x59, 0x2f, 0x08, 0x07,
0x9f, 0xd1, 0x9a, 0xad, 0x78, 0x16, 0x76, 0xbb, 0x05, 0x4d, 0xf3, 0x3b, 0xf7, 0x73, 0xd0, 0xde, 0xfc, 0x39, 0xad, 0xd9, 0xa2, 0x67, 0x61, 0x77, 0x97, 0xa0, 0x65, 0x7e, 0xe7, 0x7e, 0x0e, 0xda,
0x47, 0xc5, 0x10, 0x06, 0xe1, 0xf0, 0x96, 0x90, 0x5e, 0xd4, 0x56, 0x93, 0xe9, 0xd1, 0x53, 0x3e, 0x7b, 0xa8, 0x18, 0xc2, 0x20, 0x1c, 0xde, 0x11, 0xd2, 0x8b, 0xda, 0x2a, 0x9e, 0x1c, 0x3e, 0xe3,
0x93, 0xfb, 0x28, 0x4b, 0x28, 0x12, 0x27, 0x51, 0x92, 0xca, 0x75, 0xa1, 0xdf, 0xee, 0xbf, 0x38, 0x53, 0xb9, 0x8f, 0xb2, 0x84, 0x22, 0x71, 0x1c, 0xa5, 0x99, 0x5c, 0x17, 0xfa, 0xed, 0xfe, 0xb3,
0xb0, 0x8a, 0x8b, 0xfe, 0x91, 0x1f, 0xce, 0xd4, 0x8a, 0xef, 0x43, 0x13, 0x9b, 0x7a, 0x14, 0xdd, 0x03, 0xcb, 0xb8, 0xe8, 0x1f, 0xf9, 0xe1, 0x54, 0xad, 0xf8, 0x1e, 0xb4, 0xb0, 0xa9, 0xc7, 0xd1,
0x12, 0x3a, 0x4f, 0xc8, 0xf2, 0x35, 0xb9, 0x48, 0x39, 0xea, 0xeb, 0x26, 0x29, 0x9a, 0xe9, 0x99, 0x1d, 0xa1, 0xf3, 0x84, 0x2c, 0xdf, 0x90, 0x8b, 0x54, 0xa0, 0xbe, 0x69, 0x92, 0xa2, 0x99, 0x9e,
0x67, 0x7d, 0x8d, 0x42, 0x97, 0xfa, 0xf1, 0x90, 0xa7, 0xa4, 0x0d, 0xa5, 0x76, 0x04, 0x01, 0xed, 0x7a, 0xd6, 0xd7, 0x28, 0x74, 0x99, 0x9f, 0x0c, 0x79, 0x46, 0xda, 0x50, 0x6a, 0x47, 0x10, 0xd0,
0x44, 0xe1, 0x31, 0xbb, 0x0c, 0xcd, 0xc4, 0x4f, 0x7b, 0x13, 0x1e, 0xd3, 0xaa, 0x91, 0xe0, 0x54, 0x56, 0x14, 0x1e, 0xb1, 0xab, 0xd0, 0x4a, 0xfd, 0xac, 0x17, 0xf3, 0x84, 0x56, 0x8d, 0x04, 0x67,
0x3d, 0x48, 0xfc, 0xf4, 0x80, 0xc7, 0xb7, 0x67, 0x29, 0xef, 0x7e, 0x1e, 0xd6, 0x0a, 0xbd, 0xa0, 0xc6, 0x83, 0xd4, 0xcf, 0xf6, 0x79, 0x72, 0x77, 0x9a, 0xf1, 0xee, 0xe7, 0x61, 0xa5, 0xd4, 0x0b,
0xac, 0x66, 0x53, 0xc4, 0x9f, 0x6c, 0x03, 0x16, 0x4e, 0xfd, 0xd1, 0x94, 0x4b, 0x25, 0x2d, 0x0a, 0xca, 0x6a, 0x3e, 0x45, 0xfc, 0xc9, 0xd6, 0x60, 0xf6, 0xc4, 0x1f, 0x4d, 0xb8, 0x54, 0xd2, 0xa2,
0x1f, 0x54, 0xde, 0x77, 0xdc, 0xb7, 0xa1, 0x9d, 0x0d, 0x5b, 0x32, 0x3d, 0x83, 0x1a, 0xae, 0xa0, 0xf0, 0x41, 0xed, 0x7d, 0xc7, 0x7d, 0x1b, 0xda, 0xf9, 0xb0, 0x25, 0xd3, 0x33, 0xa8, 0xe3, 0x0a,
0x6c, 0x80, 0x7e, 0xbb, 0xbf, 0xe9, 0x08, 0xc2, 0x9d, 0x28, 0xd0, 0x0a, 0x0f, 0x09, 0x51, 0x2f, 0xca, 0x06, 0xe8, 0xb7, 0xfb, 0xeb, 0x8e, 0x20, 0xdc, 0x8a, 0x02, 0xad, 0xf0, 0x90, 0x10, 0xf5,
0x2a, 0x42, 0xfc, 0x3d, 0xd7, 0x20, 0xfc, 0xf4, 0x93, 0x75, 0xaf, 0xc2, 0x9a, 0x31, 0x84, 0x17, 0xa2, 0x22, 0xc4, 0xdf, 0x67, 0x1a, 0x84, 0x9f, 0x7c, 0xb2, 0xee, 0x75, 0x58, 0x31, 0x86, 0xf0,
0x0c, 0xf6, 0xdb, 0x0e, 0xac, 0x3d, 0xe0, 0x67, 0x72, 0xd7, 0xd5, 0x68, 0xdf, 0x87, 0x5a, 0x3a, 0x92, 0xc1, 0x7e, 0xdb, 0x81, 0x95, 0x87, 0xfc, 0x54, 0xee, 0xba, 0x1a, 0xed, 0xfb, 0x50, 0xcf,
0x9b, 0x08, 0x27, 0xab, 0xb5, 0x7d, 0x45, 0x6e, 0x5a, 0x81, 0xee, 0xba, 0x2c, 0x3e, 0x9a, 0x4d, 0xa6, 0xb1, 0x70, 0xb2, 0x96, 0x36, 0xaf, 0xc9, 0x4d, 0x2b, 0xd1, 0xdd, 0x94, 0xc5, 0xc7, 0xd3,
0xb8, 0x47, 0x5f, 0xb8, 0x9f, 0x83, 0x86, 0x01, 0xb2, 0x0b, 0xb0, 0xfe, 0xe4, 0xfe, 0xa3, 0x07, 0x98, 0x7b, 0xf4, 0x85, 0xfb, 0x39, 0x68, 0x1a, 0x20, 0xbb, 0x04, 0xab, 0x4f, 0x1f, 0x3c, 0x7e,
0xbb, 0x87, 0x87, 0xbd, 0x83, 0xc7, 0xb7, 0xbf, 0xb8, 0xfb, 0xab, 0xbd, 0xbd, 0x5b, 0x87, 0x7b, 0xb8, 0x73, 0x70, 0xd0, 0xdb, 0x7f, 0x72, 0xf7, 0x8b, 0x3b, 0xbf, 0xdc, 0xdb, 0xbd, 0x73, 0xb0,
0xed, 0x73, 0x6c, 0x13, 0xd8, 0x83, 0xdd, 0xc3, 0x47, 0xbb, 0x77, 0x2c, 0xdc, 0x71, 0xbb, 0xd0, 0xdb, 0xbe, 0xc0, 0xd6, 0x81, 0x3d, 0xdc, 0x39, 0x78, 0xbc, 0xb3, 0x6d, 0xe1, 0x8e, 0xdb, 0x85,
0x79, 0xc0, 0xcf, 0x9e, 0x04, 0x69, 0xc8, 0x93, 0xc4, 0xee, 0xcd, 0xbd, 0x0e, 0xcc, 0x1c, 0x82, 0xce, 0x43, 0x7e, 0xfa, 0x34, 0xc8, 0x42, 0x9e, 0xa6, 0x76, 0x6f, 0xee, 0x4d, 0x60, 0xe6, 0x10,
0x9c, 0x55, 0x07, 0x96, 0xa4, 0xc5, 0x51, 0x06, 0x57, 0x16, 0xdd, 0xb7, 0x81, 0x1d, 0x06, 0xc3, 0xe4, 0xac, 0x3a, 0x30, 0x2f, 0x2d, 0x8e, 0x32, 0xb8, 0xb2, 0xe8, 0xbe, 0x0d, 0xec, 0x20, 0x18,
0xf0, 0x23, 0x9e, 0x24, 0xfe, 0x50, 0xab, 0x82, 0x36, 0x54, 0xc7, 0xc9, 0x50, 0x6a, 0x00, 0xfc, 0x86, 0x1f, 0xf1, 0x34, 0xf5, 0x87, 0x5a, 0x15, 0xb4, 0x61, 0x66, 0x9c, 0x0e, 0xa5, 0x06, 0xc0,
0xe9, 0x7e, 0x0a, 0xd6, 0x2d, 0x3a, 0xd9, 0xf0, 0xeb, 0x50, 0x4f, 0x82, 0x61, 0xe8, 0xa7, 0xd3, 0x9f, 0xee, 0xa7, 0x60, 0xd5, 0xa2, 0x93, 0x0d, 0xbf, 0x0e, 0x8d, 0x34, 0x18, 0x86, 0x7e, 0x36,
0x98, 0xcb, 0xa6, 0x33, 0xc0, 0xbd, 0x0b, 0x1b, 0x5f, 0xe6, 0x71, 0x70, 0x3c, 0x7b, 0x59, 0xf3, 0x49, 0xb8, 0x6c, 0x3a, 0x07, 0xdc, 0x7b, 0xb0, 0xf6, 0x65, 0x9e, 0x04, 0x47, 0xd3, 0xf3, 0x9a,
0x76, 0x3b, 0x95, 0x7c, 0x3b, 0xbb, 0x70, 0x3e, 0xd7, 0x8e, 0xec, 0x5e, 0x30, 0xa2, 0xdc, 0xae, 0xb7, 0xdb, 0xa9, 0x15, 0xdb, 0xd9, 0x81, 0x8b, 0x85, 0x76, 0x64, 0xf7, 0x82, 0x11, 0xe5, 0x76,
0x65, 0x4f, 0x14, 0x0c, 0xb1, 0xac, 0x98, 0x62, 0xe9, 0x3e, 0x06, 0xb6, 0x13, 0x85, 0x21, 0xef, 0x2d, 0x78, 0xa2, 0x60, 0x88, 0x65, 0xcd, 0x14, 0x4b, 0xf7, 0x09, 0xb0, 0xad, 0x28, 0x0c, 0x79,
0xa7, 0x07, 0x9c, 0xc7, 0x99, 0xe7, 0x9c, 0x71, 0x5d, 0x63, 0xfb, 0x82, 0xdc, 0xc7, 0xbc, 0xac, 0x3f, 0xdb, 0xe7, 0x3c, 0xc9, 0x3d, 0xe7, 0x9c, 0xeb, 0x9a, 0x9b, 0x97, 0xe4, 0x3e, 0x16, 0x65,
0x4b, 0x76, 0x64, 0x50, 0x9b, 0xf0, 0x78, 0x4c, 0x0d, 0x2f, 0x7b, 0xf4, 0xdb, 0x3d, 0x0f, 0xeb, 0x5d, 0xb2, 0x23, 0x83, 0x7a, 0xcc, 0x93, 0x31, 0x35, 0xbc, 0xe0, 0xd1, 0x6f, 0xf7, 0x22, 0xac,
0x56, 0xb3, 0xd2, 0xe9, 0x79, 0x17, 0xce, 0xdf, 0x09, 0x92, 0x7e, 0xb1, 0xc3, 0x0e, 0x2c, 0x4d, 0x5a, 0xcd, 0x4a, 0xa7, 0xe7, 0x5d, 0xb8, 0xb8, 0x1d, 0xa4, 0xfd, 0x72, 0x87, 0x1d, 0x98, 0x8f,
0xa6, 0x47, 0xbd, 0x4c, 0xa6, 0x54, 0x11, 0x7d, 0x81, 0xfc, 0x27, 0xb2, 0xb1, 0xdf, 0x75, 0xa0, 0x27, 0x87, 0xbd, 0x5c, 0xa6, 0x54, 0x11, 0x7d, 0x81, 0xe2, 0x27, 0xb2, 0xb1, 0xdf, 0x76, 0xa0,
0xb6, 0xf7, 0x68, 0x7f, 0x87, 0x75, 0x61, 0x39, 0x08, 0xfb, 0xd1, 0x18, 0xd5, 0xae, 0x98, 0xb4, 0xbe, 0xfb, 0x78, 0x6f, 0x8b, 0x75, 0x61, 0x21, 0x08, 0xfb, 0xd1, 0x18, 0xd5, 0xae, 0x98, 0xb4,
0x2e, 0xcf, 0x95, 0x95, 0xd7, 0xa1, 0x4e, 0xda, 0x1a, 0xdd, 0x1b, 0xe9, 0xe4, 0x66, 0x00, 0xba, 0x2e, 0x9f, 0x29, 0x2b, 0xaf, 0x43, 0x83, 0xb4, 0x35, 0xba, 0x37, 0xd2, 0xc9, 0xcd, 0x01, 0x74,
0x56, 0xfc, 0xd9, 0x24, 0x88, 0xc9, 0x77, 0x52, 0x1e, 0x51, 0x8d, 0x34, 0x62, 0xb1, 0xc2, 0xfd, 0xad, 0xf8, 0xf3, 0x38, 0x48, 0xc8, 0x77, 0x52, 0x1e, 0x51, 0x9d, 0x34, 0x62, 0xb9, 0xc2, 0xfd,
0x9f, 0x1a, 0x2c, 0x49, 0x5d, 0x4d, 0xfd, 0xf5, 0xd3, 0xe0, 0x94, 0xcb, 0x91, 0xc8, 0x12, 0x5a, 0x9f, 0x3a, 0xcc, 0x4b, 0x5d, 0x4d, 0xfd, 0xf5, 0xb3, 0xe0, 0x84, 0xcb, 0x91, 0xc8, 0x12, 0x5a,
0xb9, 0x98, 0x8f, 0xa3, 0x94, 0xf7, 0xac, 0x6d, 0xb0, 0x41, 0xa4, 0xea, 0x8b, 0x86, 0x7a, 0x13, 0xb9, 0x84, 0x8f, 0xa3, 0x8c, 0xf7, 0xac, 0x6d, 0xb0, 0x41, 0xa4, 0xea, 0x8b, 0x86, 0x7a, 0x31,
0xd4, 0xfa, 0x34, 0xb2, 0xba, 0x67, 0x83, 0xb8, 0x58, 0x08, 0xf4, 0x82, 0x01, 0x8d, 0xa9, 0xe6, 0x6a, 0x7d, 0x1a, 0x59, 0xc3, 0xb3, 0x41, 0x5c, 0x2c, 0x04, 0x7a, 0xc1, 0x80, 0xc6, 0x54, 0xf7,
0xa9, 0x22, 0xae, 0x44, 0xdf, 0x9f, 0xf8, 0xfd, 0x20, 0x9d, 0x49, 0xe1, 0xd6, 0x65, 0x6c, 0x7b, 0x54, 0x11, 0x57, 0xa2, 0xef, 0xc7, 0x7e, 0x3f, 0xc8, 0xa6, 0x52, 0xb8, 0x75, 0x19, 0xdb, 0x1e,
0x14, 0xf5, 0xfd, 0x51, 0xef, 0xc8, 0x1f, 0xf9, 0x61, 0x9f, 0x4b, 0xff, 0xcd, 0x06, 0xd1, 0x45, 0x45, 0x7d, 0x7f, 0xd4, 0x3b, 0xf4, 0x47, 0x7e, 0xd8, 0xe7, 0xd2, 0x7f, 0xb3, 0x41, 0x74, 0xd1,
0x93, 0x43, 0x52, 0x64, 0xc2, 0x8d, 0xcb, 0xa1, 0xe8, 0xea, 0xf5, 0xa3, 0xf1, 0x38, 0x48, 0xd1, 0xe4, 0x90, 0x14, 0x99, 0x70, 0xe3, 0x0a, 0x28, 0xba, 0x7a, 0xfd, 0x68, 0x3c, 0x0e, 0x32, 0xf4,
0xb3, 0x23, 0xab, 0x5f, 0xf5, 0x0c, 0x84, 0x66, 0x22, 0x4a, 0x67, 0x62, 0xf5, 0xea, 0xa2, 0x37, 0xec, 0xc8, 0xea, 0xcf, 0x78, 0x06, 0x42, 0x33, 0x11, 0xa5, 0x53, 0xb1, 0x7a, 0x0d, 0xd1, 0x9b,
0x0b, 0xc4, 0x56, 0xd0, 0x75, 0x40, 0x85, 0xf4, 0xf4, 0xac, 0x03, 0xa2, 0x95, 0x0c, 0xc1, 0x7d, 0x05, 0x62, 0x2b, 0xe8, 0x3a, 0xa0, 0x42, 0x7a, 0x76, 0xda, 0x01, 0xd1, 0x4a, 0x8e, 0xe0, 0x3e,
0x98, 0x86, 0x09, 0x4f, 0xd3, 0x11, 0x1f, 0xe8, 0x01, 0x35, 0x88, 0xac, 0x58, 0xc1, 0x6e, 0xc2, 0x4c, 0xc2, 0x94, 0x67, 0xd9, 0x88, 0x0f, 0xf4, 0x80, 0x9a, 0x44, 0x56, 0xae, 0x60, 0xb7, 0x61,
0xba, 0x70, 0x36, 0x13, 0x3f, 0x8d, 0x92, 0x93, 0x20, 0xe9, 0x25, 0xe8, 0xb6, 0x35, 0x89, 0xbe, 0x55, 0x38, 0x9b, 0xa9, 0x9f, 0x45, 0xe9, 0x71, 0x90, 0xf6, 0x52, 0x74, 0xdb, 0x5a, 0x44, 0x5f,
0xac, 0x8a, 0xbd, 0x0f, 0x17, 0x72, 0x70, 0xcc, 0xfb, 0x3c, 0x38, 0xe5, 0x83, 0xce, 0x0a, 0x7d, 0x55, 0xc5, 0xde, 0x87, 0x4b, 0x05, 0x38, 0xe1, 0x7d, 0x1e, 0x9c, 0xf0, 0x41, 0x67, 0x91, 0xbe,
0x35, 0xaf, 0x9a, 0x5d, 0x86, 0x06, 0xfa, 0xd8, 0xd3, 0xc9, 0xc0, 0x47, 0x3b, 0xdc, 0xa2, 0x7d, 0x3a, 0xab, 0x9a, 0x5d, 0x85, 0x26, 0xfa, 0xd8, 0x93, 0x78, 0xe0, 0xa3, 0x1d, 0x5e, 0xa2, 0x7d,
0x30, 0x21, 0xf6, 0x2e, 0xac, 0x4c, 0xb8, 0x30, 0x96, 0x27, 0xe9, 0xa8, 0x9f, 0x74, 0x56, 0xc9, 0x30, 0x21, 0xf6, 0x2e, 0x2c, 0xc6, 0x5c, 0x18, 0xcb, 0xe3, 0x6c, 0xd4, 0x4f, 0x3b, 0xcb, 0x64,
0x92, 0x35, 0xa4, 0x30, 0x21, 0xe7, 0x7a, 0x36, 0x05, 0x32, 0x65, 0x3f, 0x21, 0x67, 0xcb, 0x9f, 0xc9, 0x9a, 0x52, 0x98, 0x90, 0x73, 0x3d, 0x9b, 0x02, 0x99, 0xb2, 0x9f, 0x92, 0xb3, 0xe5, 0x4f,
0x75, 0xda, 0xc4, 0x6e, 0x19, 0x40, 0x32, 0x12, 0x07, 0xa7, 0x7e, 0xca, 0x3b, 0x6b, 0xc4, 0x5b, 0x3b, 0x6d, 0x62, 0xb7, 0x1c, 0x20, 0x19, 0x49, 0x82, 0x13, 0x3f, 0xe3, 0x9d, 0x15, 0xe2, 0x2d,
0xaa, 0xe8, 0xfe, 0xa9, 0x03, 0xeb, 0xfb, 0x41, 0x92, 0x4a, 0x26, 0xd4, 0xea, 0xf8, 0x4d, 0x68, 0x55, 0x74, 0xff, 0xd8, 0x81, 0xd5, 0xbd, 0x20, 0xcd, 0x24, 0x13, 0x6a, 0x75, 0xfc, 0x26, 0x34,
0x08, 0xf6, 0xeb, 0x45, 0xe1, 0x68, 0x26, 0x39, 0x12, 0x04, 0xf4, 0x30, 0x1c, 0xcd, 0xd8, 0x27, 0x05, 0xfb, 0xf5, 0xa2, 0x70, 0x34, 0x95, 0x1c, 0x09, 0x02, 0x7a, 0x14, 0x8e, 0xa6, 0xec, 0x13,
0x60, 0x25, 0x08, 0x4d, 0x12, 0x21, 0xc3, 0x4d, 0x05, 0x12, 0xd1, 0x9b, 0xd0, 0x98, 0x4c, 0x8f, 0xb0, 0x18, 0x84, 0x26, 0x89, 0x90, 0xe1, 0x96, 0x02, 0x89, 0xe8, 0x4d, 0x68, 0xc6, 0x93, 0xc3,
0x46, 0x41, 0x5f, 0x90, 0x54, 0x45, 0x2b, 0x02, 0x22, 0x02, 0x74, 0x92, 0xc4, 0x48, 0x04, 0x45, 0x51, 0xd0, 0x17, 0x24, 0x33, 0xa2, 0x15, 0x01, 0x11, 0x01, 0x3a, 0x49, 0x62, 0x24, 0x82, 0xa2,
0x8d, 0x28, 0x1a, 0x12, 0x43, 0x12, 0xf7, 0x36, 0x6c, 0xd8, 0x03, 0x94, 0xca, 0x6a, 0x0b, 0x96, 0x4e, 0x14, 0x4d, 0x89, 0x21, 0x89, 0x7b, 0x17, 0xd6, 0xec, 0x01, 0x4a, 0x65, 0xb5, 0x01, 0x0b,
0x25, 0x6f, 0x27, 0x9d, 0x06, 0xad, 0x4f, 0x4b, 0xae, 0x8f, 0x24, 0xf5, 0x74, 0xbd, 0xfb, 0xe7, 0x92, 0xb7, 0xd3, 0x4e, 0x93, 0xd6, 0x67, 0x49, 0xae, 0x8f, 0x24, 0xf5, 0x74, 0xbd, 0xfb, 0xa7,
0x35, 0x58, 0x97, 0xe8, 0xce, 0x28, 0x4a, 0xf8, 0xe1, 0x74, 0x3c, 0xf6, 0xe3, 0x12, 0xa1, 0x71, 0x75, 0x58, 0x95, 0xe8, 0xd6, 0x28, 0x4a, 0xf9, 0xc1, 0x64, 0x3c, 0xf6, 0x93, 0x0a, 0xa1, 0x71,
0x5e, 0x22, 0x34, 0x15, 0x5b, 0x68, 0x90, 0x95, 0x4f, 0xfc, 0x20, 0x14, 0x1e, 0x9e, 0x90, 0x38, 0xce, 0x11, 0x9a, 0x9a, 0x2d, 0x34, 0xc8, 0xca, 0xc7, 0x7e, 0x10, 0x0a, 0x0f, 0x4f, 0x48, 0x9c,
0x03, 0x61, 0xd7, 0x60, 0xb5, 0x3f, 0x8a, 0x12, 0xe1, 0xf5, 0x98, 0xc7, 0xa7, 0x3c, 0x5c, 0x14, 0x81, 0xb0, 0x1b, 0xb0, 0xdc, 0x1f, 0x45, 0xa9, 0xf0, 0x7a, 0xcc, 0xe3, 0x53, 0x11, 0x2e, 0x0b,
0xf2, 0x85, 0x32, 0x21, 0x37, 0x85, 0x74, 0x31, 0x27, 0xa4, 0x2e, 0x34, 0xb1, 0x51, 0xae, 0x74, 0xf9, 0x6c, 0x95, 0x90, 0x9b, 0x42, 0x3a, 0x57, 0x10, 0x52, 0x17, 0x5a, 0xd8, 0x28, 0x57, 0x3a,
0xce, 0x92, 0xf0, 0xc2, 0x4c, 0x0c, 0xc7, 0x93, 0x17, 0x09, 0x21, 0x7f, 0xab, 0x65, 0x02, 0x81, 0x67, 0x5e, 0x78, 0x61, 0x26, 0x86, 0xe3, 0x29, 0x8a, 0x84, 0x90, 0xbf, 0xe5, 0x2a, 0x81, 0xc0,
0xa7, 0x33, 0xd4, 0x69, 0x06, 0x75, 0x5d, 0x0a, 0x44, 0xb1, 0x8a, 0xdd, 0x05, 0x10, 0x7d, 0x91, 0xd3, 0x19, 0xea, 0x34, 0x83, 0xba, 0x21, 0x05, 0xa2, 0x5c, 0xc5, 0xee, 0x01, 0x88, 0xbe, 0xc8,
0x19, 0x07, 0x32, 0xe3, 0x6f, 0xdb, 0x3b, 0x62, 0xae, 0xfd, 0x75, 0x2c, 0x4c, 0x63, 0x4e, 0x86, 0x8c, 0x03, 0x99, 0xf1, 0xb7, 0xed, 0x1d, 0x31, 0xd7, 0xfe, 0x26, 0x16, 0x26, 0x09, 0x27, 0x43,
0xdc, 0xf8, 0xd2, 0xfd, 0x18, 0x1a, 0x46, 0x15, 0x3b, 0x0f, 0x6b, 0x3b, 0x0f, 0x1f, 0x1e, 0xec, 0x6e, 0x7c, 0xe9, 0x7e, 0x0c, 0x4d, 0xa3, 0x8a, 0x5d, 0x84, 0x95, 0xad, 0x47, 0x8f, 0xf6, 0x77,
0x7a, 0xb7, 0x1e, 0xdd, 0xff, 0xf2, 0x6e, 0x6f, 0x67, 0xff, 0xe1, 0xe1, 0x6e, 0xfb, 0x1c, 0xc2, 0xbc, 0x3b, 0x8f, 0x1f, 0x7c, 0x79, 0xa7, 0xb7, 0xb5, 0xf7, 0xe8, 0x60, 0xa7, 0x7d, 0x01, 0xe1,
0xfb, 0x0f, 0x77, 0x6e, 0xed, 0xf7, 0xee, 0x3e, 0xf4, 0x76, 0x14, 0xec, 0xa0, 0x8d, 0xf7, 0x76, 0xbd, 0x47, 0x5b, 0x77, 0xf6, 0x7a, 0xf7, 0x1e, 0x79, 0x5b, 0x0a, 0x76, 0xd0, 0xc6, 0x7b, 0x3b,
0x3f, 0x7a, 0xf8, 0x68, 0xd7, 0xc2, 0x2b, 0xac, 0x0d, 0xcd, 0xdb, 0xde, 0xee, 0xad, 0x9d, 0x3d, 0x1f, 0x3d, 0x7a, 0xbc, 0x63, 0xe1, 0x35, 0xd6, 0x86, 0xd6, 0x5d, 0x6f, 0xe7, 0xce, 0xd6, 0xae,
0x89, 0x54, 0xd9, 0x06, 0xb4, 0xef, 0x3e, 0x7e, 0x70, 0xe7, 0xfe, 0x83, 0x7b, 0xbd, 0x9d, 0x5b, 0x44, 0x66, 0xd8, 0x1a, 0xb4, 0xef, 0x3d, 0x79, 0xb8, 0xfd, 0xe0, 0xe1, 0xfd, 0xde, 0xd6, 0x9d,
0x0f, 0x76, 0x76, 0xf7, 0x77, 0xef, 0xb4, 0x6b, 0xee, 0xdf, 0x39, 0x70, 0x9e, 0x46, 0x39, 0xc8, 0x87, 0x5b, 0x3b, 0x7b, 0x3b, 0xdb, 0xed, 0xba, 0xfb, 0x37, 0x0e, 0x5c, 0xa4, 0x51, 0x0e, 0x8a,
0x0b, 0xc4, 0x65, 0x68, 0xf4, 0xa3, 0x68, 0xc2, 0x51, 0x7f, 0x6b, 0x15, 0x6d, 0x42, 0xc8, 0xec, 0x02, 0x71, 0x15, 0x9a, 0xfd, 0x28, 0x8a, 0x39, 0xea, 0x6f, 0xad, 0xa2, 0x4d, 0x08, 0x99, 0x5d,
0x42, 0x21, 0x1e, 0x47, 0x71, 0x9f, 0x4b, 0x79, 0x00, 0x82, 0xee, 0x22, 0x82, 0xcc, 0x2e, 0xb7, 0x28, 0xc4, 0xa3, 0x28, 0xe9, 0x73, 0x29, 0x0f, 0x40, 0xd0, 0x3d, 0x44, 0x90, 0xd9, 0xe5, 0x76,
0x53, 0x50, 0x08, 0x71, 0x68, 0x08, 0x4c, 0x90, 0x6c, 0xc2, 0xe2, 0x51, 0xcc, 0xfd, 0xfe, 0x89, 0x0a, 0x0a, 0x21, 0x0e, 0x4d, 0x81, 0x09, 0x92, 0x75, 0x98, 0x3b, 0x4c, 0xb8, 0xdf, 0x3f, 0x96,
0x94, 0x04, 0x59, 0x62, 0x9f, 0xcc, 0x1c, 0xf2, 0x3e, 0xae, 0xf6, 0x88, 0x0f, 0x88, 0x43, 0x96, 0x92, 0x20, 0x4b, 0xec, 0x93, 0xb9, 0x43, 0xde, 0xc7, 0xd5, 0x1e, 0xf1, 0x01, 0x71, 0xc8, 0x82,
0xbd, 0x55, 0x89, 0xef, 0x48, 0xd8, 0x3d, 0x80, 0xcd, 0xfc, 0x0c, 0xa4, 0xc4, 0xbc, 0x67, 0x48, 0xb7, 0x2c, 0xf1, 0x2d, 0x09, 0xbb, 0xfb, 0xb0, 0x5e, 0x9c, 0x81, 0x94, 0x98, 0xf7, 0x0c, 0x89,
0x8c, 0xf0, 0x8d, 0xbb, 0xf3, 0xf7, 0xc7, 0x90, 0x9e, 0xff, 0x70, 0xa0, 0x86, 0xe6, 0x73, 0xbe, 0x11, 0xbe, 0x71, 0xf7, 0xec, 0xfd, 0x31, 0xa4, 0xe7, 0xdf, 0x1d, 0xa8, 0xa3, 0xf9, 0x3c, 0xdb,
0xa9, 0x35, 0x3d, 0xa2, 0xaa, 0xe5, 0x11, 0x51, 0xf0, 0x00, 0xcf, 0x14, 0x42, 0xa1, 0x0a, 0xa3, 0xd4, 0x9a, 0x1e, 0xd1, 0x8c, 0xe5, 0x11, 0x51, 0xf0, 0x00, 0xcf, 0x14, 0x42, 0xa1, 0x0a, 0xa3,
0x63, 0x20, 0x59, 0x7d, 0xcc, 0xfb, 0xa7, 0x34, 0x27, 0x5d, 0x8f, 0x08, 0xb2, 0x3c, 0x3a, 0x9e, 0x63, 0x20, 0x79, 0x7d, 0xc2, 0xfb, 0x27, 0x34, 0x27, 0x5d, 0x8f, 0x08, 0xb2, 0x3c, 0x3a, 0x9e,
0xf4, 0xb5, 0x64, 0x79, 0x55, 0x56, 0x75, 0xf4, 0xe5, 0x52, 0x56, 0x47, 0xdf, 0x75, 0x60, 0x29, 0xf4, 0xb5, 0x64, 0x79, 0x55, 0x56, 0x75, 0xf4, 0xe5, 0x7c, 0x5e, 0x47, 0xdf, 0x75, 0x60, 0x3e,
0x08, 0x8f, 0xa2, 0x69, 0x38, 0x20, 0x16, 0x5f, 0xf6, 0x54, 0x11, 0x55, 0xe5, 0x84, 0x44, 0x2f, 0x08, 0x0f, 0xa3, 0x49, 0x38, 0x20, 0x16, 0x5f, 0xf0, 0x54, 0x11, 0x55, 0x65, 0x4c, 0xa2, 0x17,
0x18, 0x2b, 0x86, 0xce, 0x00, 0x97, 0xe1, 0xc1, 0x24, 0x21, 0x77, 0x41, 0x7b, 0x81, 0xef, 0xc1, 0x8c, 0x15, 0x43, 0xe7, 0x80, 0xcb, 0xf0, 0x60, 0x92, 0x92, 0xbb, 0xa0, 0xbd, 0xc0, 0xf7, 0x60,
0x9a, 0x81, 0xc9, 0xd5, 0x7c, 0x0b, 0x16, 0x26, 0x08, 0xc8, 0xa5, 0x54, 0xca, 0x99, 0xfc, 0x0c, 0xc5, 0xc0, 0xe4, 0x6a, 0xbe, 0x05, 0xb3, 0x31, 0x02, 0x72, 0x29, 0x95, 0x72, 0x26, 0x3f, 0x43,
0x51, 0xe3, 0xb6, 0xa1, 0x75, 0x8f, 0xa7, 0xf7, 0xc3, 0xe3, 0x48, 0xb5, 0xf4, 0xc3, 0x2a, 0xac, 0xd4, 0xb8, 0x6d, 0x58, 0xba, 0xcf, 0xb3, 0x07, 0xe1, 0x51, 0xa4, 0x5a, 0xfa, 0xe1, 0x0c, 0x2c,
0x6a, 0x48, 0x36, 0x74, 0x0d, 0x56, 0x83, 0x01, 0x0f, 0xd3, 0x20, 0x9d, 0xf5, 0xac, 0xf3, 0x4f, 0x6b, 0x48, 0x36, 0x74, 0x03, 0x96, 0x83, 0x01, 0x0f, 0xb3, 0x20, 0x9b, 0xf6, 0xac, 0xf3, 0x4f,
0x1e, 0x46, 0xff, 0xcc, 0x1f, 0x05, 0x7e, 0x22, 0x3d, 0x00, 0x51, 0x60, 0xdb, 0xb0, 0x81, 0xc6, 0x11, 0x46, 0xff, 0xcc, 0x1f, 0x05, 0x7e, 0x2a, 0x3d, 0x00, 0x51, 0x60, 0x9b, 0xb0, 0x86, 0xc6,
0x43, 0xd9, 0x03, 0xbd, 0xc5, 0xe2, 0x18, 0x56, 0x5a, 0x87, 0xe2, 0x8d, 0xb8, 0xd4, 0xdf, 0xfa, 0x43, 0xd9, 0x03, 0xbd, 0xc5, 0xe2, 0x18, 0x56, 0x59, 0x87, 0xe2, 0x8d, 0xb8, 0xd4, 0xdf, 0xfa,
0x13, 0xe1, 0xa7, 0x94, 0x55, 0xe1, 0xaa, 0x89, 0x96, 0x70, 0xca, 0x0b, 0xc2, 0xc0, 0x68, 0xa0, 0x13, 0xe1, 0xa7, 0x54, 0x55, 0xe1, 0xaa, 0x89, 0x96, 0x70, 0xca, 0xb3, 0xc2, 0xc0, 0x68, 0xa0,
0x10, 0x02, 0x5a, 0x14, 0xca, 0x27, 0x1f, 0x02, 0x32, 0xc2, 0x48, 0xcb, 0x85, 0x30, 0x12, 0x2a, 0x14, 0x02, 0x9a, 0x13, 0xca, 0xa7, 0x18, 0x02, 0x32, 0xc2, 0x48, 0x0b, 0xa5, 0x30, 0x12, 0x2a,
0xa7, 0x59, 0xd8, 0xe7, 0x83, 0x5e, 0x1a, 0xf5, 0x48, 0x89, 0xd2, 0xee, 0x2c, 0x7b, 0x79, 0x98, 0xa7, 0x69, 0xd8, 0xe7, 0x83, 0x5e, 0x16, 0xf5, 0x48, 0x89, 0xd2, 0xee, 0x2c, 0x78, 0x45, 0x98,
0x02, 0x5e, 0x3c, 0x49, 0x43, 0x9e, 0x92, 0x9e, 0x59, 0xf6, 0x54, 0x11, 0xe5, 0x87, 0x48, 0x84, 0x02, 0x5e, 0x3c, 0xcd, 0x42, 0x9e, 0x91, 0x9e, 0x59, 0xf0, 0x54, 0x11, 0xe5, 0x87, 0x48, 0x84,
0x49, 0xa8, 0x7b, 0xb2, 0x84, 0x8e, 0xe6, 0x34, 0x0e, 0x92, 0x4e, 0x93, 0x50, 0xfa, 0xcd, 0x3e, 0x49, 0x68, 0x78, 0xb2, 0x84, 0x8e, 0xe6, 0x24, 0x09, 0xd2, 0x4e, 0x8b, 0x50, 0xfa, 0xcd, 0x3e,
0x0d, 0xe7, 0x8f, 0x78, 0x92, 0xf6, 0x4e, 0xb8, 0x3f, 0xe0, 0x31, 0xed, 0xbe, 0x88, 0x4e, 0x09, 0x0d, 0x17, 0x0f, 0x79, 0x9a, 0xf5, 0x8e, 0xb9, 0x3f, 0xe0, 0x09, 0xed, 0xbe, 0x88, 0x4e, 0x09,
0xfb, 0x5d, 0x5e, 0x89, 0x7d, 0x9f, 0xf2, 0x38, 0x09, 0xa2, 0x90, 0x2c, 0x77, 0xdd, 0x53, 0x45, 0xfb, 0x5d, 0x5d, 0x89, 0x7d, 0x9f, 0xf0, 0x24, 0x0d, 0xa2, 0x90, 0x2c, 0x77, 0xc3, 0x53, 0x45,
0xf7, 0x9b, 0xe4, 0x0f, 0xeb, 0xb8, 0xd9, 0x63, 0x32, 0xe6, 0xec, 0x35, 0xa8, 0x8b, 0x39, 0x26, 0xf7, 0x9b, 0xe4, 0x0f, 0xeb, 0xb8, 0xd9, 0x13, 0x32, 0xe6, 0xec, 0x35, 0x68, 0x88, 0x39, 0xa6,
0x27, 0xbe, 0x74, 0xd1, 0x97, 0x09, 0x38, 0x3c, 0xf1, 0x51, 0x23, 0x58, 0xcb, 0x26, 0x02, 0x91, 0xc7, 0xbe, 0x74, 0xd1, 0x17, 0x08, 0x38, 0x38, 0xf6, 0x51, 0x23, 0x58, 0xcb, 0x26, 0x02, 0x91,
0x0d, 0xc2, 0xf6, 0xc4, 0xaa, 0x5d, 0x81, 0x96, 0x8a, 0xc8, 0x25, 0xbd, 0x11, 0x3f, 0x4e, 0xd5, 0x4d, 0xc2, 0x76, 0xc5, 0xaa, 0x5d, 0x83, 0x25, 0x15, 0x91, 0x4b, 0x7b, 0x23, 0x7e, 0x94, 0xa9,
0xf1, 0x3a, 0x9c, 0x8e, 0xb1, 0xbb, 0x64, 0x9f, 0x1f, 0xa7, 0xee, 0x03, 0x58, 0x93, 0x32, 0xfc, 0xe3, 0x75, 0x38, 0x19, 0x63, 0x77, 0xe9, 0x1e, 0x3f, 0xca, 0xdc, 0x87, 0xb0, 0x22, 0x65, 0xf8,
0x70, 0xc2, 0x55, 0xd7, 0x9f, 0x2d, 0xb3, 0x6e, 0x8d, 0xed, 0x75, 0x5b, 0xe8, 0x29, 0x46, 0x90, 0x51, 0xcc, 0x55, 0xd7, 0x9f, 0xad, 0xb2, 0x6e, 0xcd, 0xcd, 0x55, 0x5b, 0xe8, 0x29, 0x46, 0x50,
0x33, 0x79, 0xae, 0x07, 0xcc, 0xd4, 0x09, 0xb2, 0x41, 0x69, 0x62, 0xd4, 0x21, 0x5e, 0x4e, 0xc7, 0x30, 0x79, 0xae, 0x07, 0xcc, 0xd4, 0x09, 0xb2, 0x41, 0x69, 0x62, 0xd4, 0x21, 0x5e, 0x4e, 0xc7,
0xc2, 0x70, 0x7d, 0x92, 0x69, 0xbf, 0x8f, 0x9a, 0x40, 0x68, 0x40, 0x55, 0x74, 0xbf, 0xe7, 0xc0, 0xc2, 0x70, 0x7d, 0xd2, 0x49, 0xbf, 0x8f, 0x9a, 0x40, 0x68, 0x40, 0x55, 0x74, 0xbf, 0xe7, 0xc0,
0x3a, 0xb5, 0xa6, 0xec, 0xb3, 0x3e, 0xf9, 0xbd, 0xfa, 0x30, 0x9b, 0x7d, 0x33, 0xb0, 0xb1, 0x01, 0x2a, 0xb5, 0xa6, 0xec, 0xb3, 0x3e, 0xf9, 0xbd, 0xfa, 0x30, 0x5b, 0x7d, 0x33, 0xb0, 0xb1, 0x06,
0x0b, 0xa6, 0xae, 0x15, 0x85, 0x1f, 0xff, 0x2c, 0x5b, 0x2b, 0x9c, 0x65, 0x7f, 0xe8, 0xc0, 0x9a, 0xb3, 0xa6, 0xae, 0x15, 0x85, 0x1f, 0xfd, 0x2c, 0x5b, 0x2f, 0x9d, 0x65, 0x7f, 0xe8, 0xc0, 0x8a,
0x50, 0x86, 0xa9, 0x9f, 0x4e, 0x13, 0x39, 0xfd, 0x5f, 0x84, 0x15, 0x61, 0xa7, 0xa4, 0x38, 0xc9, 0x50, 0x86, 0x99, 0x9f, 0x4d, 0x52, 0x39, 0xfd, 0x9f, 0x87, 0x45, 0x61, 0xa7, 0xa4, 0x38, 0xc9,
0x81, 0x6e, 0x68, 0xc9, 0x27, 0x54, 0x10, 0xef, 0x9d, 0xf3, 0x6c, 0x62, 0xf6, 0x79, 0x68, 0x9a, 0x81, 0xae, 0x69, 0xc9, 0x27, 0x54, 0x10, 0xef, 0x5e, 0xf0, 0x6c, 0x62, 0xf6, 0x79, 0x68, 0x99,
0x61, 0x55, 0x1a, 0x73, 0x63, 0xfb, 0xa2, 0x9a, 0x65, 0x81, 0x73, 0xf6, 0xce, 0x79, 0xd6, 0x07, 0x61, 0x55, 0x1a, 0x73, 0x73, 0xf3, 0xb2, 0x9a, 0x65, 0x89, 0x73, 0x76, 0x2f, 0x78, 0xd6, 0x07,
0xec, 0x43, 0x72, 0x36, 0xc2, 0x1e, 0x35, 0x2b, 0x03, 0x53, 0x17, 0x4b, 0x14, 0xb8, 0xfe, 0xdc, 0xec, 0x43, 0x72, 0x36, 0xc2, 0x1e, 0x35, 0x2b, 0x03, 0x53, 0x97, 0x2b, 0x14, 0xb8, 0xfe, 0xdc,
0x20, 0xbf, 0xbd, 0x0c, 0x8b, 0xc2, 0xbb, 0x74, 0xef, 0xc1, 0x8a, 0x35, 0x52, 0xeb, 0x8c, 0xde, 0x20, 0xbf, 0xbb, 0x00, 0x73, 0xc2, 0xbb, 0x74, 0xef, 0xc3, 0xa2, 0x35, 0x52, 0xeb, 0x8c, 0xde,
0x14, 0x67, 0xf4, 0x42, 0x48, 0xa7, 0x52, 0x0c, 0xe9, 0xb8, 0xff, 0x56, 0x01, 0x86, 0xdc, 0x96, 0x12, 0x67, 0xf4, 0x52, 0x48, 0xa7, 0x56, 0x0e, 0xe9, 0xb8, 0xff, 0x5a, 0x03, 0x86, 0xdc, 0x56,
0xdb, 0x4e, 0x74, 0x6f, 0xa3, 0x81, 0x75, 0x58, 0x69, 0x7a, 0x26, 0xc4, 0xae, 0x03, 0x33, 0x8a, 0xd8, 0x4e, 0x74, 0x6f, 0xa3, 0x81, 0x75, 0x58, 0x69, 0x79, 0x26, 0xc4, 0x6e, 0x02, 0x33, 0x8a,
0x2a, 0xea, 0x25, 0xec, 0x46, 0x49, 0x0d, 0x2a, 0x38, 0x69, 0x58, 0xa5, 0x09, 0x94, 0xc7, 0x32, 0x2a, 0xea, 0x25, 0xec, 0x46, 0x45, 0x0d, 0x2a, 0x38, 0x69, 0x58, 0xa5, 0x09, 0x94, 0xc7, 0x32,
0xb1, 0x6f, 0xa5, 0x75, 0x68, 0x1a, 0x26, 0xd3, 0xe4, 0x04, 0xdd, 0x6f, 0x75, 0x9c, 0x51, 0xe5, 0xb1, 0x6f, 0x95, 0x75, 0x68, 0x1a, 0xe2, 0x49, 0x7a, 0x8c, 0xee, 0xb7, 0x3a, 0xce, 0xa8, 0x72,
0x3c, 0x83, 0x2c, 0xbe, 0x94, 0x41, 0x96, 0xf2, 0x0c, 0x62, 0x3a, 0xd4, 0xcb, 0x96, 0x43, 0x8d, 0x91, 0x41, 0xe6, 0xce, 0x65, 0x90, 0xf9, 0x22, 0x83, 0x98, 0x0e, 0xf5, 0x82, 0xe5, 0x50, 0xa3,
0x8e, 0xdc, 0x18, 0xdd, 0xbf, 0x74, 0xd4, 0xef, 0x8d, 0xb1, 0x77, 0x79, 0x7a, 0xb1, 0x40, 0xb6, 0x23, 0x37, 0x46, 0xf7, 0x2f, 0x1b, 0xf5, 0x7b, 0x63, 0xec, 0x5d, 0x9e, 0x5e, 0x2c, 0x90, 0x6d,
0x05, 0x6d, 0xe9, 0x0a, 0x64, 0x5e, 0x3b, 0xd0, 0x1a, 0x17, 0x70, 0xf7, 0x07, 0x0e, 0xb4, 0x71, 0x40, 0x5b, 0xba, 0x02, 0xb9, 0xd7, 0x0e, 0xb4, 0xc6, 0x25, 0xdc, 0xfd, 0x81, 0x03, 0x6d, 0x5c,
0x9d, 0x2d, 0x5e, 0xfc, 0x00, 0x48, 0x14, 0x5e, 0x91, 0x15, 0x2d, 0xda, 0x9f, 0x9e, 0x13, 0xdf, 0x67, 0x8b, 0x17, 0x3f, 0x00, 0x12, 0x85, 0x57, 0x64, 0x45, 0x8b, 0xf6, 0x27, 0xe7, 0xc4, 0xf7,
0x87, 0x3a, 0x35, 0x18, 0x4d, 0x78, 0x28, 0x19, 0xb1, 0x63, 0x33, 0x62, 0xa6, 0x85, 0xf6, 0xce, 0xa1, 0x41, 0x0d, 0x46, 0x31, 0x0f, 0x25, 0x23, 0x76, 0x6c, 0x46, 0xcc, 0xb5, 0xd0, 0xee, 0x05,
0x79, 0x19, 0xb1, 0xc1, 0x86, 0xff, 0xe4, 0x40, 0x43, 0x0e, 0xf3, 0x27, 0x3e, 0x89, 0x77, 0x61, 0x2f, 0x27, 0x36, 0xd8, 0xf0, 0x1f, 0x1d, 0x68, 0xca, 0x61, 0xfe, 0xd8, 0x27, 0xf1, 0x2e, 0x2c,
0x19, 0x39, 0xd2, 0x38, 0xee, 0xea, 0x32, 0x5a, 0x93, 0xb1, 0x9f, 0x4e, 0x63, 0x34, 0x9f, 0xd6, 0x20, 0x47, 0x1a, 0xc7, 0x5d, 0x5d, 0x46, 0x6b, 0x32, 0xf6, 0xb3, 0x49, 0x82, 0xe6, 0xd3, 0x3a,
0x29, 0x3c, 0x0f, 0xa3, 0x2d, 0x24, 0x85, 0x9b, 0xf4, 0xd2, 0x60, 0xd4, 0x53, 0xb5, 0xf2, 0x16, 0x85, 0x17, 0x61, 0xb4, 0x85, 0xa4, 0x70, 0xd3, 0x5e, 0x16, 0x8c, 0x7a, 0xaa, 0x56, 0xde, 0x62,
0xa3, 0xac, 0x0a, 0xf5, 0x4e, 0x92, 0xfa, 0x43, 0x2e, 0xcd, 0x9c, 0x28, 0xb8, 0x1d, 0xd8, 0x94, 0x54, 0x55, 0xa1, 0xde, 0x49, 0x33, 0x7f, 0xc8, 0xa5, 0x99, 0x13, 0x05, 0xb7, 0x03, 0xeb, 0x72,
0x13, 0xca, 0xf9, 0x8e, 0xee, 0xdf, 0x36, 0xe1, 0x42, 0xa1, 0x4a, 0x5f, 0x03, 0xca, 0xe3, 0xe5, 0x42, 0x05, 0xdf, 0xd1, 0xfd, 0xeb, 0x16, 0x5c, 0x2a, 0x55, 0xe9, 0x6b, 0x40, 0x79, 0xbc, 0x1c,
0x28, 0x18, 0x1f, 0x45, 0xda, 0xd1, 0x76, 0xcc, 0x93, 0xa7, 0x55, 0xc5, 0x86, 0x70, 0x5e, 0xd9, 0x05, 0xe3, 0xc3, 0x48, 0x3b, 0xda, 0x8e, 0x79, 0xf2, 0xb4, 0xaa, 0xd8, 0x10, 0x2e, 0x2a, 0x7b,
0x73, 0x5c, 0xd3, 0xcc, 0x7a, 0x57, 0xc8, 0x11, 0x79, 0xd7, 0xe6, 0x81, 0x7c, 0x87, 0x0a, 0x37, 0x8e, 0x6b, 0x9a, 0x5b, 0xef, 0x1a, 0x39, 0x22, 0xef, 0xda, 0x3c, 0x50, 0xec, 0x50, 0xe1, 0xa6,
0x25, 0xb7, 0xbc, 0x3d, 0x76, 0x02, 0x1d, 0xed, 0x38, 0x48, 0x15, 0x6f, 0x38, 0x17, 0xd8, 0xd7, 0xe4, 0x56, 0xb7, 0xc7, 0x8e, 0xa1, 0xa3, 0x1d, 0x07, 0xa9, 0xe2, 0x0d, 0xe7, 0x02, 0xfb, 0x7a,
0x3b, 0x2f, 0xe9, 0xcb, 0x72, 0x44, 0xbd, 0xb9, 0xad, 0xb1, 0x19, 0x5c, 0x52, 0x75, 0xa4, 0xc3, 0xe7, 0x9c, 0xbe, 0x2c, 0x47, 0xd4, 0x3b, 0xb3, 0x35, 0x36, 0x85, 0x2b, 0xaa, 0x8e, 0x74, 0x78,
0x8b, 0xfd, 0xd5, 0x5e, 0x69, 0x6e, 0xe4, 0x44, 0xdb, 0x9d, 0xbe, 0xa4, 0x61, 0xf6, 0x75, 0xd8, 0xb9, 0xbf, 0xfa, 0x2b, 0xcd, 0x8d, 0x9c, 0x68, 0xbb, 0xd3, 0x73, 0x1a, 0x66, 0x5f, 0x87, 0xf5,
0x3c, 0xf3, 0x83, 0x54, 0x0d, 0xcb, 0x70, 0x86, 0x16, 0xa8, 0xcb, 0xed, 0x97, 0x74, 0xf9, 0x44, 0x53, 0x3f, 0xc8, 0xd4, 0xb0, 0x0c, 0x67, 0x68, 0x96, 0xba, 0xdc, 0x3c, 0xa7, 0xcb, 0xa7, 0xe2,
0x7c, 0x6c, 0x19, 0xb6, 0x39, 0x2d, 0x76, 0xff, 0xc1, 0x81, 0x96, 0xdd, 0x0e, 0xb2, 0xa9, 0x14, 0x63, 0xcb, 0xb0, 0x9d, 0xd1, 0x62, 0xf7, 0xef, 0x1d, 0x58, 0xb2, 0xdb, 0x41, 0x36, 0x95, 0x02,
0x78, 0xa5, 0xf8, 0x94, 0xf3, 0x97, 0x83, 0x8b, 0x67, 0xd5, 0x4a, 0xd9, 0x59, 0xd5, 0x3c, 0x21, 0xaf, 0x14, 0x9f, 0x72, 0xfe, 0x0a, 0x70, 0xf9, 0xac, 0x5a, 0xab, 0x3a, 0xab, 0x9a, 0x27, 0xc4,
0x56, 0x5f, 0x16, 0xc6, 0xa9, 0xbd, 0x5a, 0x18, 0x67, 0xa1, 0x2c, 0x8c, 0xd3, 0xfd, 0x6f, 0x07, 0x99, 0xf3, 0xc2, 0x38, 0xf5, 0x57, 0x0b, 0xe3, 0xcc, 0x56, 0x85, 0x71, 0xba, 0xff, 0xed, 0x00,
0x58, 0x91, 0x97, 0xd8, 0x3d, 0x71, 0x58, 0x0e, 0xf9, 0x48, 0xea, 0xa4, 0x9f, 0x7f, 0x35, 0x7e, 0x2b, 0xf3, 0x12, 0xbb, 0x2f, 0x0e, 0xcb, 0x21, 0x1f, 0x49, 0x9d, 0xf4, 0xb3, 0xaf, 0xc6, 0x8f,
0x54, 0x6b, 0xa7, 0xbe, 0x46, 0xc1, 0x30, 0x95, 0x8e, 0xe9, 0x22, 0xad, 0x78, 0x65, 0x55, 0xb9, 0x6a, 0xed, 0xd4, 0xd7, 0x28, 0x18, 0xa6, 0xd2, 0x31, 0x5d, 0xa4, 0x45, 0xaf, 0xaa, 0xaa, 0x10,
0xc0, 0x52, 0xed, 0xe5, 0x81, 0xa5, 0x85, 0x97, 0x07, 0x96, 0x16, 0xf3, 0x81, 0xa5, 0xee, 0xef, 0x58, 0xaa, 0x9f, 0x1f, 0x58, 0x9a, 0x3d, 0x3f, 0xb0, 0x34, 0x57, 0x0c, 0x2c, 0x75, 0x7f, 0xcb,
0x38, 0xb0, 0x5e, 0xb2, 0xe9, 0x3f, 0xbb, 0x89, 0xe3, 0x36, 0x59, 0xba, 0xa0, 0x22, 0xb7, 0xc9, 0x81, 0xd5, 0x8a, 0x4d, 0xff, 0xe9, 0x4d, 0x1c, 0xb7, 0xc9, 0xd2, 0x05, 0x35, 0xb9, 0x4d, 0x26,
0x04, 0xbb, 0xbf, 0x01, 0x2b, 0x16, 0xa3, 0xff, 0xec, 0xfa, 0xcf, 0x7b, 0x79, 0x82, 0xcf, 0x2c, 0xd8, 0xfd, 0x35, 0x58, 0xb4, 0x18, 0xfd, 0xa7, 0xd7, 0x7f, 0xd1, 0xcb, 0x13, 0x7c, 0x66, 0x61,
0xac, 0xfb, 0xa3, 0x0a, 0xb0, 0xa2, 0xb0, 0xfd, 0xbf, 0x8e, 0xa1, 0xb8, 0x4e, 0xd5, 0x92, 0x75, 0xdd, 0xff, 0xa8, 0x01, 0x2b, 0x0b, 0xdb, 0xff, 0xeb, 0x18, 0xca, 0xeb, 0x34, 0x53, 0xb1, 0x4e,
0xfa, 0x3f, 0xb5, 0x03, 0xef, 0xc0, 0x9a, 0xcc, 0x19, 0x30, 0x42, 0x24, 0x82, 0x63, 0x8a, 0x15, 0xff, 0xa7, 0x76, 0xe0, 0x1d, 0x58, 0x91, 0x39, 0x03, 0x46, 0x88, 0x44, 0x70, 0x4c, 0xb9, 0x02,
0xe8, 0xe7, 0xda, 0x51, 0xbd, 0x65, 0xeb, 0xae, 0xd9, 0x30, 0x86, 0xb9, 0xe0, 0x9e, 0xbb, 0x09, 0xfd, 0x5c, 0x3b, 0xaa, 0xb7, 0x60, 0xdd, 0x35, 0x1b, 0xc6, 0xb0, 0x10, 0xdc, 0x73, 0xd7, 0x61,
0x1b, 0x22, 0x07, 0xe1, 0xb6, 0x68, 0x4a, 0xd9, 0x95, 0x3f, 0x71, 0xe0, 0x7c, 0xae, 0x22, 0xbb, 0x4d, 0xe4, 0x20, 0xdc, 0x15, 0x4d, 0x29, 0xbb, 0xf2, 0x47, 0x0e, 0x5c, 0x2c, 0x54, 0xe4, 0x37,
0x19, 0x15, 0xa6, 0xc3, 0xb6, 0x27, 0x36, 0x88, 0xe3, 0x97, 0x72, 0x64, 0x8c, 0x5f, 0x70, 0x5b, 0xa3, 0xc2, 0x74, 0xd8, 0xf6, 0xc4, 0x06, 0x71, 0xfc, 0x52, 0x8e, 0x8c, 0xf1, 0x0b, 0x6e, 0x2b,
0xb1, 0x02, 0xd7, 0x67, 0x1a, 0x16, 0xe9, 0xc5, 0xaa, 0x97, 0x55, 0xb9, 0x17, 0x44, 0xa6, 0x44, 0x57, 0xe0, 0xfa, 0x4c, 0xc2, 0x32, 0xbd, 0x58, 0xf5, 0xaa, 0x2a, 0xf7, 0x92, 0xc8, 0x94, 0x08,
0xc8, 0x47, 0xb9, 0x81, 0x1f, 0x8b, 0xdc, 0x06, 0xb3, 0x22, 0xbb, 0x5a, 0xb1, 0x87, 0xac, 0x8a, 0xf9, 0xa8, 0x30, 0xf0, 0x23, 0x91, 0xdb, 0x60, 0x56, 0xe4, 0x57, 0x2b, 0xf6, 0x90, 0x55, 0x11,
0xe8, 0x05, 0x5a, 0x66, 0xca, 0x1e, 0x6f, 0x69, 0x9d, 0xfb, 0xd7, 0x0e, 0xb0, 0x2f, 0x4d, 0x79, 0xbd, 0x40, 0xcb, 0x4c, 0xd9, 0xe3, 0xad, 0xac, 0x73, 0xff, 0xd2, 0x01, 0xf6, 0xa5, 0x09, 0x4f,
0x3c, 0xa3, 0x1b, 0x52, 0x1d, 0xcb, 0xb9, 0x90, 0x8f, 0x63, 0x2c, 0x4e, 0xa6, 0x47, 0x5f, 0xe4, 0xa6, 0x74, 0x43, 0xaa, 0x63, 0x39, 0x97, 0x8a, 0x71, 0x8c, 0xb9, 0x78, 0x72, 0xf8, 0x45, 0x3e,
0x33, 0x75, 0x8f, 0x5e, 0xc9, 0xee, 0xd1, 0xdf, 0x00, 0xc0, 0xe3, 0x97, 0xbe, 0x76, 0x45, 0x5e, 0x55, 0xf7, 0xe8, 0xb5, 0xfc, 0x1e, 0xfd, 0x0d, 0x00, 0x3c, 0x7e, 0xe9, 0x6b, 0x57, 0xe4, 0x05,
0xc0, 0x73, 0xaf, 0x68, 0xb0, 0xf4, 0xaa, 0xbb, 0xf6, 0xf2, 0xab, 0xee, 0x85, 0x97, 0x5d, 0x75, 0x3c, 0xf7, 0x8a, 0x06, 0x2b, 0xaf, 0xba, 0xeb, 0xe7, 0x5f, 0x75, 0xcf, 0x9e, 0x77, 0xd5, 0xfd,
0x7f, 0x08, 0xeb, 0xd6, 0xb8, 0xf5, 0xb6, 0xaa, 0x0b, 0x60, 0xe7, 0x05, 0x17, 0xc0, 0xff, 0xe9, 0x21, 0xac, 0x5a, 0xe3, 0xd6, 0xdb, 0xaa, 0x2e, 0x80, 0x9d, 0x97, 0x5c, 0x00, 0xff, 0xa7, 0x03,
0x40, 0x75, 0x2f, 0x9a, 0x98, 0x71, 0x4b, 0xc7, 0x8e, 0x5b, 0x4a, 0x5b, 0xd2, 0xd3, 0xa6, 0x42, 0x33, 0xbb, 0x51, 0x6c, 0xc6, 0x2d, 0x1d, 0x3b, 0x6e, 0x29, 0x6d, 0x49, 0x4f, 0x9b, 0x0a, 0xa9,
0xaa, 0x18, 0x0b, 0x64, 0x5b, 0xd0, 0xf2, 0xc7, 0x29, 0x1e, 0xbb, 0x8f, 0xa3, 0xf8, 0xcc, 0x8f, 0x62, 0x2c, 0x90, 0x6d, 0xc0, 0x92, 0x3f, 0xce, 0xf0, 0xd8, 0x7d, 0x14, 0x25, 0xa7, 0x7e, 0x32,
0x07, 0x62, 0xaf, 0x6f, 0x57, 0x3a, 0x8e, 0x97, 0xab, 0x61, 0x1b, 0x50, 0xd5, 0x4a, 0x97, 0x08, 0x10, 0x7b, 0x7d, 0xb7, 0xd6, 0x71, 0xbc, 0x42, 0x0d, 0x5b, 0x83, 0x19, 0xad, 0x74, 0x89, 0x00,
0xb0, 0x88, 0x8e, 0x1b, 0xdd, 0x79, 0xcc, 0x64, 0xc4, 0x40, 0x96, 0x90, 0x95, 0xec, 0xef, 0x85, 0x8b, 0xe8, 0xb8, 0xd1, 0x9d, 0xc7, 0x54, 0x46, 0x0c, 0x64, 0x09, 0x59, 0xc9, 0xfe, 0x5e, 0xb8,
0xab, 0x2c, 0x44, 0xa7, 0xac, 0x0a, 0xed, 0x1a, 0x2e, 0x1f, 0x91, 0xc9, 0x50, 0x8f, 0x2a, 0xbb, 0xca, 0x42, 0x74, 0xaa, 0xaa, 0xd0, 0xae, 0xe1, 0xf2, 0x11, 0x99, 0x0c, 0xf5, 0xa8, 0xb2, 0xfb,
0xff, 0xee, 0xc0, 0x02, 0xad, 0x00, 0x0a, 0xbb, 0xe0, 0x70, 0x1d, 0xa0, 0xa4, 0x99, 0xaf, 0x78, 0x6f, 0x0e, 0xcc, 0xd2, 0x0a, 0xa0, 0xb0, 0x0b, 0x0e, 0xd7, 0x01, 0x4a, 0x9a, 0xf9, 0xa2, 0x57,
0x79, 0x98, 0xb9, 0x56, 0xbe, 0x49, 0x45, 0x0f, 0xdb, 0xcc, 0x39, 0xb9, 0x0c, 0x75, 0x51, 0xd2, 0x84, 0x99, 0x6b, 0xe5, 0x9b, 0xd4, 0xf4, 0xb0, 0xcd, 0x9c, 0x93, 0xab, 0xd0, 0x10, 0x25, 0x9d,
0xb9, 0x15, 0x44, 0x92, 0x81, 0xec, 0x12, 0xd4, 0x4e, 0xa2, 0x89, 0xf2, 0x4e, 0x40, 0xc5, 0xe7, 0x5b, 0x41, 0x24, 0x39, 0xc8, 0xae, 0x40, 0xfd, 0x38, 0x8a, 0x95, 0x77, 0x02, 0x2a, 0x3e, 0x1f,
0xa3, 0x89, 0x47, 0x78, 0x36, 0x1e, 0x6c, 0x4f, 0x0c, 0x5e, 0xd8, 0x9c, 0x3c, 0x8c, 0x56, 0x57, 0xc5, 0x1e, 0xe1, 0xf9, 0x78, 0xb0, 0x3d, 0x31, 0x78, 0x61, 0x73, 0x8a, 0x30, 0x5a, 0x5d, 0xdd,
0x37, 0x6b, 0x2e, 0x46, 0x0e, 0x75, 0xb7, 0x60, 0xf5, 0x41, 0x34, 0xe0, 0x46, 0x4c, 0x69, 0x2e, 0xac, 0xb9, 0x18, 0x05, 0xd4, 0xdd, 0x80, 0xe5, 0x87, 0xd1, 0x80, 0x1b, 0x31, 0xa5, 0x33, 0xb9,
0x37, 0xbb, 0xdf, 0x72, 0x60, 0x59, 0x11, 0xb3, 0x6b, 0x50, 0x43, 0x57, 0x22, 0x77, 0x50, 0xd0, 0xd9, 0xfd, 0x96, 0x03, 0x0b, 0x8a, 0x98, 0xdd, 0x80, 0x3a, 0xba, 0x12, 0x85, 0x83, 0x82, 0xbe,
0xf7, 0x72, 0x48, 0xe7, 0x11, 0x05, 0xea, 0x5e, 0x8a, 0x38, 0x64, 0x6e, 0xa5, 0x8a, 0x37, 0x64, 0x97, 0x43, 0x3a, 0x8f, 0x28, 0x50, 0xf7, 0x52, 0xc4, 0x21, 0x77, 0x2b, 0x55, 0xbc, 0x21, 0xf7,
0x5e, 0x93, 0x1e, 0x6e, 0xce, 0xd9, 0xc8, 0xa1, 0xee, 0x5f, 0x38, 0xb0, 0x62, 0xf5, 0x81, 0xc7, 0x9a, 0xf4, 0x70, 0x0b, 0xce, 0x46, 0x01, 0x75, 0xff, 0xcc, 0x81, 0x45, 0xab, 0x0f, 0x3c, 0x1e,
0xc3, 0x91, 0x9f, 0xa4, 0xf2, 0xae, 0x43, 0x6e, 0x8f, 0x09, 0x99, 0x51, 0xc6, 0x8a, 0x1d, 0x65, 0x8e, 0xfc, 0x34, 0x93, 0x77, 0x1d, 0x72, 0x7b, 0x4c, 0xc8, 0x8c, 0x32, 0xd6, 0xec, 0x28, 0xa3,
0xd4, 0xf1, 0xaf, 0xaa, 0x19, 0xff, 0xba, 0x09, 0xf5, 0x2c, 0x2b, 0xa8, 0x66, 0xe9, 0x54, 0xec, 0x8e, 0x7f, 0xcd, 0x98, 0xf1, 0xaf, 0xdb, 0xd0, 0xc8, 0xb3, 0x82, 0xea, 0x96, 0x4e, 0xc5, 0x1e,
0x51, 0xdd, 0x38, 0x66, 0x44, 0xd8, 0x4e, 0x3f, 0x1a, 0x45, 0xb1, 0x0c, 0xb2, 0x8b, 0x82, 0xfb, 0xd5, 0x8d, 0x63, 0x4e, 0x84, 0xed, 0xf4, 0xa3, 0x51, 0x94, 0xc8, 0x20, 0xbb, 0x28, 0xb8, 0x1f,
0x21, 0x34, 0x0c, 0x7a, 0x1c, 0x46, 0xc8, 0xd3, 0xb3, 0x28, 0x7e, 0xaa, 0x82, 0x9d, 0xb2, 0xa8, 0x42, 0xd3, 0xa0, 0xc7, 0x61, 0x84, 0x3c, 0x3b, 0x8d, 0x92, 0x67, 0x2a, 0xd8, 0x29, 0x8b, 0xfa,
0x2f, 0xd6, 0x2b, 0xd9, 0xc5, 0xba, 0xfb, 0x97, 0x0e, 0xac, 0x20, 0x0f, 0x06, 0xe1, 0xf0, 0x20, 0x62, 0xbd, 0x96, 0x5f, 0xac, 0xbb, 0x7f, 0xee, 0xc0, 0x22, 0xf2, 0x60, 0x10, 0x0e, 0xf7, 0xa3,
0x1a, 0x05, 0xfd, 0x19, 0xed, 0xbd, 0x62, 0x37, 0xa9, 0x19, 0x14, 0x2f, 0xda, 0x30, 0xf2, 0xb6, 0x51, 0xd0, 0x9f, 0xd2, 0xde, 0x2b, 0x76, 0x93, 0x9a, 0x41, 0xf1, 0xa2, 0x0d, 0x23, 0x6f, 0xab,
0x3a, 0x1d, 0x4a, 0x41, 0xd4, 0x65, 0x94, 0x54, 0xe4, 0xf3, 0x23, 0x3f, 0x91, 0xcc, 0x2f, 0x8d, 0xd3, 0xa1, 0x14, 0x44, 0x5d, 0x46, 0x49, 0x45, 0x3e, 0x3f, 0xf4, 0x53, 0xc9, 0xfc, 0xd2, 0xc8,
0x9c, 0x05, 0xa2, 0x3c, 0x21, 0x10, 0xfb, 0x29, 0xef, 0x8d, 0x83, 0xd1, 0x28, 0x10, 0xb4, 0xc2, 0x59, 0x20, 0xca, 0x13, 0x02, 0x89, 0x9f, 0xf1, 0xde, 0x38, 0x18, 0x8d, 0x02, 0x41, 0x2b, 0x5c,
0x05, 0x2a, 0xab, 0x72, 0xbf, 0x5f, 0x81, 0x86, 0x54, 0xc1, 0xbb, 0x83, 0x21, 0x97, 0x37, 0x19, 0xa0, 0xaa, 0x2a, 0xf7, 0xfb, 0x35, 0x68, 0x4a, 0x15, 0xbc, 0x33, 0x18, 0x72, 0x79, 0x93, 0x41,
0xe4, 0x48, 0x6a, 0x75, 0x61, 0x20, 0xaa, 0xde, 0x72, 0x3d, 0x0d, 0x24, 0xbf, 0xad, 0xd5, 0xe2, 0x8e, 0xa4, 0x56, 0x17, 0x06, 0xa2, 0xea, 0x2d, 0xd7, 0xd3, 0x40, 0x8a, 0xdb, 0x3a, 0x53, 0xde,
0xb6, 0xbe, 0x0e, 0x75, 0x64, 0xaf, 0x77, 0xc9, 0xc7, 0x15, 0xb7, 0x20, 0x19, 0xa0, 0x6a, 0xb7, 0xd6, 0xd7, 0xa1, 0x81, 0xec, 0xf5, 0x2e, 0xf9, 0xb8, 0xe2, 0x16, 0x24, 0x07, 0x54, 0xed, 0x26,
0xa9, 0x76, 0x21, 0xab, 0x25, 0xe0, 0x85, 0xf7, 0x1e, 0xef, 0x43, 0x53, 0x36, 0x43, 0xeb, 0x4e, 0xd5, 0xce, 0xe6, 0xb5, 0x04, 0xbc, 0xf4, 0xde, 0xe3, 0x7d, 0x68, 0xc9, 0x66, 0x68, 0xdd, 0x49,
0xda, 0x21, 0x63, 0x70, 0x6b, 0x4f, 0x3c, 0x8b, 0x52, 0x7d, 0xb9, 0xad, 0xbe, 0x5c, 0x7e, 0xd9, 0x3b, 0xe4, 0x0c, 0x6e, 0xed, 0x89, 0x67, 0x51, 0xaa, 0x2f, 0x37, 0xd5, 0x97, 0x0b, 0xe7, 0x7d,
0x97, 0x8a, 0x92, 0xee, 0xa8, 0xc5, 0xda, 0xdc, 0x8b, 0xfd, 0xc9, 0x89, 0x32, 0x6b, 0x03, 0x9d, 0xa9, 0x28, 0xe9, 0x8e, 0x5a, 0xac, 0xcd, 0xfd, 0xc4, 0x8f, 0x8f, 0x95, 0x59, 0x1b, 0xe8, 0xc4,
0x78, 0x43, 0x30, 0xdb, 0x82, 0x05, 0xfc, 0x4c, 0x69, 0xeb, 0x72, 0xa1, 0x13, 0x24, 0xec, 0x1a, 0x1b, 0x82, 0xd9, 0x06, 0xcc, 0xe2, 0x67, 0x4a, 0x5b, 0x57, 0x0b, 0x9d, 0x20, 0x61, 0x37, 0x60,
0x2c, 0xf0, 0xc1, 0x90, 0xab, 0x53, 0x1c, 0xb3, 0xcf, 0xd3, 0xb8, 0x47, 0x9e, 0x20, 0x40, 0x15, 0x96, 0x0f, 0x86, 0x5c, 0x9d, 0xe2, 0x98, 0x7d, 0x9e, 0xc6, 0x3d, 0xf2, 0x04, 0x01, 0xaa, 0x00,
0x80, 0x68, 0x4e, 0x05, 0xd8, 0x9a, 0x7e, 0x11, 0x8b, 0xf7, 0x07, 0xee, 0x06, 0xb0, 0x07, 0x82, 0x44, 0x0b, 0x2a, 0xc0, 0xd6, 0xf4, 0x73, 0x58, 0x7c, 0x30, 0x70, 0xd7, 0x80, 0x3d, 0x14, 0x5c,
0x6b, 0xcd, 0x28, 0xf4, 0x6f, 0x57, 0xa1, 0x61, 0xc0, 0x28, 0xcd, 0x43, 0x1c, 0x70, 0x6f, 0x10, 0x6b, 0x46, 0xa1, 0x7f, 0x73, 0x06, 0x9a, 0x06, 0x8c, 0xd2, 0x3c, 0xc4, 0x01, 0xf7, 0x06, 0x81,
0xf8, 0x63, 0x9e, 0xf2, 0x58, 0x72, 0x6a, 0x0e, 0x45, 0x3a, 0xff, 0x74, 0xd8, 0x8b, 0xa6, 0x69, 0x3f, 0xe6, 0x19, 0x4f, 0x24, 0xa7, 0x16, 0x50, 0xa4, 0xf3, 0x4f, 0x86, 0xbd, 0x68, 0x92, 0xf5,
0x6f, 0xc0, 0x87, 0x31, 0x17, 0xc6, 0x17, 0x8d, 0x81, 0x85, 0x22, 0xdd, 0xd8, 0x7f, 0x66, 0xd2, 0x06, 0x7c, 0x98, 0x70, 0x61, 0x7c, 0xd1, 0x18, 0x58, 0x28, 0xd2, 0x8d, 0xfd, 0xe7, 0x26, 0x9d,
0x09, 0x7e, 0xc8, 0xa1, 0x2a, 0xa6, 0x2c, 0xd6, 0xa8, 0x96, 0xc5, 0x94, 0xc5, 0x8a, 0xe4, 0xf5, 0xe0, 0x87, 0x02, 0xaa, 0x62, 0xca, 0x62, 0x8d, 0xea, 0x79, 0x4c, 0x59, 0xac, 0x48, 0x51, 0x0f,
0xd0, 0x42, 0x89, 0x1e, 0x7a, 0x0f, 0x36, 0x85, 0xc6, 0x91, 0xb2, 0xd9, 0xcb, 0xb1, 0xc9, 0x9c, 0xcd, 0x56, 0xe8, 0xa1, 0xf7, 0x60, 0x5d, 0x68, 0x1c, 0x29, 0x9b, 0xbd, 0x02, 0x9b, 0x9c, 0x51,
0x5a, 0xb6, 0x05, 0x6d, 0x1c, 0xb3, 0x62, 0xf0, 0x24, 0xf8, 0xa6, 0x88, 0xf2, 0x38, 0x5e, 0x01, 0xcb, 0x36, 0xa0, 0x8d, 0x63, 0x56, 0x0c, 0x9e, 0x06, 0xdf, 0x14, 0x51, 0x1e, 0xc7, 0x2b, 0xe1,
0x47, 0x5a, 0x14, 0x47, 0x8b, 0x56, 0xdc, 0x9a, 0x15, 0x70, 0xa2, 0xf5, 0x9f, 0xd9, 0xb4, 0x75, 0x48, 0x8b, 0xe2, 0x68, 0xd1, 0x8a, 0x5b, 0xb3, 0x12, 0x4e, 0xb4, 0xfe, 0x73, 0x9b, 0xb6, 0x21,
0x49, 0x9b, 0xc3, 0xdd, 0x15, 0x68, 0x1c, 0xa6, 0xd1, 0x44, 0x6d, 0x4a, 0x0b, 0x9a, 0xa2, 0x28, 0x69, 0x0b, 0xb8, 0xbb, 0x08, 0xcd, 0x83, 0x2c, 0x8a, 0xd5, 0xa6, 0x2c, 0x41, 0x4b, 0x14, 0x65,
0x73, 0x14, 0x5e, 0x83, 0x8b, 0xc4, 0x45, 0x8f, 0xa2, 0x49, 0x34, 0x8a, 0x86, 0xb3, 0xc3, 0xe9, 0x8e, 0xc2, 0x6b, 0x70, 0x99, 0xb8, 0xe8, 0x71, 0x14, 0x47, 0xa3, 0x68, 0x38, 0x3d, 0x98, 0x1c,
0x51, 0xd2, 0x8f, 0x83, 0x09, 0x9e, 0x78, 0xdc, 0x7f, 0x74, 0x60, 0xdd, 0xaa, 0x95, 0x61, 0xa1, 0xa6, 0xfd, 0x24, 0x88, 0xf1, 0xc4, 0xe3, 0xfe, 0x83, 0x03, 0xab, 0x56, 0xad, 0x0c, 0x0b, 0x7d,
0x4f, 0x0b, 0x96, 0xd6, 0x97, 0xcb, 0x82, 0xf1, 0xd6, 0x0c, 0x75, 0x28, 0x08, 0x45, 0x40, 0xee, 0x5a, 0xb0, 0xb4, 0xbe, 0x5c, 0x16, 0x8c, 0xb7, 0x62, 0xa8, 0x43, 0x41, 0x28, 0x02, 0x72, 0x4f,
0xb1, 0xbc, 0x6f, 0xbe, 0x05, 0xab, 0x6a, 0x64, 0xea, 0x43, 0xc1, 0x85, 0x9d, 0x22, 0x17, 0xca, 0xe4, 0x7d, 0xf3, 0x1d, 0x58, 0x56, 0x23, 0x53, 0x1f, 0x0a, 0x2e, 0xec, 0x94, 0xb9, 0x50, 0x7e,
0xef, 0x5b, 0xf2, 0x03, 0xd5, 0xc4, 0x2f, 0xc9, 0xdb, 0xc7, 0x01, 0xcd, 0x51, 0xc5, 0x07, 0xf4, 0xbf, 0x24, 0x3f, 0x50, 0x4d, 0xfc, 0x82, 0xbc, 0x7d, 0x1c, 0xd0, 0x1c, 0x55, 0x7c, 0x40, 0xdf,
0xfd, 0x92, 0x79, 0x4a, 0x50, 0x23, 0xe8, 0x6b, 0x30, 0x71, 0x7f, 0xcf, 0x01, 0xc8, 0x46, 0x87, 0x2f, 0x99, 0xa7, 0x04, 0x35, 0x82, 0xbe, 0x06, 0x53, 0xf7, 0x77, 0x1c, 0x80, 0x7c, 0x74, 0xc8,
0x8c, 0x91, 0xa9, 0x74, 0x91, 0x10, 0x6d, 0xa8, 0xef, 0xb7, 0xa0, 0xa9, 0x6f, 0x46, 0x32, 0x2b, 0x18, 0xb9, 0x4a, 0x17, 0x09, 0xd1, 0x86, 0xfa, 0x7e, 0x0b, 0x5a, 0xfa, 0x66, 0x24, 0xb7, 0x12,
0xd1, 0x50, 0x18, 0x3a, 0x72, 0x57, 0x61, 0x75, 0x38, 0x8a, 0x8e, 0xc8, 0xc4, 0x52, 0xd2, 0x4b, 0x4d, 0x85, 0xa1, 0x23, 0x77, 0x1d, 0x96, 0x87, 0xa3, 0xe8, 0x90, 0x4c, 0x2c, 0x25, 0xbd, 0xa4,
0x22, 0x33, 0x35, 0x5a, 0x02, 0xbe, 0x2b, 0xd1, 0xcc, 0xa4, 0xd4, 0x0c, 0x93, 0xe2, 0x7e, 0xbb, 0x32, 0x53, 0x63, 0x49, 0xc0, 0xf7, 0x24, 0x9a, 0x9b, 0x94, 0xba, 0x61, 0x52, 0xdc, 0x6f, 0xd7,
0xa2, 0xe3, 0xe9, 0xd9, 0x9c, 0xe7, 0x4a, 0x19, 0xdb, 0x2e, 0x28, 0xc7, 0x39, 0xe1, 0x6b, 0x8a, 0x74, 0x3c, 0x3d, 0x9f, 0xf3, 0x99, 0x52, 0xc6, 0x36, 0x4b, 0xca, 0xf1, 0x8c, 0xf0, 0x35, 0x45,
0x84, 0x1d, 0xbc, 0xf4, 0xa0, 0xfe, 0x21, 0xb4, 0x62, 0xa1, 0x7d, 0x94, 0x6a, 0xaa, 0xbd, 0x40, 0xc2, 0xf6, 0xcf, 0x3d, 0xa8, 0x7f, 0x08, 0x4b, 0x89, 0xd0, 0x3e, 0x4a, 0x35, 0xd5, 0x5f, 0xa2,
0x35, 0xad, 0xc4, 0x96, 0xdd, 0xf9, 0x24, 0xb4, 0xfd, 0xc1, 0x29, 0x8f, 0xd3, 0x80, 0x8e, 0x4a, 0x9a, 0x16, 0x13, 0xcb, 0xee, 0x7c, 0x12, 0xda, 0xfe, 0xe0, 0x84, 0x27, 0x59, 0x40, 0x47, 0x25,
0x64, 0xf4, 0x85, 0x42, 0x5d, 0x35, 0x70, 0xb2, 0xc5, 0x57, 0x61, 0x55, 0x66, 0xc7, 0x68, 0x4a, 0x32, 0xfa, 0x42, 0xa1, 0x2e, 0x1b, 0x38, 0xd9, 0xe2, 0xeb, 0xb0, 0x2c, 0xb3, 0x63, 0x34, 0xa5,
0x99, 0xd1, 0x99, 0xc1, 0x48, 0xe8, 0xfe, 0x99, 0x0a, 0xdd, 0xdb, 0x7b, 0x38, 0x7f, 0x45, 0xcc, 0xcc, 0xe8, 0xcc, 0x61, 0x24, 0x74, 0xff, 0x44, 0x85, 0xee, 0xed, 0x3d, 0x3c, 0x7b, 0x45, 0xcc,
0xd9, 0x55, 0x72, 0xb3, 0xfb, 0x84, 0x0c, 0xa3, 0x0f, 0xd4, 0x79, 0xac, 0x6a, 0xdc, 0x54, 0x0f, 0xd9, 0xd5, 0x0a, 0xb3, 0xfb, 0x84, 0x0c, 0xa3, 0x0f, 0xd4, 0x79, 0x6c, 0xc6, 0xb8, 0xa9, 0x1e,
0xe4, 0xb5, 0x87, 0xbd, 0xa4, 0xb5, 0x57, 0x59, 0x52, 0xf7, 0x07, 0x0e, 0x2c, 0xed, 0x45, 0x93, 0xc8, 0x6b, 0x0f, 0x7b, 0x49, 0xeb, 0xaf, 0xb2, 0xa4, 0xee, 0x0f, 0x1c, 0x98, 0xdf, 0x8d, 0xe2,
0x3d, 0x79, 0x67, 0x4f, 0x82, 0xa0, 0x73, 0xcf, 0x54, 0xf1, 0x05, 0xb7, 0xf9, 0xa5, 0xb6, 0x76, 0x5d, 0x79, 0x67, 0x4f, 0x82, 0xa0, 0x73, 0xcf, 0x54, 0xf1, 0x25, 0xb7, 0xf9, 0x95, 0xb6, 0x76,
0x25, 0x6f, 0x6b, 0x7f, 0x19, 0x5e, 0xa3, 0x68, 0x40, 0x1c, 0x4d, 0xa2, 0x18, 0x85, 0xd1, 0x1f, 0xb1, 0x68, 0x6b, 0x7f, 0x11, 0x5e, 0xa3, 0x68, 0x40, 0x12, 0xc5, 0x51, 0x82, 0xc2, 0xe8, 0x8f,
0x09, 0xc3, 0x1a, 0x85, 0xe9, 0x89, 0x52, 0x63, 0x2f, 0x22, 0xa1, 0x63, 0x17, 0x1e, 0x17, 0x84, 0x84, 0x61, 0x8d, 0xc2, 0xec, 0x58, 0xa9, 0xb1, 0x97, 0x91, 0xd0, 0xb1, 0x0b, 0x8f, 0x0b, 0xc2,
0x33, 0x2c, 0x7d, 0x03, 0xa1, 0xdd, 0x8a, 0x15, 0xee, 0x67, 0xa1, 0x4e, 0xce, 0x2d, 0x4d, 0xeb, 0x19, 0x96, 0xbe, 0x81, 0xd0, 0x6e, 0xe5, 0x0a, 0xf7, 0xb3, 0xd0, 0x20, 0xe7, 0x96, 0xa6, 0xf5,
0x1d, 0xa8, 0x9f, 0x44, 0x93, 0xde, 0x49, 0x10, 0xa6, 0x4a, 0xb8, 0x5b, 0x99, 0xd7, 0xb9, 0x47, 0x0e, 0x34, 0x8e, 0xa3, 0xb8, 0x77, 0x1c, 0x84, 0x99, 0x12, 0xee, 0xa5, 0xdc, 0xeb, 0xdc, 0xa5,
0x0b, 0xa2, 0x09, 0xdc, 0x1f, 0x55, 0x61, 0xe9, 0x7e, 0x78, 0x1a, 0x05, 0x7d, 0x8a, 0xf2, 0x8f, 0x05, 0xd1, 0x04, 0xee, 0xdf, 0xd5, 0x61, 0xfe, 0x41, 0x78, 0x12, 0x05, 0x7d, 0x8a, 0xf2, 0x8f,
0xf9, 0x38, 0x52, 0x99, 0x78, 0xf8, 0x1b, 0x97, 0x82, 0xb2, 0x52, 0x26, 0xa9, 0x0c, 0xd3, 0xab, 0xf9, 0x38, 0x52, 0x99, 0x78, 0xf8, 0x1b, 0x97, 0x82, 0xb2, 0x52, 0xe2, 0x4c, 0x86, 0xe9, 0x55,
0x22, 0x9a, 0xfb, 0x38, 0xcb, 0x96, 0x15, 0xa2, 0x63, 0x20, 0xe8, 0xd8, 0xc7, 0x66, 0x62, 0xb1, 0x11, 0xcd, 0x7d, 0x92, 0x67, 0xcb, 0x0a, 0xd1, 0x31, 0x10, 0x74, 0xec, 0x13, 0x33, 0xb1, 0x58,
0x2c, 0x65, 0xa9, 0x8c, 0x0b, 0x46, 0x2a, 0x23, 0xdd, 0x09, 0x89, 0xfc, 0x02, 0xe2, 0xaf, 0x65, 0x96, 0xf2, 0x54, 0xc6, 0x59, 0x23, 0x95, 0x91, 0xee, 0x84, 0x44, 0x7e, 0x01, 0xf1, 0xd7, 0x82,
0x4f, 0x15, 0xe9, 0x20, 0x12, 0x73, 0x11, 0xc5, 0x21, 0xc7, 0x61, 0x49, 0x1e, 0x44, 0x4c, 0x10, 0xa7, 0x8a, 0x74, 0x10, 0x49, 0xb8, 0x88, 0xe2, 0x90, 0xe3, 0x30, 0x2f, 0x0f, 0x22, 0x26, 0x88,
0x9d, 0x0b, 0xf1, 0x81, 0xa0, 0x11, 0xca, 0xd7, 0x84, 0xd0, 0xd9, 0xca, 0xe7, 0x26, 0xd7, 0x05, 0xce, 0x85, 0xf8, 0x40, 0xd0, 0x08, 0xe5, 0x6b, 0x42, 0xe8, 0x6c, 0x15, 0x73, 0x93, 0x1b, 0x82,
0xcf, 0xe7, 0x60, 0xd4, 0xd0, 0x03, 0xae, 0x15, 0xa9, 0x98, 0x03, 0x88, 0x6c, 0xe0, 0x3c, 0x6e, 0xe7, 0x0b, 0x30, 0x6a, 0xe8, 0x01, 0xd7, 0x8a, 0x54, 0xcc, 0x01, 0x44, 0x36, 0x70, 0x11, 0x37,
0x1c, 0x5f, 0x44, 0xe2, 0x90, 0x3a, 0xbe, 0x20, 0xa3, 0xf8, 0xa3, 0xd1, 0x91, 0xdf, 0x7f, 0x4a, 0x8e, 0x2f, 0x22, 0x71, 0x48, 0x1d, 0x5f, 0x90, 0x51, 0xfc, 0xd1, 0xe8, 0xd0, 0xef, 0x3f, 0xa3,
0xa9, 0xe7, 0x94, 0x27, 0x54, 0xf7, 0x6c, 0x90, 0x32, 0x06, 0xb2, 0xdd, 0xa4, 0x5b, 0xc5, 0x9a, 0xd4, 0x73, 0xca, 0x13, 0x6a, 0x78, 0x36, 0x48, 0x19, 0x03, 0xf9, 0x6e, 0xd2, 0xad, 0x62, 0xdd,
0x67, 0x42, 0x6c, 0x1b, 0x1a, 0x74, 0x64, 0x93, 0xfb, 0xd9, 0xa2, 0xfd, 0x6c, 0x9b, 0x67, 0x3a, 0x33, 0x21, 0xb6, 0x09, 0x4d, 0x3a, 0xb2, 0xc9, 0xfd, 0x5c, 0xa2, 0xfd, 0x6c, 0x9b, 0x67, 0x3a,
0xda, 0x51, 0x93, 0xc8, 0xbc, 0x79, 0x58, 0xb5, 0x53, 0x79, 0xbe, 0x0c, 0xec, 0xd6, 0x60, 0x20, 0xda, 0x51, 0x93, 0xc8, 0xbc, 0x79, 0x58, 0xb6, 0x6f, 0x1e, 0x84, 0xd2, 0x94, 0x17, 0x36, 0x6d,
0xf7, 0x5b, 0x1f, 0x19, 0xb3, 0x9d, 0x72, 0xac, 0x9d, 0x2a, 0x59, 0xb1, 0x4a, 0xe9, 0x8a, 0xb9, 0xea, 0x2d, 0x07, 0xd0, 0x9a, 0xca, 0x05, 0x13, 0x04, 0x2b, 0x44, 0x60, 0x61, 0x28, 0xb5, 0x78,
0xbb, 0xd0, 0x38, 0x30, 0x92, 0x96, 0x89, 0x35, 0x54, 0xba, 0xb2, 0x64, 0x27, 0x03, 0x31, 0x3a, 0xd0, 0x88, 0xfd, 0x60, 0xd0, 0x61, 0x42, 0x6a, 0x55, 0xd9, 0xcd, 0x80, 0xdd, 0x19, 0x0c, 0x24,
0xac, 0x98, 0x1d, 0xba, 0xbf, 0x00, 0x6c, 0x3f, 0x48, 0x52, 0x3d, 0xbe, 0x2c, 0x4b, 0x5a, 0x9d, 0x37, 0xe9, 0x03, 0x69, 0xce, 0x07, 0x8e, 0xc5, 0x07, 0x15, 0xfb, 0x51, 0xab, 0xde, 0x8f, 0x97,
0xdc, 0xb3, 0x44, 0xa3, 0x86, 0xc4, 0x28, 0x01, 0xe8, 0x96, 0xc8, 0x50, 0xca, 0x4f, 0x6c, 0x0b, 0x8e, 0xda, 0xdd, 0x81, 0xe6, 0xbe, 0x91, 0x30, 0x4d, 0x6c, 0xa9, 0x52, 0xa5, 0x25, 0x2b, 0x1b,
0x96, 0x03, 0x01, 0xe5, 0x25, 0x41, 0x51, 0xea, 0x7a, 0xf4, 0xd7, 0x24, 0x68, 0x59, 0xd1, 0xef, 0x88, 0x31, 0x9c, 0x9a, 0x39, 0x1c, 0xf7, 0xe7, 0x80, 0xed, 0x05, 0x69, 0xa6, 0x47, 0x9f, 0x67,
0x3b, 0xb0, 0x24, 0xa7, 0x86, 0xde, 0x46, 0x21, 0x5d, 0xbb, 0xee, 0x59, 0x58, 0x79, 0xa2, 0x6e, 0x68, 0xab, 0xa8, 0x41, 0x9e, 0xe4, 0xd4, 0x94, 0x18, 0x25, 0x1f, 0xdd, 0x11, 0xd9, 0x51, 0xc5,
0x91, 0x87, 0xab, 0x65, 0x3c, 0xcc, 0xa0, 0x36, 0xf1, 0xd3, 0x13, 0x3a, 0xa0, 0xd4, 0x3d, 0xfa, 0x69, 0x6f, 0xc0, 0x42, 0x20, 0xa0, 0xa2, 0x14, 0x2a, 0x4a, 0x5d, 0xef, 0x3e, 0x85, 0x55, 0x09,
0xcd, 0xda, 0xe2, 0xd0, 0x2c, 0x64, 0x85, 0x0e, 0xcc, 0x65, 0xb9, 0xea, 0x42, 0x25, 0x17, 0x70, 0x9a, 0x16, 0xdc, 0x9e, 0xb7, 0x73, 0xde, 0x6e, 0xd5, 0xca, 0xbb, 0xe5, 0x7e, 0xdf, 0x81, 0x79,
0x9c, 0x14, 0x65, 0x25, 0x08, 0x5c, 0x5f, 0x36, 0xc8, 0x7c, 0xa9, 0x0c, 0xce, 0xd6, 0x4b, 0x36, 0xb9, 0x38, 0x48, 0x5f, 0x4a, 0x36, 0x6f, 0x78, 0x16, 0x56, 0x9d, 0x66, 0x5c, 0x96, 0xc0, 0x99,
0x91, 0x5f, 0x2f, 0x49, 0xea, 0xe9, 0x7a, 0xb7, 0x0b, 0x9d, 0x3b, 0x7c, 0xc4, 0x53, 0x7e, 0x6b, 0x2a, 0x09, 0x64, 0x50, 0x8f, 0xfd, 0xec, 0x98, 0x8e, 0x57, 0x0d, 0x8f, 0x7e, 0xb3, 0xb6, 0x38,
0x34, 0xca, 0xb7, 0xff, 0x1a, 0x5c, 0x2c, 0xa9, 0x93, 0x4e, 0xcb, 0x5d, 0x58, 0xbb, 0xc3, 0x8f, 0xf2, 0x0b, 0x49, 0xa7, 0xe3, 0x7e, 0x55, 0xa6, 0xbd, 0x30, 0x28, 0x25, 0x1c, 0x5d, 0x68, 0xca,
0xa6, 0xc3, 0x7d, 0x7e, 0x9a, 0xdd, 0x08, 0x32, 0xa8, 0x25, 0x27, 0xd1, 0x99, 0xdc, 0x5b, 0xfa, 0xa9, 0x10, 0xb8, 0xbe, 0x2a, 0x91, 0xd9, 0x5e, 0x39, 0x9c, 0xaf, 0xb8, 0x6c, 0xa2, 0xb8, 0xe2,
0xcd, 0xde, 0x00, 0x18, 0x21, 0x4d, 0x2f, 0x99, 0xf0, 0xbe, 0x4a, 0x51, 0x25, 0xe4, 0x70, 0xc2, 0x92, 0xd4, 0xd3, 0xf5, 0x6e, 0x17, 0x3a, 0xdb, 0x7c, 0xc4, 0x33, 0x7e, 0x67, 0x34, 0x2a, 0xb6,
0xfb, 0xee, 0x7b, 0xc0, 0xcc, 0x76, 0xe4, 0x14, 0x50, 0x0f, 0x4c, 0x8f, 0x7a, 0xc9, 0x2c, 0x49, 0xff, 0x1a, 0x5c, 0xae, 0xa8, 0x93, 0x2e, 0xd7, 0x3d, 0x58, 0xd9, 0xe6, 0x87, 0x93, 0xe1, 0x1e,
0xf9, 0x58, 0xe5, 0xde, 0x9a, 0x90, 0x7b, 0x15, 0x9a, 0x07, 0xfe, 0xcc, 0xe3, 0xdf, 0x90, 0x19, 0x3f, 0xc9, 0xef, 0x33, 0x19, 0xd4, 0xd3, 0xe3, 0xe8, 0x54, 0x72, 0x07, 0xfd, 0x66, 0x6f, 0x00,
0xf3, 0x78, 0x36, 0xf6, 0x67, 0xc8, 0xca, 0xfa, 0x6c, 0x4c, 0xd5, 0xee, 0x7f, 0x55, 0x60, 0x51, 0x8c, 0x90, 0xa6, 0x97, 0xc6, 0xbc, 0xaf, 0x12, 0x6c, 0x09, 0x39, 0x88, 0x79, 0xdf, 0x7d, 0x0f,
0x50, 0x62, 0xab, 0x03, 0x9e, 0xa4, 0x41, 0x28, 0x6e, 0xc3, 0x64, 0xab, 0x06, 0x54, 0xe0, 0x8d, 0x98, 0xd9, 0x8e, 0x9c, 0x02, 0x6a, 0xb1, 0xc9, 0x61, 0x2f, 0x9d, 0xa6, 0x19, 0x1f, 0xab, 0xcc,
0x4a, 0x09, 0x6f, 0x48, 0x6f, 0x55, 0xa5, 0xfb, 0x49, 0x26, 0xb0, 0x30, 0x74, 0x6b, 0xb2, 0x2c, 0x61, 0x13, 0x72, 0xaf, 0x43, 0x6b, 0xdf, 0x9f, 0x7a, 0xfc, 0x1b, 0x32, 0xdf, 0x1f, 0x4f, 0xf6,
0x03, 0x71, 0x38, 0xcb, 0x80, 0x5c, 0xb0, 0x24, 0xd3, 0x36, 0x62, 0x7c, 0x8a, 0x69, 0x25, 0x3b, 0xfe, 0x14, 0x45, 0x45, 0x9f, 0xec, 0xa9, 0xda, 0xfd, 0xaf, 0x1a, 0xcc, 0x09, 0x4a, 0x6c, 0x75,
0x98, 0x50, 0xa9, 0x4e, 0x5b, 0x12, 0x5c, 0x53, 0xd0, 0x69, 0x05, 0xdd, 0xb5, 0xfc, 0x0a, 0xba, 0xc0, 0xd3, 0x2c, 0x08, 0xc5, 0x5d, 0x9e, 0x6c, 0xd5, 0x80, 0x4a, 0xbc, 0x51, 0xab, 0xe0, 0x0d,
0x4b, 0xb8, 0xb0, 0x2f, 0xd2, 0x5d, 0xf0, 0x0a, 0xba, 0xcb, 0x65, 0xd0, 0xbe, 0xcb, 0xb9, 0xc7, 0xe9, 0x6b, 0xab, 0x64, 0x45, 0xc9, 0x04, 0x16, 0x86, 0x1c, 0x9b, 0xe7, 0x48, 0x88, 0xa3, 0x65,
0xd1, 0x2a, 0x2a, 0x76, 0xfa, 0x8e, 0x03, 0x6d, 0x69, 0xd0, 0x75, 0x1d, 0x7b, 0xcb, 0xb2, 0xfe, 0x0e, 0x14, 0x42, 0x3d, 0xb9, 0xae, 0x14, 0xe3, 0x53, 0x6c, 0x2f, 0xd9, 0xc1, 0x84, 0x2a, 0x35,
0xa5, 0x49, 0x79, 0x57, 0x60, 0x85, 0x6c, 0xb2, 0x8e, 0x0a, 0xc9, 0x10, 0x96, 0x05, 0xe2, 0x3c, 0xf2, 0xbc, 0xe0, 0x9a, 0x92, 0x46, 0x2e, 0x69, 0xde, 0x85, 0x57, 0xd0, 0xbc, 0xc2, 0x01, 0x7f,
0x54, 0xe8, 0x7e, 0x1c, 0x8c, 0xe4, 0xa6, 0x98, 0x90, 0x0a, 0x2c, 0xe1, 0xf9, 0x98, 0xb6, 0xc4, 0x99, 0xe6, 0x85, 0x57, 0xd0, 0xbc, 0x2e, 0x83, 0xf6, 0x3d, 0xce, 0x3d, 0x8e, 0x36, 0x5d, 0xb1,
0xf1, 0x74, 0xd9, 0xfd, 0x1b, 0x07, 0xd6, 0x8c, 0x01, 0x4b, 0x2e, 0xfc, 0x10, 0x54, 0x16, 0x82, 0xd3, 0x77, 0x1c, 0x68, 0x4b, 0x77, 0x44, 0xd7, 0xb1, 0xb7, 0x2c, 0xdf, 0xa5, 0x32, 0xa5, 0xf0,
0x08, 0x1e, 0x09, 0x61, 0xba, 0x60, 0x3b, 0x27, 0xd9, 0x67, 0x16, 0x31, 0x6d, 0xa6, 0x3f, 0xa3, 0x1a, 0x2c, 0x92, 0x47, 0xa1, 0x63, 0x5a, 0x32, 0x00, 0x67, 0x81, 0x38, 0x0f, 0x75, 0xf1, 0x30,
0x01, 0x26, 0xd3, 0xb1, 0xf4, 0x40, 0x4c, 0x08, 0x19, 0xe9, 0x8c, 0xf3, 0xa7, 0x9a, 0xa4, 0x4a, 0x0e, 0x46, 0x72, 0x53, 0x4c, 0x48, 0x85, 0xc5, 0xf0, 0x74, 0x4f, 0x5b, 0xe2, 0x78, 0xba, 0xec,
0x24, 0x16, 0x46, 0x97, 0xcc, 0xe8, 0x4b, 0x68, 0x22, 0x91, 0x57, 0x65, 0x83, 0xee, 0x3f, 0x3b, 0xfe, 0x95, 0x03, 0x2b, 0xc6, 0x80, 0x25, 0x17, 0x7e, 0x08, 0x2a, 0x87, 0x42, 0x84, 0xbe, 0x84,
0xb0, 0x2e, 0x9c, 0x42, 0xe9, 0x72, 0xeb, 0x8c, 0xe9, 0x45, 0xe1, 0x05, 0x0b, 0x89, 0xdc, 0x3b, 0x30, 0x5d, 0xb2, 0x5d, 0xab, 0xfc, 0x33, 0x8b, 0x98, 0x36, 0xd3, 0x9f, 0xd2, 0x00, 0xd3, 0xc9,
0xe7, 0xc9, 0x32, 0xfb, 0xcc, 0x2b, 0x3a, 0xb2, 0x3a, 0xb9, 0x60, 0xce, 0x5e, 0x54, 0xcb, 0xf6, 0x58, 0x6a, 0x25, 0x13, 0x42, 0x46, 0x3a, 0xe5, 0xfc, 0x99, 0x26, 0x99, 0x11, 0x8a, 0xcb, 0xc4,
0xe2, 0x05, 0x2b, 0x5d, 0x16, 0x2c, 0x59, 0x28, 0x0d, 0x96, 0xdc, 0x5e, 0x82, 0x85, 0xa4, 0x1f, 0xe8, 0x8a, 0x1c, 0x3d, 0x21, 0x4d, 0x24, 0xb2, 0xc2, 0x6c, 0xd0, 0xfd, 0x27, 0x07, 0x56, 0x85,
0x4d, 0xb8, 0xbb, 0x09, 0x1b, 0xf6, 0xe4, 0xa4, 0x0a, 0xfa, 0xae, 0x03, 0x9d, 0xbb, 0x22, 0x74, 0x4b, 0x2b, 0x0f, 0x0c, 0x3a, 0xdf, 0x7b, 0x4e, 0xf8, 0xf0, 0x42, 0x22, 0x77, 0x2f, 0x78, 0xb2,
0x18, 0x84, 0xc3, 0xbd, 0x20, 0x49, 0xa3, 0x58, 0x3f, 0x11, 0xb9, 0x04, 0x90, 0xa4, 0x7e, 0x9c, 0xcc, 0x3e, 0xf3, 0x8a, 0x6e, 0xb8, 0x4e, 0x8d, 0x38, 0x63, 0x2f, 0x66, 0xaa, 0xf6, 0xe2, 0x25,
0x8a, 0xe4, 0x2f, 0x19, 0xe6, 0xc8, 0x10, 0x1c, 0x23, 0x0f, 0x07, 0xa2, 0x56, 0xec, 0x8d, 0x2e, 0x2b, 0x5d, 0x15, 0xea, 0x99, 0xad, 0x0c, 0xf5, 0xdc, 0x9d, 0x87, 0xd9, 0xb4, 0x1f, 0xc5, 0xdc,
0xe3, 0xc6, 0x50, 0xe2, 0x43, 0x2f, 0x3a, 0x3e, 0x4e, 0xb8, 0x76, 0x5b, 0x4d, 0x0c, 0x4f, 0xbe, 0x5d, 0x87, 0x35, 0x7b, 0x72, 0x52, 0x05, 0x7d, 0xd7, 0x81, 0xce, 0x3d, 0x11, 0xf8, 0x0c, 0xc2,
0x28, 0xf1, 0x78, 0xd6, 0xe3, 0xa7, 0xa4, 0x6a, 0x85, 0x3f, 0x98, 0x43, 0xdd, 0xbf, 0x72, 0x60, 0xe1, 0x6e, 0x90, 0x66, 0x51, 0xa2, 0x1f, 0xb8, 0x5c, 0x01, 0x48, 0x33, 0x3f, 0xc9, 0x44, 0xea,
0x35, 0x1b, 0xe4, 0x2e, 0x82, 0xb6, 0x76, 0x10, 0x43, 0x33, 0xb4, 0x83, 0x0a, 0xc0, 0x04, 0x83, 0x9a, 0x0c, 0xd2, 0xe4, 0x08, 0x8e, 0x91, 0x87, 0x03, 0x51, 0x2b, 0xf6, 0x46, 0x97, 0x71, 0x63,
0x5e, 0x10, 0xca, 0xb1, 0x19, 0x08, 0x49, 0xac, 0x2c, 0x45, 0x53, 0x95, 0x68, 0x67, 0x42, 0xe2, 0xc8, 0x6c, 0xf4, 0xa2, 0xa3, 0xa3, 0x94, 0x6b, 0xa7, 0xdb, 0xc4, 0xf0, 0xdc, 0x8e, 0x12, 0x8f,
0x16, 0x3d, 0xc5, 0xaf, 0x45, 0x96, 0x9d, 0x2c, 0x51, 0xee, 0xde, 0x38, 0xa5, 0xaf, 0x16, 0x85, 0x27, 0x55, 0x7e, 0x42, 0xaa, 0x56, 0x78, 0xb3, 0x05, 0xd4, 0xfd, 0x0b, 0x07, 0x96, 0xf3, 0x41,
0x43, 0x2c, 0x8b, 0xca, 0x3e, 0x2d, 0x11, 0x8a, 0x3f, 0xdd, 0xdf, 0x77, 0xe0, 0x62, 0xc9, 0xe2, 0xee, 0x20, 0x68, 0x6b, 0x07, 0x69, 0xcf, 0x72, 0xed, 0xa0, 0xc2, 0x47, 0x01, 0x1a, 0x38, 0x39,
0x4a, 0xc9, 0xb8, 0x03, 0x6b, 0xc7, 0xba, 0x52, 0x2d, 0x80, 0x10, 0x8f, 0x4d, 0x15, 0xeb, 0xb6, 0x36, 0x03, 0x21, 0x89, 0x95, 0xa5, 0x68, 0xa2, 0xd2, 0x04, 0x4d, 0x48, 0xe4, 0x00, 0x64, 0xf8,
0x27, 0xed, 0x15, 0x3f, 0x40, 0xf7, 0x98, 0xe2, 0x46, 0x62, 0x49, 0xad, 0x04, 0x94, 0x62, 0xc5, 0xb5, 0xc8, 0x11, 0x94, 0x25, 0xca, 0x3c, 0x1c, 0x67, 0xf4, 0xd5, 0x9c, 0x70, 0xe7, 0x65, 0x51,
0xf6, 0x1f, 0x54, 0xa1, 0x25, 0xee, 0x40, 0xc4, 0x63, 0x4d, 0x1e, 0xb3, 0x8f, 0x60, 0x49, 0x3e, 0xd9, 0xa7, 0x79, 0x42, 0xf1, 0xa7, 0xfb, 0xbb, 0x0e, 0x5c, 0xae, 0x58, 0x5c, 0x29, 0x19, 0xdb,
0xb6, 0x65, 0xe7, 0x65, 0xb7, 0xf6, 0xf3, 0xde, 0xee, 0x66, 0x1e, 0x96, 0xbc, 0xb3, 0xfe, 0x5b, 0xb0, 0x72, 0xa4, 0x2b, 0xd5, 0x02, 0x08, 0xf1, 0x58, 0x57, 0x91, 0x7a, 0x7b, 0xd2, 0x5e, 0xf9,
0x3f, 0xf8, 0xd7, 0x3f, 0xac, 0xac, 0xb0, 0xc6, 0x8d, 0xd3, 0x77, 0x6f, 0x0c, 0x79, 0x98, 0x60, 0x03, 0x74, 0xee, 0x29, 0xea, 0x25, 0x96, 0xd4, 0x4a, 0x9f, 0x29, 0x57, 0x6c, 0xfe, 0xde, 0x0c,
0x1b, 0xbf, 0x06, 0x90, 0x3d, 0x43, 0x65, 0x1d, 0xed, 0x64, 0xe4, 0xde, 0xd7, 0x76, 0x2f, 0x96, 0x2c, 0x89, 0x1b, 0x1c, 0xf1, 0xd4, 0x94, 0x27, 0xec, 0x23, 0x98, 0x97, 0x4f, 0x85, 0xd9, 0x45,
0xd4, 0xc8, 0x76, 0x2f, 0x52, 0xbb, 0xeb, 0x6e, 0x0b, 0xdb, 0x0d, 0xc2, 0x20, 0x15, 0x6f, 0x52, 0xd9, 0xad, 0xfd, 0x38, 0xb9, 0xbb, 0x5e, 0x84, 0x25, 0xef, 0xac, 0xfe, 0xc6, 0x0f, 0xfe, 0xe5,
0x3f, 0x70, 0xb6, 0xd8, 0x00, 0x9a, 0xe6, 0x2b, 0x53, 0xa6, 0x8e, 0xcc, 0x25, 0x6f, 0x5c, 0xbb, 0xf7, 0x6b, 0x8b, 0xac, 0x79, 0xeb, 0xe4, 0xdd, 0x5b, 0x43, 0x1e, 0xa6, 0xd8, 0xc6, 0xaf, 0x00,
0xaf, 0x95, 0xd6, 0xa9, 0x78, 0x01, 0xf5, 0x71, 0xde, 0x6d, 0x63, 0x1f, 0x53, 0xa2, 0xc8, 0x7a, 0xe4, 0x8f, 0x68, 0x59, 0x47, 0xbb, 0x29, 0x85, 0xd7, 0xc1, 0xdd, 0xcb, 0x15, 0x35, 0xb2, 0xdd,
0x19, 0x41, 0xcb, 0x7e, 0x4c, 0xca, 0x5e, 0x37, 0xc4, 0xba, 0xf0, 0x94, 0xb5, 0xfb, 0xc6, 0x9c, 0xcb, 0xd4, 0xee, 0xaa, 0xbb, 0x84, 0xed, 0x06, 0x61, 0x90, 0x89, 0x17, 0xb5, 0x1f, 0x38, 0x1b,
0x5a, 0xd9, 0xd7, 0x1b, 0xd4, 0xd7, 0x05, 0x97, 0x61, 0x5f, 0x7d, 0xa2, 0x51, 0x4f, 0x59, 0x3f, 0x6c, 0x00, 0x2d, 0xf3, 0x8d, 0x2c, 0x53, 0x07, 0xfe, 0x8a, 0x17, 0xba, 0xdd, 0xd7, 0x2a, 0xeb,
0x70, 0xb6, 0xb6, 0xbf, 0x75, 0x09, 0xea, 0x3a, 0xc8, 0xc5, 0xbe, 0x0e, 0x2b, 0xd6, 0x25, 0x15, 0x54, 0xb4, 0x83, 0xfa, 0xb8, 0xe8, 0xb6, 0xb1, 0x8f, 0x09, 0x51, 0xe4, 0xbd, 0x8c, 0x60, 0xc9,
0x53, 0xd3, 0x28, 0xbb, 0xd3, 0xea, 0xbe, 0x5e, 0x5e, 0x29, 0x3b, 0xbe, 0x44, 0x1d, 0x77, 0xd8, 0x7e, 0x0a, 0xcb, 0x5e, 0x37, 0xc4, 0xba, 0xf4, 0x10, 0xb7, 0xfb, 0xc6, 0x19, 0xb5, 0xb2, 0xaf,
0x26, 0x76, 0x2c, 0x6f, 0x79, 0x6e, 0xd0, 0xd5, 0x9c, 0xc8, 0x0c, 0x7c, 0x2a, 0xe6, 0x99, 0x5d, 0x37, 0xa8, 0xaf, 0x4b, 0x2e, 0xc3, 0xbe, 0xfa, 0x44, 0xa3, 0x1e, 0xe2, 0x7e, 0xe0, 0x6c, 0x6c,
0x2c, 0x59, 0xf3, 0x2c, 0x5c, 0x44, 0x59, 0xf3, 0x2c, 0xde, 0x46, 0xb9, 0xaf, 0x53, 0x77, 0x9b, 0x7e, 0xeb, 0x0a, 0x34, 0x74, 0x88, 0x8e, 0x7d, 0x1d, 0x16, 0xad, 0x2b, 0x36, 0xa6, 0xa6, 0x51,
0x6c, 0xc3, 0xec, 0x4e, 0x07, 0x9f, 0x38, 0xe5, 0x72, 0x9a, 0x6f, 0x4d, 0xd9, 0x1b, 0x9a, 0xb1, 0x75, 0x23, 0xd7, 0x7d, 0xbd, 0xba, 0x52, 0x76, 0x7c, 0x85, 0x3a, 0xee, 0xb0, 0x75, 0xec, 0x58,
0xca, 0xde, 0xa0, 0x6a, 0x16, 0x29, 0x3e, 0x44, 0x75, 0x3b, 0xd4, 0x15, 0x63, 0xb4, 0x7d, 0xe6, 0xde, 0x51, 0xdd, 0xa2, 0x8b, 0x45, 0x91, 0xd7, 0xf8, 0x4c, 0xcc, 0x33, 0xbf, 0x16, 0xb3, 0xe6,
0x53, 0x53, 0xf6, 0x55, 0xa8, 0xeb, 0x87, 0x55, 0xec, 0x82, 0xf1, 0x9a, 0xcd, 0x7c, 0xed, 0xd5, 0x59, 0xba, 0x46, 0xb3, 0xe6, 0x59, 0xbe, 0x4b, 0x73, 0x5f, 0xa7, 0xee, 0xd6, 0xd9, 0x9a, 0xd9,
0xed, 0x14, 0x2b, 0xca, 0x18, 0xc3, 0x6c, 0x19, 0x19, 0x63, 0x1f, 0xce, 0x4b, 0x97, 0xf8, 0x88, 0x9d, 0x0e, 0x9d, 0x71, 0xca, 0x44, 0x35, 0x5f, 0xca, 0xb2, 0x37, 0x34, 0x63, 0x55, 0xbd, 0xa0,
0xff, 0x38, 0x33, 0x29, 0x79, 0x21, 0x7b, 0xd3, 0x61, 0x1f, 0xc2, 0xb2, 0x7a, 0xaf, 0xc6, 0x36, 0xd5, 0x2c, 0x52, 0x7e, 0x46, 0xeb, 0x76, 0xa8, 0x2b, 0xc6, 0x68, 0xfb, 0xcc, 0x87, 0xb2, 0xec,
0xcb, 0xdf, 0xdd, 0x75, 0x2f, 0x14, 0x70, 0xa9, 0x3d, 0x6e, 0x01, 0x64, 0x6f, 0xad, 0xb4, 0x9c, 0xab, 0xd0, 0xd0, 0xcf, 0xc2, 0xd8, 0x25, 0xe3, 0x2d, 0x9e, 0xf9, 0x56, 0xad, 0xdb, 0x29, 0x57,
0x15, 0x5e, 0x80, 0xe9, 0x45, 0x2c, 0x79, 0x98, 0x35, 0xa4, 0x97, 0x65, 0xf6, 0x53, 0x2e, 0xf6, 0x54, 0x31, 0x86, 0xd9, 0x32, 0x32, 0xc6, 0x1e, 0x5c, 0x94, 0x4e, 0xf5, 0x21, 0xff, 0x51, 0x66,
0x66, 0x46, 0x5f, 0xfa, 0xc8, 0xeb, 0x05, 0x0d, 0xba, 0x9b, 0xb4, 0x76, 0x6d, 0x46, 0x82, 0x1b, 0x52, 0xf1, 0xbe, 0xf7, 0xb6, 0xc3, 0x3e, 0x84, 0x05, 0xf5, 0xda, 0x8e, 0xad, 0x57, 0xbf, 0x1a,
0xf2, 0x33, 0x95, 0xd5, 0x7c, 0x07, 0x1a, 0xc6, 0xfb, 0x2d, 0xa6, 0x5a, 0x28, 0xbe, 0xfd, 0xea, 0xec, 0x5e, 0x2a, 0xe1, 0x52, 0x7b, 0xdc, 0x01, 0xc8, 0x5f, 0x8a, 0x69, 0x39, 0x2b, 0xbd, 0x5f,
0x76, 0xcb, 0xaa, 0xe4, 0x70, 0xbf, 0x00, 0x2b, 0xd6, 0x43, 0x2c, 0x2d, 0x19, 0x65, 0xcf, 0xbc, 0xd3, 0x8b, 0x58, 0xf1, 0xac, 0x6c, 0x48, 0xef, 0xe2, 0xec, 0x87, 0x68, 0xec, 0xcd, 0x9c, 0xbe,
0xb4, 0x64, 0x94, 0xbf, 0xdd, 0xfa, 0x0a, 0x34, 0x8c, 0x67, 0x53, 0xcc, 0xc8, 0xe6, 0xca, 0x3d, 0xf2, 0x89, 0xda, 0x4b, 0x1a, 0x74, 0xd7, 0x69, 0xed, 0xda, 0x8c, 0x04, 0x37, 0xe4, 0xa7, 0x2a,
0x98, 0xd2, 0x23, 0x2a, 0x7b, 0x65, 0xb5, 0x41, 0xf3, 0x6d, 0xb9, 0x75, 0x9c, 0x2f, 0xa5, 0xf6, 0x27, 0x7b, 0x1b, 0x9a, 0xc6, 0xeb, 0x33, 0xa6, 0x5a, 0x28, 0xbf, 0x5c, 0xeb, 0x76, 0xab, 0xaa,
0x22, 0x93, 0x7c, 0x1d, 0x5a, 0xf6, 0x43, 0x2a, 0x2d, 0x55, 0xa5, 0x4f, 0xb2, 0xb4, 0x54, 0xcd, 0xe4, 0x70, 0xbf, 0x00, 0x8b, 0xd6, 0x33, 0x32, 0x2d, 0x19, 0x55, 0x8f, 0xd4, 0xb4, 0x64, 0x54,
0x79, 0x7d, 0x25, 0x19, 0x72, 0x6b, 0x5d, 0x77, 0x72, 0xe3, 0x63, 0x79, 0xc5, 0xf3, 0x9c, 0x7d, 0xbf, 0x3c, 0xfb, 0x0a, 0x34, 0x8d, 0x47, 0x5f, 0xcc, 0xc8, 0x45, 0x2b, 0x3c, 0xf7, 0xd2, 0x23,
0x09, 0x55, 0x87, 0xcc, 0xb5, 0x66, 0xd9, 0xf3, 0x31, 0x3b, 0x23, 0x5b, 0x73, 0x7b, 0x21, 0x2d, 0xaa, 0x7a, 0x23, 0xb6, 0x46, 0xf3, 0x5d, 0x72, 0x1b, 0x38, 0x5f, 0x4a, 0x4c, 0x46, 0x26, 0xf9,
0xdb, 0x5d, 0xa3, 0xc6, 0x1b, 0x2c, 0x9b, 0x81, 0xb0, 0x07, 0x94, 0x73, 0x6d, 0xd8, 0x03, 0x33, 0x3a, 0x2c, 0xd9, 0xcf, 0xc0, 0xb4, 0x54, 0x55, 0x3e, 0x28, 0xd3, 0x52, 0x75, 0xc6, 0xdb, 0x31,
0x2d, 0xdb, 0xb0, 0x07, 0x56, 0x6a, 0x76, 0xde, 0x1e, 0xa4, 0x01, 0xb6, 0x11, 0xc2, 0x6a, 0x2e, 0xc9, 0x90, 0x1b, 0xab, 0xba, 0x93, 0x5b, 0x1f, 0xcb, 0x0b, 0xaa, 0x17, 0xec, 0x4b, 0xa8, 0x3a,
0x9d, 0x41, 0x0b, 0x4b, 0x79, 0xfe, 0x57, 0xf7, 0xd2, 0x8b, 0xb3, 0x20, 0x6c, 0x35, 0xa3, 0xd4, 0x64, 0xa6, 0x38, 0xcb, 0x1f, 0xbf, 0xd9, 0xf9, 0xe4, 0x9a, 0xdb, 0x4b, 0x49, 0xe5, 0xee, 0x0a,
0xcb, 0x0d, 0x95, 0xae, 0xf7, 0xeb, 0xd0, 0x34, 0x1f, 0xc0, 0x68, 0x0b, 0x51, 0xf2, 0x6c, 0x47, 0x35, 0xde, 0x64, 0xf9, 0x0c, 0x84, 0x3d, 0xa0, 0x8c, 0x71, 0xc3, 0x1e, 0x98, 0x49, 0xe5, 0x86,
0x5b, 0x88, 0xb2, 0x17, 0x33, 0x6a, 0x73, 0x59, 0xd3, 0xec, 0x06, 0x37, 0xd7, 0x7e, 0x2f, 0x90, 0x3d, 0xb0, 0x12, 0xcb, 0x8b, 0xf6, 0x20, 0x0b, 0xb0, 0x8d, 0x10, 0x96, 0x0b, 0xc9, 0x18, 0x5a,
0xa9, 0xcc, 0xb2, 0x87, 0x10, 0x99, 0xca, 0x2c, 0x7d, 0x64, 0xa0, 0x36, 0x97, 0xad, 0x5b, 0x73, 0x58, 0xaa, 0xb3, 0xd7, 0xba, 0x57, 0x5e, 0x9e, 0xc3, 0x61, 0xab, 0x19, 0xa5, 0x5e, 0x6e, 0xa9,
0x11, 0xb1, 0x3d, 0xf6, 0x15, 0x58, 0x35, 0x72, 0x85, 0x0e, 0x67, 0x61, 0x5f, 0x33, 0x6a, 0x31, 0x64, 0xc3, 0x5f, 0x85, 0x96, 0xf9, 0x7c, 0x47, 0x5b, 0x88, 0x8a, 0x47, 0x47, 0xda, 0x42, 0x54,
0x93, 0xb4, 0x5b, 0xe6, 0x79, 0xba, 0x17, 0xa8, 0xfd, 0x35, 0xd7, 0x9a, 0x04, 0x32, 0xe9, 0x0e, 0xbd, 0xf7, 0x51, 0x9b, 0xcb, 0x5a, 0x66, 0x37, 0xb8, 0xb9, 0xf6, 0x6b, 0x87, 0x5c, 0x65, 0x56,
0x34, 0xcc, 0x3c, 0xa4, 0x17, 0xb4, 0x7b, 0xc1, 0xa8, 0x32, 0x93, 0x2a, 0x6f, 0x3a, 0xec, 0x8f, 0x3d, 0xe3, 0xc8, 0x55, 0x66, 0xe5, 0x13, 0x09, 0xb5, 0xb9, 0x6c, 0xd5, 0x9a, 0x8b, 0x88, 0x4c,
0x1d, 0x68, 0x5a, 0x59, 0x3d, 0x56, 0x04, 0x3b, 0xd7, 0x4e, 0xc7, 0xac, 0x33, 0x1b, 0x72, 0x3d, 0xb2, 0xaf, 0xc0, 0xb2, 0x91, 0xe9, 0x74, 0x30, 0x0d, 0xfb, 0x9a, 0x51, 0xcb, 0x79, 0xb0, 0xdd,
0x1a, 0xe4, 0xfe, 0xd6, 0x17, 0xac, 0x45, 0xf8, 0xd8, 0x3a, 0xc1, 0x5c, 0xcf, 0xbf, 0xa3, 0x7e, 0x2a, 0xcf, 0xd3, 0xbd, 0x44, 0xed, 0xaf, 0xb8, 0xd6, 0x24, 0x90, 0x49, 0xb7, 0xa0, 0x69, 0x66,
0x9e, 0x27, 0x30, 0xb3, 0x6d, 0x9f, 0xdf, 0x74, 0xd8, 0x07, 0xe2, 0x9f, 0x02, 0x54, 0xc4, 0x82, 0x51, 0xbd, 0xa4, 0xdd, 0x4b, 0x46, 0x95, 0x99, 0x12, 0x7a, 0xdb, 0x61, 0x7f, 0xe8, 0x40, 0xcb,
0x19, 0x8a, 0x34, 0xbf, 0x64, 0xe6, 0x33, 0xf9, 0x6b, 0xce, 0x4d, 0x87, 0x7d, 0x4d, 0x3c, 0x97, 0xca, 0x49, 0xb2, 0xe2, 0xef, 0x85, 0x76, 0x3a, 0x66, 0x9d, 0xd9, 0x90, 0xeb, 0xd1, 0x20, 0xf7,
0x96, 0xdf, 0xd2, 0xca, 0xbf, 0xea, 0xf7, 0xee, 0x15, 0x9a, 0xcd, 0x25, 0xf7, 0xa2, 0x35, 0x9b, 0x36, 0xbe, 0x60, 0x2d, 0xc2, 0xc7, 0xd6, 0x09, 0xe6, 0x66, 0xf1, 0x15, 0xf8, 0x8b, 0x22, 0x81,
0xbc, 0x25, 0xb9, 0x25, 0x46, 0x27, 0x5f, 0xc1, 0x67, 0x2a, 0xb1, 0xf0, 0x32, 0x7e, 0xfe, 0x20, 0x99, 0x2b, 0xfc, 0xe2, 0xb6, 0xc3, 0x3e, 0x10, 0xff, 0x73, 0xa0, 0x22, 0x16, 0xcc, 0x50, 0xa4,
0xc7, 0x62, 0x90, 0x92, 0xdc, 0x62, 0x8f, 0x57, 0x6c, 0xc6, 0xdd, 0xa2, 0xb1, 0x5e, 0x71, 0xdf, 0xc5, 0x25, 0x33, 0x1f, 0xf9, 0xdf, 0x70, 0x6e, 0x3b, 0xec, 0x6b, 0xe2, 0xb1, 0xb7, 0xfc, 0x96,
0x9c, 0x3b, 0xd6, 0x1b, 0x74, 0x22, 0xc5, 0x11, 0x1f, 0x00, 0x64, 0x01, 0x33, 0x96, 0x8b, 0x1e, 0x56, 0xfe, 0x55, 0xbf, 0x77, 0xaf, 0xd1, 0x6c, 0xae, 0xb8, 0x97, 0xad, 0xd9, 0x14, 0x2d, 0xc9,
0x69, 0xab, 0x50, 0x8c, 0xa9, 0xd9, 0x3c, 0xa8, 0x82, 0x4c, 0xd8, 0xe2, 0x57, 0x85, 0xa8, 0x4a, 0x1d, 0x31, 0x3a, 0xf9, 0x86, 0x3f, 0x57, 0x89, 0xa5, 0x77, 0xfd, 0x67, 0x0f, 0x72, 0x2c, 0x06,
0xfa, 0x44, 0x8f, 0xbe, 0x18, 0xf8, 0xea, 0x76, 0xcb, 0xaa, 0xca, 0x04, 0x55, 0xb5, 0xcf, 0x1e, 0x29, 0xc9, 0x2d, 0xf6, 0x78, 0xc5, 0x66, 0xdc, 0x0d, 0x1a, 0xeb, 0x35, 0xf7, 0xcd, 0x33, 0xc7,
0xc3, 0xca, 0x7e, 0x14, 0x3d, 0x9d, 0x4e, 0x74, 0x48, 0xd7, 0x8e, 0xdf, 0xec, 0xf9, 0xc9, 0x49, 0x7a, 0x8b, 0x4e, 0xa4, 0x38, 0xe2, 0x7d, 0x80, 0x3c, 0x20, 0xc7, 0x0a, 0xf1, 0x27, 0x6d, 0x15,
0x37, 0x37, 0x0b, 0xf7, 0x32, 0x35, 0xd5, 0x65, 0x1d, 0xa3, 0xa9, 0x1b, 0x1f, 0x67, 0xe1, 0xba, 0xca, 0x31, 0x3b, 0x9b, 0x07, 0x55, 0x98, 0x0a, 0x5b, 0xfc, 0xaa, 0x10, 0x55, 0x49, 0x9f, 0xea,
0xe7, 0xcc, 0x87, 0x35, 0xed, 0x01, 0xe8, 0x81, 0x77, 0xed, 0x66, 0xcc, 0xa8, 0x59, 0xa1, 0x0b, 0xd1, 0x97, 0x43, 0x67, 0xdd, 0x6e, 0x55, 0x55, 0x95, 0xa0, 0xaa, 0xf6, 0xd9, 0x13, 0x58, 0xdc,
0xcb, 0x27, 0x53, 0xa3, 0xbd, 0x91, 0xa8, 0x36, 0x6f, 0x3a, 0xec, 0x00, 0x9a, 0x77, 0x78, 0x3f, 0x8b, 0xa2, 0x67, 0x93, 0x58, 0x07, 0xa4, 0xed, 0xf8, 0xcd, 0xae, 0x9f, 0x1e, 0x77, 0x0b, 0xb3,
0x1a, 0x70, 0x19, 0x71, 0x59, 0xcf, 0x06, 0xae, 0x43, 0x35, 0xdd, 0x15, 0x0b, 0xb4, 0x75, 0xe2, 0x70, 0xaf, 0x52, 0x53, 0x5d, 0xd6, 0x31, 0x9a, 0xba, 0xf5, 0x71, 0x1e, 0xf0, 0x7b, 0xc1, 0x7c,
0xc4, 0x9f, 0xc5, 0xfc, 0x1b, 0x37, 0x3e, 0x96, 0xb1, 0x9c, 0xe7, 0x4a, 0x27, 0xaa, 0xf8, 0x93, 0x58, 0xd1, 0x1e, 0x80, 0x1e, 0x78, 0xd7, 0x6e, 0xc6, 0x8c, 0xbb, 0x95, 0xba, 0xb0, 0x7c, 0x32,
0xa5, 0x13, 0x73, 0x01, 0x2b, 0x4b, 0x27, 0x16, 0x02, 0x56, 0xd6, 0x52, 0xab, 0xf8, 0x17, 0x1b, 0x35, 0xda, 0x5b, 0xa9, 0x6a, 0xf3, 0xb6, 0xc3, 0xf6, 0xa1, 0xb5, 0xcd, 0xfb, 0xd1, 0x80, 0xcb,
0xc1, 0x5a, 0x21, 0xc6, 0xa5, 0xfd, 0x88, 0x79, 0x91, 0xb1, 0xee, 0xe5, 0xf9, 0x04, 0x76, 0x6f, 0x88, 0xcb, 0x6a, 0x3e, 0x70, 0x1d, 0xaa, 0xe9, 0x2e, 0x5a, 0xa0, 0xad, 0x13, 0x63, 0x7f, 0x9a,
0x5b, 0x76, 0x6f, 0x87, 0xb0, 0x72, 0x87, 0x8b, 0xc5, 0x12, 0xf7, 0xc6, 0xb9, 0x67, 0x59, 0xe6, 0xf0, 0x6f, 0xdc, 0xfa, 0x58, 0xc6, 0x72, 0x5e, 0x28, 0x9d, 0xa8, 0xe2, 0x4f, 0x96, 0x4e, 0x2c,
0x1d, 0x73, 0x5e, 0x29, 0x52, 0x9d, 0x6d, 0xf4, 0xe8, 0xd2, 0x96, 0x7d, 0x15, 0x1a, 0xf7, 0x78, 0x04, 0xac, 0x2c, 0x9d, 0x58, 0x0a, 0x58, 0x59, 0x4b, 0xad, 0xe2, 0x5f, 0x6c, 0x04, 0x2b, 0xa5,
0xaa, 0x2e, 0x8a, 0xb5, 0x37, 0x96, 0xbb, 0x39, 0xee, 0x96, 0xdc, 0x33, 0xdb, 0x3c, 0x43, 0xad, 0x18, 0x97, 0xf6, 0x23, 0xce, 0x8a, 0x8c, 0x75, 0xaf, 0x9e, 0x4d, 0x60, 0xf7, 0xb6, 0x61, 0xf7,
0xdd, 0xe0, 0x83, 0x21, 0x17, 0xea, 0xa9, 0x17, 0x0c, 0x9e, 0xb3, 0x5f, 0xa1, 0xc6, 0x75, 0x6e, 0x76, 0x00, 0x8b, 0xdb, 0x5c, 0x2c, 0x96, 0xb8, 0xf5, 0x2e, 0x3c, 0x2a, 0x33, 0x6f, 0xc8, 0x8b,
0xc9, 0xa6, 0x71, 0xbf, 0x68, 0x36, 0xbe, 0x9a, 0xc3, 0xcb, 0x5a, 0x0e, 0xa3, 0x01, 0x37, 0xcc, 0x4a, 0x91, 0xea, 0x6c, 0xa3, 0x47, 0x57, 0xce, 0xec, 0xab, 0xd0, 0xbc, 0xcf, 0x33, 0x75, 0xcd,
0x7f, 0x08, 0x0d, 0x23, 0xf1, 0x49, 0x0b, 0x50, 0x31, 0x89, 0x4b, 0x0b, 0x50, 0x49, 0x9e, 0x94, 0xad, 0xbd, 0xb1, 0xc2, 0xbd, 0x77, 0xb7, 0xe2, 0x96, 0xdc, 0xe6, 0x19, 0x6a, 0xed, 0x16, 0x1f,
0x7b, 0x8d, 0xfa, 0x71, 0xd9, 0xe5, 0xac, 0x1f, 0x91, 0x1b, 0x95, 0xf5, 0x74, 0xe3, 0x63, 0x7f, 0x0c, 0xb9, 0x50, 0x4f, 0xbd, 0x60, 0xf0, 0x82, 0xfd, 0x12, 0x35, 0xae, 0x33, 0x63, 0xd6, 0x8d,
0x9c, 0x3e, 0x67, 0x4f, 0xe8, 0x89, 0x96, 0x79, 0x19, 0x9e, 0x79, 0x83, 0xf9, 0x7b, 0x73, 0xbd, 0xdb, 0x51, 0xb3, 0xf1, 0xe5, 0x02, 0x5e, 0xd5, 0x72, 0x18, 0x0d, 0xb8, 0x61, 0xfe, 0x43, 0x68,
0x58, 0x46, 0x95, 0xed, 0x21, 0x8a, 0xae, 0xc8, 0x4b, 0xf8, 0x0c, 0xc0, 0x61, 0x1a, 0x4d, 0xee, 0x1a, 0x69, 0x5b, 0x5a, 0x80, 0xca, 0x29, 0x68, 0x5a, 0x80, 0x2a, 0xb2, 0xbc, 0xdc, 0x1b, 0xd4,
0xf8, 0x7c, 0x1c, 0x85, 0x99, 0xae, 0xcd, 0x2e, 0x7c, 0x33, 0xfd, 0x65, 0xdc, 0xfa, 0xb2, 0x27, 0x8f, 0xcb, 0xae, 0xe6, 0xfd, 0x88, 0xcc, 0xae, 0xbc, 0xa7, 0x5b, 0x1f, 0xfb, 0xe3, 0xec, 0x05,
0x86, 0x3f, 0x6e, 0xe5, 0x12, 0x28, 0xe6, 0x9a, 0x7b, 0x27, 0xac, 0x17, 0xa4, 0xe4, 0x5e, 0xf8, 0x7b, 0x4a, 0x0f, 0xcc, 0xcc, 0xab, 0xfc, 0xdc, 0x1b, 0x2c, 0xde, 0xfa, 0xeb, 0xc5, 0x32, 0xaa,
0xa6, 0x83, 0xde, 0x75, 0x16, 0x51, 0xd5, 0xde, 0x75, 0x21, 0x58, 0xab, 0xd5, 0x5e, 0x49, 0xf8, 0x6c, 0x0f, 0x51, 0x74, 0x45, 0x5e, 0xc2, 0x67, 0x00, 0x0e, 0xb2, 0x28, 0xde, 0xf6, 0xf9, 0x38,
0xf5, 0x00, 0xea, 0x59, 0x88, 0xee, 0x42, 0x96, 0xbc, 0x66, 0x05, 0xf4, 0xb4, 0x55, 0x2c, 0x04, 0x0a, 0x73, 0x5d, 0x9b, 0x5f, 0x57, 0xe7, 0xfa, 0xcb, 0xb8, 0xb3, 0x66, 0x4f, 0x0d, 0x7f, 0xdc,
0xce, 0xdc, 0x36, 0x2d, 0x15, 0xb0, 0x65, 0x5c, 0x2a, 0x8a, 0x86, 0x05, 0xb0, 0x2e, 0x06, 0xa8, 0xca, 0x84, 0x50, 0xcc, 0x75, 0xe6, 0x8d, 0xb6, 0x5e, 0x90, 0x8a, 0x5b, 0xed, 0xdb, 0x0e, 0x7a,
0x4d, 0x3c, 0x5d, 0x61, 0xaa, 0x99, 0x94, 0x04, 0xaf, 0xb4, 0x34, 0x97, 0xc6, 0x7e, 0xac, 0x73, 0xd7, 0x79, 0x44, 0x55, 0x7b, 0xd7, 0xa5, 0x60, 0xad, 0x56, 0x7b, 0x15, 0xe1, 0xd7, 0x7d, 0x68,
0x36, 0x72, 0xab, 0xb8, 0x3e, 0x45, 0xd5, 0x3c, 0x86, 0xb5, 0x42, 0xe0, 0x42, 0x8b, 0xf4, 0xbc, 0xe4, 0x21, 0xba, 0x4b, 0x79, 0xea, 0x9d, 0x15, 0xd0, 0xd3, 0x56, 0xb1, 0x14, 0x38, 0x73, 0xdb,
0x78, 0x91, 0x16, 0xe9, 0xb9, 0x31, 0x0f, 0xf7, 0x3c, 0x75, 0xb9, 0xea, 0x02, 0x76, 0x99, 0x9c, 0xb4, 0x54, 0xc0, 0x16, 0x70, 0xa9, 0x28, 0x1a, 0x16, 0xc0, 0xaa, 0x18, 0xa0, 0x36, 0xf1, 0x74,
0x05, 0x69, 0xff, 0xe4, 0x03, 0x67, 0xeb, 0x68, 0x91, 0xfe, 0x59, 0xec, 0x53, 0xff, 0x1b, 0x00, 0x01, 0xab, 0x66, 0x52, 0x11, 0xbc, 0xd2, 0xd2, 0x5c, 0x19, 0xfb, 0xb1, 0xce, 0xd9, 0xc8, 0xad,
0x00, 0xff, 0xff, 0x49, 0x74, 0xd2, 0x22, 0x8b, 0x4c, 0x00, 0x00, 0xe2, 0xf2, 0x17, 0x55, 0xf3, 0x18, 0x56, 0x4a, 0x81, 0x0b, 0x2d, 0xd2, 0x67, 0xc5, 0x8b, 0xb4,
0x48, 0x9f, 0x19, 0xf3, 0x70, 0x2f, 0x52, 0x97, 0xcb, 0x2e, 0x60, 0x97, 0xe9, 0x69, 0x90, 0xf5,
0x8f, 0x3f, 0x70, 0x36, 0x0e, 0xe7, 0xe8, 0x7f, 0xd1, 0x3e, 0xf5, 0xbf, 0x01, 0x00, 0x00, 0xff,
0xff, 0xb6, 0xb3, 0x40, 0xb7, 0x49, 0x4d, 0x00, 0x00,
} }

@ -392,10 +392,18 @@ func request_Lightning_LookupInvoice_0(ctx context.Context, marshaler runtime.Ma
} }
var (
filter_Lightning_SubscribeInvoices_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Lightning_SubscribeInvoices_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (Lightning_SubscribeInvoicesClient, runtime.ServerMetadata, error) { func request_Lightning_SubscribeInvoices_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (Lightning_SubscribeInvoicesClient, runtime.ServerMetadata, error) {
var protoReq InvoiceSubscription var protoReq InvoiceSubscription
var metadata runtime.ServerMetadata var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Lightning_SubscribeInvoices_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
stream, err := client.SubscribeInvoices(ctx, &protoReq) stream, err := client.SubscribeInvoices(ctx, &protoReq)
if err != nil { if err != nil {
return nil, metadata, err return nil, metadata, err

@ -464,7 +464,14 @@ service Lightning {
/** /**
SubscribeInvoices returns a uni-directional stream (sever -> client) for SubscribeInvoices returns a uni-directional stream (sever -> client) for
notifying the client of newly added/settled invoices. notifying the client of newly added/settled invoices. The caller can
optionally specify the add_index and/or the settle_index. If the add_index
is specified, then we'll first start by sending add invoice events for all
invoices with an add_index greater than the specified value. If the
settle_index is specified, the next, we'll send out all settle events for
invoices with a settle_index greater than the specified value. One or both
of these fields can be set. If no fields are set, then we'll only send out
the latest add/settle events.
*/ */
rpc SubscribeInvoices (InvoiceSubscription) returns (stream Invoice) { rpc SubscribeInvoices (InvoiceSubscription) returns (stream Invoice) {
option (google.api.http) = { option (google.api.http) = {
@ -1642,6 +1649,32 @@ message Invoice {
/// Whether this invoice should include routing hints for private channels. /// Whether this invoice should include routing hints for private channels.
bool private = 15 [json_name = "private"]; bool private = 15 [json_name = "private"];
/**
The "add" index of this invoice. Each newly created invoice will increment
this index making it monotonically increasing. Callers to the
SubscribeInvoices call can use this to instantly get notified of all added
invoices with an add_index greater than this one.
*/
uint64 add_index = 16 [json_name = "add_index"];
/**
The "settle" index of this invoice. Each newly settled invoice will
increment this index making it monotonically increasing. Callers to the
SubscribeInvoices call can use this to instantly get notified of all
settled invoices with an settle_index greater than this one.
*/
uint64 settle_index = 17 [json_name = "settle_index"];
/**
The amount that was accepted for this invoice. This will ONLY be set if
this invoice has been settled. We provide this field as if the invoice was
created with a zero value, then we need to record what amount was
ultimately accepted. Additionally, it's possible that the sender paid MORE
that was specified in the original invoice. So we'll record that here as
well.
*/
int64 amt_paid = 18 [json_name = "amt_paid"];
} }
message AddInvoiceResponse { message AddInvoiceResponse {
bytes r_hash = 1 [json_name = "r_hash"]; bytes r_hash = 1 [json_name = "r_hash"];
@ -1652,6 +1685,14 @@ message AddInvoiceResponse {
payment to the recipient. payment to the recipient.
*/ */
string payment_request = 2 [json_name = "payment_request"]; string payment_request = 2 [json_name = "payment_request"];
/**
The "add" index of this invoice. Each newly created invoice will increment
this index making it monotonically increasing. Callers to the
SubscribeInvoices call can use this to instantly get notified of all added
invoices with an add_index greater than this one.
*/
uint64 add_index = 16 [json_name = "add_index"];
} }
message PaymentHash { message PaymentHash {
/** /**
@ -1672,6 +1713,21 @@ message ListInvoiceResponse {
} }
message InvoiceSubscription { message InvoiceSubscription {
/**
If specified (non-zero), then we'll first start by sending out
notifications for all added indexes with an add_index greater than this
value. This allows callers to catch up on any events they missed while they
weren't connected to the streaming RPC.
*/
uint64 add_index = 1 [json_name = "add_index"];
/**
If specified (non-zero), then we'll first start by sending out
notifications for all settled indexes with an settle_index greater than
this value. This allows callers to catch up on any events they missed while
they weren't connected to the streaming RPC.
*/
uint64 settle_index = 2 [json_name = "settle_index"];
} }

@ -666,7 +666,7 @@
}, },
"/v1/invoices/subscribe": { "/v1/invoices/subscribe": {
"get": { "get": {
"summary": "*\nSubscribeInvoices returns a uni-directional stream (sever -\u003e client) for\nnotifying the client of newly added/settled invoices.", "summary": "*\nSubscribeInvoices returns a uni-directional stream (sever -\u003e client) for\nnotifying the client of newly added/settled invoices. The caller can\noptionally specify the add_index and/or the settle_index. If the add_index\nis specified, then we'll first start by sending add invoice events for all\ninvoices with an add_index greater than the specified value. If the\nsettle_index is specified, the next, we'll send out all settle events for\ninvoices with a settle_index greater than the specified value. One or both\nof these fields can be set. If no fields are set, then we'll only send out\nthe latest add/settle events.",
"operationId": "SubscribeInvoices", "operationId": "SubscribeInvoices",
"responses": { "responses": {
"200": { "200": {
@ -676,6 +676,24 @@
} }
} }
}, },
"parameters": [
{
"name": "add_index",
"description": "*\nIf specified (non-zero), then we'll first start by sending out\nnotifications for all added indexes with an add_index greater than this\nvalue. This allows callers to catch up on any events they missed while they\nweren't connected to the streaming RPC.",
"in": "query",
"required": false,
"type": "string",
"format": "uint64"
},
{
"name": "settle_index",
"description": "*\nIf specified (non-zero), then we'll first start by sending out\nnotifications for all settled indexes with an settle_index greater than\nthis value. This allows callers to catch up on any events they missed while\nthey weren't connected to the streaming RPC.",
"in": "query",
"required": false,
"type": "string",
"format": "uint64"
}
],
"tags": [ "tags": [
"Lightning" "Lightning"
] ]
@ -1060,6 +1078,11 @@
"payment_request": { "payment_request": {
"type": "string", "type": "string",
"description": "*\nA bare-bones invoice for a payment within the Lightning Network. With the\ndetails of the invoice, the sender has all the data necessary to send a\npayment to the recipient." "description": "*\nA bare-bones invoice for a payment within the Lightning Network. With the\ndetails of the invoice, the sender has all the data necessary to send a\npayment to the recipient."
},
"add_index": {
"type": "string",
"format": "uint64",
"description": "*\nThe \"add\" index of this invoice. Each newly created invoice will increment\nthis index making it monotonically increasing. Callers to the\nSubscribeInvoices call can use this to instantly get notified of all added\ninvoices with an add_index greater than this one."
} }
} }
}, },
@ -1893,6 +1916,21 @@
"type": "boolean", "type": "boolean",
"format": "boolean", "format": "boolean",
"description": "/ Whether this invoice should include routing hints for private channels." "description": "/ Whether this invoice should include routing hints for private channels."
},
"add_index": {
"type": "string",
"format": "uint64",
"description": "*\nThe \"add\" index of this invoice. Each newly created invoice will increment\nthis index making it monotonically increasing. Callers to the\nSubscribeInvoices call can use this to instantly get notified of all added\ninvoices with an add_index greater than this one."
},
"settle_index": {
"type": "string",
"format": "uint64",
"description": "*\nThe \"settle\" index of this invoice. Each newly settled invoice will\nincrement this index making it monotonically increasing. Callers to the\nSubscribeInvoices call can use this to instantly get notified of all\nsettled invoices with an settle_index greater than this one."
},
"amt_paid": {
"type": "string",
"format": "int64",
"description": "*\nThe amount that was accepted for this invoice. This will ONLY be set if\nthis invoice has been settled. We provide this field as if the invoice was\ncreated with a zero value, then we need to record what amount was\nultimately accepted. Additionally, it's possible that the sender paid MORE\nthat was specified in the original invoice. So we'll record that here as\nwell."
} }
} }
}, },

@ -639,9 +639,9 @@ func (hn *HarnessNode) lightningNetworkWatcher() {
go func() { go func() {
defer hn.wg.Done() defer hn.wg.Done()
ctxb := context.Background()
req := &lnrpc.GraphTopologySubscription{} req := &lnrpc.GraphTopologySubscription{}
topologyClient, err := hn.SubscribeChannelGraph(ctxb, req) ctx, cancelFunc := context.WithCancel(context.Background())
topologyClient, err := hn.SubscribeChannelGraph(ctx, req)
if err != nil { if err != nil {
// We panic here in case of an error as failure to // We panic here in case of an error as failure to
// create the topology client will cause all subsequent // create the topology client will cause all subsequent
@ -650,6 +650,8 @@ func (hn *HarnessNode) lightningNetworkWatcher() {
"client: %v", err)) "client: %v", err))
} }
defer cancelFunc()
for { for {
update, err := topologyClient.Recv() update, err := topologyClient.Recv()
if err == io.EOF { if err == io.EOF {

@ -2657,7 +2657,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
return nil, err return nil, err
} }
i := &channeldb.Invoice{ newInvoice := &channeldb.Invoice{
CreationDate: creationDate, CreationDate: creationDate,
Memo: []byte(invoice.Memo), Memo: []byte(invoice.Memo),
Receipt: invoice.Receipt, Receipt: invoice.Receipt,
@ -2666,22 +2666,24 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
Value: amtMSat, Value: amtMSat,
}, },
} }
copy(i.Terms.PaymentPreimage[:], paymentPreimage[:]) copy(newInvoice.Terms.PaymentPreimage[:], paymentPreimage[:])
rpcsLog.Tracef("[addinvoice] adding new invoice %v", rpcsLog.Tracef("[addinvoice] adding new invoice %v",
newLogClosure(func() string { newLogClosure(func() string {
return spew.Sdump(i) return spew.Sdump(newInvoice)
}), }),
) )
// With all sanity checks passed, write the invoice to the database. // With all sanity checks passed, write the invoice to the database.
if err := r.server.invoices.AddInvoice(i); err != nil { addIndex, err := r.server.invoices.AddInvoice(newInvoice)
if err != nil {
return nil, err return nil, err
} }
return &lnrpc.AddInvoiceResponse{ return &lnrpc.AddInvoiceResponse{
RHash: rHash[:], RHash: rHash[:],
PaymentRequest: payReqString, PaymentRequest: payReqString,
AddIndex: addIndex,
}, nil }, nil
} }
@ -2737,6 +2739,9 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
CltvExpiry: cltvExpiry, CltvExpiry: cltvExpiry,
FallbackAddr: fallbackAddr, FallbackAddr: fallbackAddr,
RouteHints: routeHints, RouteHints: routeHints,
AddIndex: invoice.AddIndex,
SettleIndex: invoice.SettleIndex,
AmtPaid: int64(invoice.AmtPaid),
}, nil }, nil
} }
@ -2833,7 +2838,7 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
invoices := make([]*lnrpc.Invoice, len(dbInvoices)) invoices := make([]*lnrpc.Invoice, len(dbInvoices))
for i, dbInvoice := range dbInvoices { for i, dbInvoice := range dbInvoices {
rpcInvoice, err := createRPCInvoice(dbInvoice) rpcInvoice, err := createRPCInvoice(&dbInvoice)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2851,14 +2856,24 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
updateStream lnrpc.Lightning_SubscribeInvoicesServer) error { updateStream lnrpc.Lightning_SubscribeInvoicesServer) error {
invoiceClient := r.server.invoices.SubscribeNotifications() invoiceClient := r.server.invoices.SubscribeNotifications(
req.AddIndex, req.SettleIndex,
)
defer invoiceClient.Cancel() defer invoiceClient.Cancel()
for { for {
select { select {
// TODO(roasbeef): include newly added invoices? case newInvoice := <-invoiceClient.NewInvoices:
case settledInvoice := <-invoiceClient.SettledInvoices: rpcInvoice, err := createRPCInvoice(newInvoice)
if err != nil {
return err
}
if err := updateStream.Send(rpcInvoice); err != nil {
return err
}
case settledInvoice := <-invoiceClient.SettledInvoices:
rpcInvoice, err := createRPCInvoice(settledInvoice) rpcInvoice, err := createRPCInvoice(settledInvoice)
if err != nil { if err != nil {
return err return err
@ -2867,6 +2882,7 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
if err := updateStream.Send(rpcInvoice); err != nil { if err := updateStream.Send(rpcInvoice); err != nil {
return err return err
} }
case <-r.quit: case <-r.quit:
return nil return nil
} }
@ -2899,6 +2915,7 @@ func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
if err := updateStream.Send(detail); err != nil { if err := updateStream.Send(detail); err != nil {
return err return err
} }
case tx := <-txClient.UnconfirmedTransactions(): case tx := <-txClient.UnconfirmedTransactions():
detail := &lnrpc.Transaction{ detail := &lnrpc.Transaction{
TxHash: tx.Hash.String(), TxHash: tx.Hash.String(),
@ -2909,6 +2926,7 @@ func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
if err := updateStream.Send(detail); err != nil { if err := updateStream.Send(detail); err != nil {
return err return err
} }
case <-r.quit: case <-r.quit:
return nil return nil
} }

@ -753,6 +753,10 @@ func (s *server) Start() error {
} }
s.connMgr.Start() s.connMgr.Start()
if err := s.invoices.Start(); err != nil {
return err
}
// With all the relevant sub-systems started, we'll now attempt to // With all the relevant sub-systems started, we'll now attempt to
// establish persistent connections to our direct channel collaborators // establish persistent connections to our direct channel collaborators
// within the network. // within the network.
@ -809,6 +813,7 @@ func (s *server) Stop() error {
s.cc.chainView.Stop() s.cc.chainView.Stop()
s.connMgr.Stop() s.connMgr.Stop()
s.cc.feeEstimator.Stop() s.cc.feeEstimator.Stop()
s.invoices.Stop()
// Disconnect from each active peers to ensure that // Disconnect from each active peers to ensure that
// peerTerminationWatchers signal completion to each peer. // peerTerminationWatchers signal completion to each peer.