diff --git a/channeldb/db.go b/channeldb/db.go index 0fa3953f..4c96123f 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -103,6 +103,13 @@ var ( number: 9, migration: migrateOutgoingPayments, }, + { + // The DB version where we started to store legacy + // payload information for all routes, as well as the + // optional TLV records. + number: 10, + migration: migrateRouteSerialization, + }, } // Big endian is the preferred byte order, due to cursor scans over diff --git a/channeldb/migration_10_route_tlv_records.go b/channeldb/migration_10_route_tlv_records.go new file mode 100644 index 00000000..2659c4a7 --- /dev/null +++ b/channeldb/migration_10_route_tlv_records.go @@ -0,0 +1,236 @@ +package channeldb + +import ( + "bytes" + "io" + + "github.com/coreos/bbolt" + "github.com/lightningnetwork/lnd/routing/route" +) + +// migrateRouteSerialization migrates the way we serialize routes across the +// entire database. At the time of writing of this migration, this includes our +// payment attempts, as well as the payment results in mission control. +func migrateRouteSerialization(tx *bbolt.Tx) error { + // First, we'll do all the payment attempts. + rootPaymentBucket := tx.Bucket(paymentsRootBucket) + if rootPaymentBucket == nil { + return nil + } + + // As we can't mutate a bucket while we're iterating over it with + // ForEach, we'll need to collect all the known payment hashes in + // memory first. + var payHashes [][]byte + err := rootPaymentBucket.ForEach(func(k, v []byte) error { + if v != nil { + return nil + } + + payHashes = append(payHashes, k) + return nil + }) + if err != nil { + return err + } + + // Now that we have all the payment hashes, we can carry out the + // migration itself. + for _, payHash := range payHashes { + payHashBucket := rootPaymentBucket.Bucket(payHash) + + // First, we'll migrate the main (non duplicate) payment to + // this hash. + err := migrateAttemptEncoding(tx, payHashBucket) + if err != nil { + return err + } + + // Now that we've migrated the main payment, we'll also check + // for any duplicate payments to the same payment hash. + dupBucket := payHashBucket.Bucket(paymentDuplicateBucket) + + // If there's no dup bucket, then we can move on to the next + // payment. + if dupBucket == nil { + continue + } + + // Otherwise, we'll now iterate through all the duplicate pay + // hashes and migrate those. + var dupSeqNos [][]byte + err = dupBucket.ForEach(func(k, v []byte) error { + dupSeqNos = append(dupSeqNos, k) + return nil + }) + if err != nil { + return err + } + + // Now in this second pass, we'll re-serialize their duplicate + // payment attempts under the new encoding. + for _, seqNo := range dupSeqNos { + dupPayHashBucket := dupBucket.Bucket(seqNo) + err := migrateAttemptEncoding(tx, dupPayHashBucket) + if err != nil { + return err + } + } + } + + log.Infof("Migration of route/hop serialization complete!") + + log.Infof("Migrating to new mission control store by clearing " + + "existing data") + + resultsKey := []byte("missioncontrol-results") + err = tx.DeleteBucket(resultsKey) + if err != nil && err != bbolt.ErrBucketNotFound { + return err + } + + log.Infof("Migration to new mission control completed!") + + return nil +} + +// migrateAttemptEncoding migrates payment attempts using the legacy format to +// the new format. +func migrateAttemptEncoding(tx *bbolt.Tx, payHashBucket *bbolt.Bucket) error { + payAttemptBytes := payHashBucket.Get(paymentAttemptInfoKey) + if payAttemptBytes == nil { + return nil + } + + // For our migration, we'll first read out the existing payment attempt + // using the legacy serialization of the attempt. + payAttemptReader := bytes.NewReader(payAttemptBytes) + payAttempt, err := deserializePaymentAttemptInfoLegacy( + payAttemptReader, + ) + if err != nil { + return err + } + + // Now that we have the old attempts, we'll explicitly mark this as + // needing a legacy payload, since after this migration, the modern + // payload will be the default if signalled. + for _, hop := range payAttempt.Route.Hops { + hop.LegacyPayload = true + } + + // Finally, we'll write out the payment attempt using the new encoding. + var b bytes.Buffer + err = serializePaymentAttemptInfo(&b, payAttempt) + if err != nil { + return err + } + + return payHashBucket.Put(paymentAttemptInfoKey, b.Bytes()) +} + +func deserializePaymentAttemptInfoLegacy(r io.Reader) (*PaymentAttemptInfo, error) { + a := &PaymentAttemptInfo{} + err := ReadElements(r, &a.PaymentID, &a.SessionKey) + if err != nil { + return nil, err + } + a.Route, err = deserializeRouteLegacy(r) + if err != nil { + return nil, err + } + return a, nil +} + +func serializePaymentAttemptInfoLegacy(w io.Writer, a *PaymentAttemptInfo) error { + if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil { + return err + } + + if err := serializeRouteLegacy(w, a.Route); err != nil { + return err + } + + return nil +} + +func deserializeHopLegacy(r io.Reader) (*route.Hop, error) { + h := &route.Hop{} + + var pub []byte + if err := ReadElements(r, &pub); err != nil { + return nil, err + } + copy(h.PubKeyBytes[:], pub) + + if err := ReadElements(r, + &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward, + ); err != nil { + return nil, err + } + + return h, nil +} + +func serializeHopLegacy(w io.Writer, h *route.Hop) error { + if err := WriteElements(w, + h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock, + h.AmtToForward, + ); err != nil { + return err + } + + return nil +} + +func deserializeRouteLegacy(r io.Reader) (route.Route, error) { + rt := route.Route{} + if err := ReadElements(r, + &rt.TotalTimeLock, &rt.TotalAmount, + ); err != nil { + return rt, err + } + + var pub []byte + if err := ReadElements(r, &pub); err != nil { + return rt, err + } + copy(rt.SourcePubKey[:], pub) + + var numHops uint32 + if err := ReadElements(r, &numHops); err != nil { + return rt, err + } + + var hops []*route.Hop + for i := uint32(0); i < numHops; i++ { + hop, err := deserializeHopLegacy(r) + if err != nil { + return rt, err + } + hops = append(hops, hop) + } + rt.Hops = hops + + return rt, nil +} + +func serializeRouteLegacy(w io.Writer, r route.Route) error { + if err := WriteElements(w, + r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:], + ); err != nil { + return err + } + + if err := WriteElements(w, uint32(len(r.Hops))); err != nil { + return err + } + + for _, h := range r.Hops { + if err := serializeHopLegacy(w, h); err != nil { + return err + } + } + + return nil +} diff --git a/channeldb/migrations_test.go b/channeldb/migrations_test.go index aee98629..13991d6e 100644 --- a/channeldb/migrations_test.go +++ b/channeldb/migrations_test.go @@ -5,14 +5,18 @@ import ( "crypto/sha256" "encoding/binary" "fmt" + "math/rand" "reflect" "testing" + "time" "github.com/btcsuite/btcutil" "github.com/coreos/bbolt" "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" ) // TestPaymentStatusesMigration checks that already completed payments will have @@ -723,3 +727,223 @@ func TestOutgoingPaymentsMigration(t *testing.T) { migrateOutgoingPayments, false) } + +func makeRandPaymentCreationInfo() (*PaymentCreationInfo, error) { + var payHash lntypes.Hash + if _, err := rand.Read(payHash[:]); err != nil { + return nil, err + } + + return &PaymentCreationInfo{ + PaymentHash: payHash, + Value: lnwire.MilliSatoshi(rand.Int63()), + CreationDate: time.Now(), + PaymentRequest: []byte("test"), + }, nil +} + +// TestPaymentRouteSerialization tests that we're able to properly migrate +// existing payments on disk that contain the traversed routes to the new +// routing format which supports the TLV payloads. We also test that the +// migration is able to handle duplicate payment attempts. +func TestPaymentRouteSerialization(t *testing.T) { + t.Parallel() + + legacyHop1 := &route.Hop{ + PubKeyBytes: route.NewVertex(pub), + ChannelID: 12345, + OutgoingTimeLock: 111, + LegacyPayload: true, + AmtToForward: 555, + } + legacyHop2 := &route.Hop{ + PubKeyBytes: route.NewVertex(pub), + ChannelID: 12345, + OutgoingTimeLock: 111, + LegacyPayload: true, + AmtToForward: 555, + } + legacyRoute := route.Route{ + TotalTimeLock: 123, + TotalAmount: 1234567, + SourcePubKey: route.NewVertex(pub), + Hops: []*route.Hop{legacyHop1, legacyHop2}, + } + + const numPayments = 4 + var oldPayments []*Payment + + sharedPayAttempt := PaymentAttemptInfo{ + PaymentID: 1, + SessionKey: priv, + Route: legacyRoute, + } + + // We'll first add a series of fake payments, using the existing legacy + // serialization format. + beforeMigrationFunc := func(d *DB) { + err := d.Update(func(tx *bbolt.Tx) error { + paymentsBucket, err := tx.CreateBucket( + paymentsRootBucket, + ) + if err != nil { + t.Fatalf("unable to create new payments "+ + "bucket: %v", err) + } + + for i := 0; i < numPayments; i++ { + var seqNum [8]byte + byteOrder.PutUint64(seqNum[:], uint64(i)) + + // All payments will be randomly generated, + // other than the final payment. We'll force + // the final payment to re-use an existing + // payment hash so we can insert it into the + // duplicate payment hash bucket. + var payInfo *PaymentCreationInfo + if i < numPayments-1 { + payInfo, err = makeRandPaymentCreationInfo() + if err != nil { + t.Fatalf("unable to create "+ + "payment: %v", err) + } + } else { + payInfo = oldPayments[0].Info + } + + // Next, legacy encoded when needed, we'll + // serialize the info and the attempt. + var payInfoBytes bytes.Buffer + err = serializePaymentCreationInfo( + &payInfoBytes, payInfo, + ) + if err != nil { + t.Fatalf("unable to encode pay "+ + "info: %v", err) + } + var payAttemptBytes bytes.Buffer + err = serializePaymentAttemptInfoLegacy( + &payAttemptBytes, &sharedPayAttempt, + ) + if err != nil { + t.Fatalf("unable to encode payment attempt: "+ + "%v", err) + } + + // Before we write to disk, we'll need to fetch + // the proper bucket. If this is the duplicate + // payment, then we'll grab the dup bucket, + // otherwise, we'll use the top level bucket. + var payHashBucket *bbolt.Bucket + if i < numPayments-1 { + payHashBucket, err = paymentsBucket.CreateBucket( + payInfo.PaymentHash[:], + ) + } else { + payHashBucket = paymentsBucket.Bucket( + payInfo.PaymentHash[:], + ) + dupPayBucket, err := payHashBucket.CreateBucket( + paymentDuplicateBucket, + ) + if err != nil { + t.Fatalf("unable to create "+ + "dup hash bucket: %v", err) + } + + payHashBucket, err = dupPayBucket.CreateBucket( + seqNum[:], + ) + if err != nil { + t.Fatalf("unable to make dup "+ + "bucket: %v", err) + } + } + + err = payHashBucket.Put(paymentSequenceKey, seqNum[:]) + if err != nil { + t.Fatalf("unable to write seqno: %v", err) + } + + err = payHashBucket.Put( + paymentCreationInfoKey, payInfoBytes.Bytes(), + ) + if err != nil { + t.Fatalf("unable to write creation "+ + "info: %v", err) + } + + err = payHashBucket.Put( + paymentAttemptInfoKey, payAttemptBytes.Bytes(), + ) + if err != nil { + t.Fatalf("unable to write attempt "+ + "info: %v", err) + } + + oldPayments = append(oldPayments, &Payment{ + Info: payInfo, + Attempt: &sharedPayAttempt, + }) + } + + return nil + }) + if err != nil { + t.Fatalf("unable to create test payments: %v", err) + } + } + + afterMigrationFunc := func(d *DB) { + newPayments, err := d.FetchPayments() + if err != nil { + t.Fatalf("unable to fetch new payments: %v", err) + } + + if len(newPayments) != numPayments { + t.Fatalf("expected %d payments, got %d", numPayments, + len(newPayments)) + } + + for i, p := range newPayments { + // Order of payments should be be preserved. + old := oldPayments[i] + + if p.Attempt.PaymentID != old.Attempt.PaymentID { + t.Fatalf("wrong pay ID: expected %v, got %v", + p.Attempt.PaymentID, + old.Attempt.PaymentID) + } + + if p.Attempt.Route.TotalFees() != old.Attempt.Route.TotalFees() { + t.Fatalf("Fee mismatch") + } + + if p.Attempt.Route.TotalAmount != old.Attempt.Route.TotalAmount { + t.Fatalf("Total amount mismatch") + } + + if p.Attempt.Route.TotalTimeLock != old.Attempt.Route.TotalTimeLock { + t.Fatalf("timelock mismatch") + } + + if p.Attempt.Route.SourcePubKey != old.Attempt.Route.SourcePubKey { + t.Fatalf("source mismatch: %x vs %x", + p.Attempt.Route.SourcePubKey[:], + old.Attempt.Route.SourcePubKey[:]) + } + + for i, hop := range p.Attempt.Route.Hops { + if !reflect.DeepEqual(hop, legacyRoute.Hops[i]) { + t.Fatalf("hop mismatch") + } + } + } + } + + applyMigration(t, + beforeMigrationFunc, + afterMigrationFunc, + migrateRouteSerialization, + false) +} diff --git a/channeldb/payment_control_test.go b/channeldb/payment_control_test.go index cc4ae492..b51c9bdf 100644 --- a/channeldb/payment_control_test.go +++ b/channeldb/payment_control_test.go @@ -15,6 +15,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" ) func initDB() (*DB, error) { @@ -137,8 +138,37 @@ func TestPaymentControlSwitchFail(t *testing.T) { if err != nil { t.Fatalf("error shouldn't have been received, got: %v", err) } + + err = assertRouteHopRecordsEqual(route, &attempt.Route) + if err != nil { + t.Fatalf("route tlv records not equal: %v", err) + } + + for i := 0; i < len(route.Hops); i++ { + for j := 0; j < len(route.Hops[i].TLVRecords); j++ { + expectedRecord := route.Hops[i].TLVRecords[j] + newRecord := attempt.Route.Hops[i].TLVRecords[j] + + err := assertHopRecordsEqual(expectedRecord, newRecord) + if err != nil { + t.Fatalf("route record mismatch: %v", err) + } + } + } + + for i := 0; i < len(route.Hops); i++ { + // reflect.DeepEqual can't assert that two function closures + // are equal. The underlying tlv.Record uses function closures + // internally, so after we verify that the records match above + // manually, we unset these so we can use reflect.DeepEqual + // below. + route.Hops[i].TLVRecords = nil + attempt.Route.Hops[i].TLVRecords = nil + } + if !reflect.DeepEqual(*route, attempt.Route) { - t.Fatalf("unexpected route returned") + t.Fatalf("unexpected route returned: %v vs %v", + spew.Sdump(attempt.Route), spew.Sdump(*route)) } assertPaymentStatus(t, db, info.PaymentHash, StatusSucceeded) @@ -427,7 +457,6 @@ func checkPaymentCreationInfo(bucket *bbolt.Bucket, c *PaymentCreationInfo) erro r := bytes.NewReader(b) c2, err := deserializePaymentCreationInfo(r) if err != nil { - fmt.Println("creation info err: ", err) return err } if !reflect.DeepEqual(c, c2) { @@ -454,11 +483,34 @@ func checkPaymentAttemptInfo(bucket *bbolt.Bucket, a *PaymentAttemptInfo) error if err != nil { return err } + + err = assertRouteHopRecordsEqual(&a.Route, &a2.Route) + if err != nil { + return err + } + + recordCache := make(map[int][]tlv.Record) + for i := 0; i < len(a.Route.Hops); i++ { + recordCache[i] = a.Route.Hops[i].TLVRecords + + // reflect.DeepEqual can't assert that two function closures + // are equal. The underlying tlv.Record uses function closures + // internally, so after we verify that the records match above + // manually, we unset these so we can use reflect.DeepEqual + // below. + a.Route.Hops[i].TLVRecords = nil + a2.Route.Hops[i].TLVRecords = nil + } + if !reflect.DeepEqual(a, a2) { return fmt.Errorf("PaymentAttemptInfos don't match: %v vs %v", spew.Sdump(a), spew.Sdump(a2)) } + for index, records := range recordCache { + a.Route.Hops[index].TLVRecords = records + } + return nil } diff --git a/channeldb/payments.go b/channeldb/payments.go index 8a0f04a0..00d29366 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -10,10 +10,12 @@ import ( "time" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" "github.com/coreos/bbolt" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" ) var ( @@ -512,9 +514,47 @@ func serializeHop(w io.Writer, h *route.Hop) error { return err } + if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil { + return err + } + + // For legacy payloads, we don't need to write any TLV records, so + // we'll write a zero indicating the our serialized TLV map has no + // records. + if h.LegacyPayload { + return WriteElements(w, uint32(0)) + } + + // Otherwise, we'll transform our slice of records into a map of the + // raw bytes, then serialize them in-line with a length (number of + // elements) prefix. + mapRecords, err := tlv.RecordsToMap(h.TLVRecords) + if err != nil { + return err + } + + numRecords := uint32(len(mapRecords)) + if err := WriteElements(w, numRecords); err != nil { + return err + } + + for recordType, rawBytes := range mapRecords { + if err := WriteElements(w, recordType); err != nil { + return err + } + + if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil { + return err + } + } + return nil } +// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need +// to read/write a TLV stream larger than this. +const maxOnionPayloadSize = 1300 + func deserializeHop(r io.Reader) (*route.Hop, error) { h := &route.Hop{} @@ -530,6 +570,47 @@ func deserializeHop(r io.Reader) (*route.Hop, error) { return nil, err } + // TODO(roasbeef): change field to allow LegacyPayload false to be the + // legacy default? + err := binary.Read(r, byteOrder, &h.LegacyPayload) + if err != nil { + return nil, err + } + + var numElements uint32 + if err := ReadElements(r, &numElements); err != nil { + return nil, err + } + + // If there're no elements, then we can return early. + if numElements == 0 { + return h, nil + } + + tlvMap := make(map[uint64][]byte) + for i := uint32(0); i < numElements; i++ { + var tlvType uint64 + if err := ReadElements(r, &tlvType); err != nil { + return nil, err + } + + rawRecordBytes, err := wire.ReadVarBytes( + r, 0, maxOnionPayloadSize, "tlv", + ) + if err != nil { + return nil, err + } + + tlvMap[tlvType] = rawRecordBytes + } + + tlvRecords, err := tlv.MapToRecords(tlvMap) + if err != nil { + return nil, err + } + + h.TLVRecords = tlvRecords + return h, nil } diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index a12cf65e..a1ba6fdd 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -13,17 +13,32 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" ) var ( priv, _ = btcec.NewPrivateKey(btcec.S256()) pub = priv.PubKey() - testHop = &route.Hop{ + tlvBytes = []byte{1, 2, 3} + tlvEncoder = tlv.StubEncoder(tlvBytes) + testHop1 = &route.Hop{ PubKeyBytes: route.NewVertex(pub), ChannelID: 12345, OutgoingTimeLock: 111, AmtToForward: 555, + TLVRecords: []tlv.Record{ + tlv.MakeStaticRecord(1, nil, 3, tlvEncoder, nil), + tlv.MakeStaticRecord(2, nil, 3, tlvEncoder, nil), + }, + } + + testHop2 = &route.Hop{ + PubKeyBytes: route.NewVertex(pub), + ChannelID: 12345, + OutgoingTimeLock: 111, + AmtToForward: 555, + LegacyPayload: true, } testRoute = route.Route{ @@ -31,8 +46,8 @@ var ( TotalAmount: 1234567, SourcePubKey: route.NewVertex(pub), Hops: []*route.Hop{ - testHop, - testHop, + testHop1, + testHop2, }, } ) @@ -191,6 +206,8 @@ func TestSentPaymentSerialization(t *testing.T) { } if !reflect.DeepEqual(s, newAttemptInfo) { + s.SessionKey.Curve = nil + newAttemptInfo.SessionKey.Curve = nil t.Fatalf("Payments do not match after "+ "serialization/deserialization %v vs %v", spew.Sdump(s), spew.Sdump(newAttemptInfo), @@ -199,6 +216,46 @@ func TestSentPaymentSerialization(t *testing.T) { } +func assertRouteHopRecordsEqual(r1, r2 *route.Route) error { + for i := 0; i < len(r1.Hops); i++ { + for j := 0; j < len(r1.Hops[i].TLVRecords); j++ { + expectedRecord := r1.Hops[i].TLVRecords[j] + newRecord := r2.Hops[i].TLVRecords[j] + + err := assertHopRecordsEqual(expectedRecord, newRecord) + if err != nil { + return fmt.Errorf("route record mismatch: %v", err) + } + } + } + + return nil +} + +func assertHopRecordsEqual(h1, h2 tlv.Record) error { + if h1.Type() != h2.Type() { + return fmt.Errorf("wrong type: expected %v, got %v", h1.Type(), + h2.Type()) + } + + var b bytes.Buffer + if err := h2.Encode(&b); err != nil { + return fmt.Errorf("unable to encode record: %v", err) + } + + if !bytes.Equal(b.Bytes(), tlvBytes) { + return fmt.Errorf("wrong raw record: expected %x, got %x", + tlvBytes, b.Bytes()) + } + + if h1.Size() != h2.Size() { + return fmt.Errorf("wrong size: expected %v, "+ + "got %v", h1.Size(), h2.Size()) + } + + return nil +} + func TestRouteSerialization(t *testing.T) { t.Parallel() @@ -213,9 +270,23 @@ func TestRouteSerialization(t *testing.T) { t.Fatal(err) } + // First we verify all the records match up porperly, as they aren't + // able to be properly compared using reflect.DeepEqual. + err = assertRouteHopRecordsEqual(&testRoute, &route2) + if err != nil { + t.Fatalf("route tlv records don't match: %v", err) + } + + // Now that we know the records match up, we'll examine the remainder + // of the route without the TLV records attached as reflect.DeepEqual + // can't properly assert their equality. + testRoute.Hops[0].TLVRecords = nil + testRoute.Hops[1].TLVRecords = nil + route2.Hops[0].TLVRecords = nil + route2.Hops[1].TLVRecords = nil + if !reflect.DeepEqual(testRoute, route2) { t.Fatalf("routes not equal: \n%v vs \n%v", spew.Sdump(testRoute), spew.Sdump(route2)) } - } diff --git a/config.go b/config.go index b22cbf2f..fdb4344d 100644 --- a/config.go +++ b/config.go @@ -328,6 +328,8 @@ type config struct { WtClient *lncfg.WtClient `group:"wtclient" namespace:"wtclient"` Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"` + + LegacyProtocol *lncfg.LegacyProtocol `group:"legacyprotocol" namespace:"legacyprotocol"` } // loadConfig initializes and parses the config using a config file and command diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index 9af694d6..54bccab5 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -166,7 +166,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) { // identical to HTLC resolution in the link. event, err := h.Registry.NotifyExitHopHtlc( h.payHash, h.htlcAmt, h.htlcExpiry, currentHeight, - hodlChan, + hodlChan, nil, ) switch err { case channeldb.ErrInvoiceNotFound: diff --git a/contractcourt/interfaces.go b/contractcourt/interfaces.go index a18ee3d6..3333e6b6 100644 --- a/contractcourt/interfaces.go +++ b/contractcourt/interfaces.go @@ -22,7 +22,8 @@ type Registry interface { // the resolution is sent on the passed in hodlChan later. NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, - hodlChan chan<- interface{}) (*invoices.HodlEvent, error) + hodlChan chan<- interface{}, + eob []byte) (*invoices.HodlEvent, error) // HodlUnsubscribeAll unsubscribes from all hodl events. HodlUnsubscribeAll(subscriber chan<- interface{}) diff --git a/contractcourt/mock_registry_test.go b/contractcourt/mock_registry_test.go index f54a1465..288ea5ba 100644 --- a/contractcourt/mock_registry_test.go +++ b/contractcourt/mock_registry_test.go @@ -23,7 +23,7 @@ type mockRegistry struct { func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, - hodlChan chan<- interface{}) (*invoices.HodlEvent, error) { + hodlChan chan<- interface{}, eob []byte) (*invoices.HodlEvent, error) { r.notifyChan <- notifyExitHopData{ hodlChan: hodlChan, diff --git a/go.mod b/go.mod index d04b0f83..2d34d4ac 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,7 @@ module github.com/lightningnetwork/lnd require ( - git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e // indirect - github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0 // indirect + github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2 github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8 @@ -21,22 +20,19 @@ require ( github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad github.com/jessevdk/go-flags v1.4.0 github.com/jrick/logrotate v1.0.0 - github.com/juju/clock v0.0.0-20180808021310-bab88fc67299 // indirect - github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 // indirect - github.com/juju/loggo v0.0.0-20180524022052-584905176618 // indirect - github.com/juju/retry v0.0.0-20180821225755-9058e192b216 // indirect - github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 // indirect - github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d // indirect - github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect + github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c // indirect + github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d // indirect + github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect + github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec github.com/lightninglabs/neutrino v0.0.0-20190725230401-ddf667a8b5c4 - github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9 + github.com/lightningnetwork/lightning-onion v0.0.0-20190808221659-0c0c9dbf17ea github.com/lightningnetwork/lnd/queue v1.0.1 github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 github.com/prometheus/client_golang v0.9.3 - github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af // indirect + github.com/rogpeppe/fastuuid v1.2.0 // indirect github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 github.com/urfave/cli v1.18.0 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 @@ -44,10 +40,9 @@ require ( golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 google.golang.org/grpc v1.18.0 - gopkg.in/errgo.v1 v1.0.0 // indirect + gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/macaroon-bakery.v2 v2.0.1 gopkg.in/macaroon.v2 v2.0.0 - gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect ) replace github.com/lightningnetwork/lnd/ticker => ./ticker diff --git a/go.sum b/go.sum index f24783c0..5e39c79e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0 h1:g/ETZwHx5wN2fqKWS3gCUrEU7dLko+DvVs3hakQCfyE= github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= +github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4= +github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -20,18 +21,14 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd v0.0.0-20180823030728-d81d8877b8f3/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20181130015935-7d2daa5bfef2/go.mod h1:Jr9bmNVGZ7TH2Ux1QuP0ec+yGgh0gE9FIlkzQiI5bR0= -github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS36cXdAzJbeHaduLtnLQQwNoIi78= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= -github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c h1:aEbSeNALREWXk0G7UdNhR3ayBV7tZ4M2PNmnrCAph6Q= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50 h1:4i3KsuVA0o0KoBxAC5x+MY7RbteiMK1V7gf/G08NGIQ= github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8 h1:mOg8/RgDSHTQ1R0IR+LMDuW4TDShPv+JzYHuR4GLoNA= github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803 h1:j3AgPKKZtZStM2nyhrDSLSYgT7YHrZKdSkq1OYeLjvM= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -46,11 +43,9 @@ github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JG github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8 h1:nOsAWScwueMVk/VLm/dvQQD7DuanyvAUb6B3P3eT274= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= @@ -60,7 +55,6 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v0.0.0-20180223184059-7ee3ded59d4835e10f3e7d0f7603c42aa5e83820/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -68,6 +62,8 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/frankban/quicktest v1.2.2 h1:xfmOhhoH5fGPgbEAlhLpJH9p0z/0Qizio9osmvn9IUY= +github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -82,10 +78,11 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20180821051752-b27b920f9e71/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42 h1:q3pnF5JFBNRz8sRD+IRj7Y6DMyYGTNqnZ9axTbSfoNI= +github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -103,16 +100,20 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/juju/clock v0.0.0-20180808021310-bab88fc67299 h1:K9nBHQ3UNqg/HhZkQnGG2AE4YxDyNmGS9FFT2gGegLQ= github.com/juju/clock v0.0.0-20180808021310-bab88fc67299/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= +github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c h1:3UvYABOQRhJAApj9MdCN+Ydv841ETSoy6xLzdmmr/9A= +github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618 h1:MK144iBQF9hTSwBW/9eJm034bVoG30IshVm688T2hi8= +github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d h1:hJXjZMxj0SWlMoQkzeZDLi2cmeiWKa7y1B8Rg+qaoEc= +github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/retry v0.0.0-20180821225755-9058e192b216 h1:/eQL7EJQKFHByJe3DeE8Z36yqManj9UY5zppDoQi4FU= github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs= github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 h1:Pp8RxiF4rSoXP9SED26WCfNB28/dwTDpPXS8XMJR8rc= +github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d h1:irPlN9z5VCe6BTsqVsxheCZH99OFSmqSVyTigW4mEoY= github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= github.com/juju/version v0.0.0-20180108022336-b64dbd566305 h1:lQxPJ1URr2fjsKnJRt/BxiIxjLt9IKGvS+0injMHbag= @@ -134,12 +135,13 @@ github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d h1:tt8hwvxl6fk github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/neutrino v0.0.0-20181017011010-4d6069299130/go.mod h1:KJq43Fu9ceitbJsSXMILcT4mGDNI/crKmPIkDOZXFyM= github.com/lightninglabs/neutrino v0.0.0-20190213031021-ae4583a89cfb/go.mod h1:g6cMQd+hfAU8pQTJAdjm6/EQREhupyd22f+CL0qYFOE= -github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18 h1:lxD7RgKYrjRRht6Cn1AiBPn4Rjgr5pkKSRxTs++EgaI= github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18/go.mod h1:v6tz6jbuAubTrRpX8ke2KH9sJxml8KlPQTKgo9mAp1Q= github.com/lightninglabs/neutrino v0.0.0-20190725230401-ddf667a8b5c4 h1:Yq3usMeTtJyRHFRJQsVqmr5oJTFm6uhZdKL2/YhoVrA= github.com/lightninglabs/neutrino v0.0.0-20190725230401-ddf667a8b5c4/go.mod h1:vzLU75ll8qbRJIzW5dvK/UXtR9c2FecJ6VNOM8chyVM= -github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9 h1:u6dbtgPtilk/HWg9GwA8GniHqzCW/7an3ZSpZARfHx4= github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM= +github.com/lightningnetwork/lightning-onion v0.0.0-20190808221659-0c0c9dbf17ea h1:ER+s5SmDJZeVp2WG6NFg9HuDzaMaEna5I3iQwMU9SEk= +github.com/lightningnetwork/lightning-onion v0.0.0-20190808221659-0c0c9dbf17ea/go.mod h1:kuepZyHzr5O4sYQ/Y5yLYXWz+GoHZm3N+M+Jri9Hb64= +github.com/lightningnetwork/lnd v0.7.1-beta.0.20190807225126-ea77ff91c221/go.mod h1:F6I/YZmW3e+kVRgQtCLqvLzBFl+tRCyoT8nolFeqQOo= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= @@ -171,8 +173,9 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -183,7 +186,6 @@ github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S0 github.com/urfave/cli v1.18.0 h1:m9MfmZWX7bwr9kUcs/Asr95j0IVXzGNNc+/5ku2m26Q= github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -199,16 +201,13 @@ golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953 h1:LuZIitY8waaxUfNIdtajyE/YzA/zyf0YxXG27VpLrkg= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80= golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -219,7 +218,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -231,7 +229,6 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -239,8 +236,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v1 v1.0.0 h1:n+7XfCyygBFb8sEjg6692xjC6Us50TFRO54+xYUEwjE= gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk= +gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso= +gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/macaroon-bakery.v2 v2.0.1 h1:0N1TlEdfLP4HXNCg7MQUMp5XwvOoxk+oe9Owr2cpvsc= @@ -251,7 +249,6 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 4dff21a5..52a4c194 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -23,10 +23,13 @@ type InvoiceDatabase interface { // invoice is a debug invoice, then this method is a noop as debug // invoices are never fully settled. The return value describes how the // htlc should be resolved. If the htlc cannot be resolved immediately, - // the resolution is sent on the passed in hodlChan later. + // the resolution is sent on the passed in hodlChan later. The eob + // field passes the entire onion hop payload into the invoice registry + // for decoding purposes. NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, - hodlChan chan<- interface{}) (*invoices.HodlEvent, error) + hodlChan chan<- interface{}, + eob []byte) (*invoices.HodlEvent, error) // CancelInvoice attempts to cancel the invoice corresponding to the // passed payment hash. diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index a068000d..6e3ae07d 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -1,12 +1,15 @@ package htlcswitch import ( + "bytes" "encoding/binary" + "fmt" "io" "github.com/btcsuite/btcd/btcec" "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" ) // NetworkHop indicates the blockchain network that is intended to be the next @@ -85,7 +88,10 @@ type HopIterator interface { // Additionally, the information encoded within the returned // ForwardingInfo is to be used by each hop to authenticate the // information given to it by the prior hop. - ForwardingInstructions() ForwardingInfo + ForwardingInstructions() (ForwardingInfo, error) + + // ExtraOnionBlob returns the additional EOB data (if available). + ExtraOnionBlob() []byte // EncodeNextHop encodes the onion packet destined for the next hop // into the passed io.Writer. @@ -139,24 +145,79 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { // hop to authenticate the information given to it by the prior hop. // // NOTE: Part of the HopIterator interface. -func (r *sphinxHopIterator) ForwardingInstructions() ForwardingInfo { - fwdInst := r.processedPacket.ForwardingInstructions +func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { + var ( + nextHop lnwire.ShortChannelID + amt uint64 + cltv uint32 + ) - var nextHop lnwire.ShortChannelID - switch r.processedPacket.Action { - case sphinx.ExitNode: - nextHop = exitHop - case sphinx.MoreHops: - s := binary.BigEndian.Uint64(fwdInst.NextAddress[:]) - nextHop = lnwire.NewShortChanIDFromInt(s) + switch r.processedPacket.Payload.Type { + // If this is the legacy payload, then we'll extract the information + // directly from the pre-populated ForwardingInstructions field. + case sphinx.PayloadLegacy: + fwdInst := r.processedPacket.ForwardingInstructions + + switch r.processedPacket.Action { + case sphinx.ExitNode: + nextHop = exitHop + case sphinx.MoreHops: + s := binary.BigEndian.Uint64(fwdInst.NextAddress[:]) + nextHop = lnwire.NewShortChanIDFromInt(s) + } + + amt = fwdInst.ForwardAmount + cltv = fwdInst.OutgoingCltv + + // Otherwise, if this is the TLV payload, then we'll make a new stream + // to decode only what we need to make routing decisions. + case sphinx.PayloadTLV: + var cid uint64 + + tlvStream, err := tlv.NewStream( + tlv.MakeDynamicRecord( + tlv.AmtOnionType, &amt, nil, + tlv.ETUint64, tlv.DTUint64, + ), + tlv.MakeDynamicRecord( + tlv.LockTimeOnionType, &cltv, nil, + tlv.ETUint32, tlv.DTUint32, + ), + tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &cid), + ) + if err != nil { + return ForwardingInfo{}, err + } + + err = tlvStream.Decode(bytes.NewReader( + r.processedPacket.Payload.Payload, + )) + if err != nil { + return ForwardingInfo{}, err + } + + nextHop = lnwire.NewShortChanIDFromInt(cid) + + default: + return ForwardingInfo{}, fmt.Errorf("unknown sphinx payload "+ + "type: %v", r.processedPacket.Payload.Type) } return ForwardingInfo{ Network: BitcoinHop, NextHop: nextHop, - AmountToForward: lnwire.MilliSatoshi(fwdInst.ForwardAmount), - OutgoingCTLV: fwdInst.OutgoingCltv, + AmountToForward: lnwire.MilliSatoshi(amt), + OutgoingCTLV: cltv, + }, nil +} + +// ExtraOnionBlob returns the additional EOB data (if available). +func (r *sphinxHopIterator) ExtraOnionBlob() []byte { + if r.processedPacket.Payload.Type == sphinx.PayloadLegacy { + return nil } + + return r.processedPacket.Payload.Payload } // ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop, diff --git a/htlcswitch/iterator_test.go b/htlcswitch/iterator_test.go new file mode 100644 index 00000000..01c28ed9 --- /dev/null +++ b/htlcswitch/iterator_test.go @@ -0,0 +1,109 @@ +package htlcswitch + +import ( + "bytes" + "encoding/binary" + "testing" + + "github.com/davecgh/go-spew/spew" + sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// TestSphinxHopIteratorForwardingInstructions tests that we're able to +// properly decode an onion payload, no matter the payload type, into the +// original set of forwarding instructions. +func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { + t.Parallel() + + // First, we'll make the hop data that the sender would create to send + // an HTLC through our imaginary route. + hopData := sphinx.HopData{ + ForwardAmount: 100000, + OutgoingCltv: 4343, + } + copy(hopData.NextAddress[:], bytes.Repeat([]byte("a"), 8)) + + // Next, we'll make the hop forwarding information that we should + // extract each type, no matter the payload type. + nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:]) + expectedFwdInfo := ForwardingInfo{ + NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt), + AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount), + OutgoingCTLV: hopData.OutgoingCltv, + } + + // For our TLV payload, we'll serialize the hop into into a TLV stream + // as we would normally in the routing network. + var b bytes.Buffer + tlvRecords := []tlv.Record{ + tlv.MakeDynamicRecord( + tlv.AmtOnionType, &hopData.ForwardAmount, func() uint64 { + return tlv.SizeTUint64(hopData.ForwardAmount) + }, + tlv.ETUint64, tlv.DTUint64, + ), + tlv.MakeDynamicRecord( + tlv.LockTimeOnionType, &hopData.OutgoingCltv, func() uint64 { + return tlv.SizeTUint32(hopData.OutgoingCltv) + }, + tlv.ETUint32, tlv.DTUint32, + ), + tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &nextAddrInt), + } + tlvStream, err := tlv.NewStream(tlvRecords...) + if err != nil { + t.Fatalf("unable to create stream: %v", err) + } + if err := tlvStream.Encode(&b); err != nil { + t.Fatalf("unable to encode stream: %v", err) + } + + var testCases = []struct { + sphinxPacket *sphinx.ProcessedPacket + expectedFwdInfo ForwardingInfo + }{ + // A regular legacy payload that signals more hops. + { + sphinxPacket: &sphinx.ProcessedPacket{ + Payload: sphinx.HopPayload{ + Type: sphinx.PayloadLegacy, + }, + Action: sphinx.MoreHops, + ForwardingInstructions: &hopData, + }, + expectedFwdInfo: expectedFwdInfo, + }, + // A TLV payload, we can leave off the action as we'll always + // read the cid encoded. + { + sphinxPacket: &sphinx.ProcessedPacket{ + Payload: sphinx.HopPayload{ + Type: sphinx.PayloadTLV, + Payload: b.Bytes(), + }, + }, + expectedFwdInfo: expectedFwdInfo, + }, + } + + // Finally, we'll test that we get the same set of + // ForwardingInstructions for each payload type. + iterator := sphinxHopIterator{} + for i, testCase := range testCases { + iterator.processedPacket = testCase.sphinxPacket + + fwdInfo, err := iterator.ForwardingInstructions() + if err != nil { + t.Fatalf("#%v: unable to extract forwarding "+ + "instructions: %v", i, err) + } + + if fwdInfo != testCase.expectedFwdInfo { + t.Fatalf("#%v: wrong fwding info: expected %v, got %v", + i, spew.Sdump(testCase.expectedFwdInfo), + spew.Sdump(fwdInfo)) + } + } +} diff --git a/htlcswitch/link.go b/htlcswitch/link.go index bd79b621..7d14b7af 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2627,8 +2627,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // If we're unable to process the onion blob than we // should send the malformed htlc error to payment // sender. - l.sendMalformedHTLCError(pd.HtlcIndex, failureCode, - onionBlob[:], pd.SourceRef) + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, onionBlob[:], pd.SourceRef, + ) needUpdate = true log.Errorf("unable to decode onion "+ @@ -2638,11 +2639,29 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, heightNow := l.cfg.Switch.BestHeight() - fwdInfo := chanIterator.ForwardingInstructions() + fwdInfo, err := chanIterator.ForwardingInstructions() + if err != nil { + // If we're unable to process the onion payload, or we + // we received malformed TLV stream, then we should + // send an error back to the caller so the HTLC can be + // cancelled. + l.sendHTLCError( + pd.HtlcIndex, + lnwire.NewInvalidOnionVersion(onionBlob[:]), + obfuscator, pd.SourceRef, + ) + needUpdate = true + + log.Errorf("Unable to decode forwarding "+ + "instructions: %v", err) + continue + } + switch fwdInfo.NextHop { case exitHop: updated, err := l.processExitHop( pd, obfuscator, fwdInfo, heightNow, + chanIterator.ExtraOnionBlob(), ) if err != nil { l.fail(LinkFailureError{code: ErrInternalError}, @@ -2814,8 +2833,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // processExitHop handles an htlc for which this link is the exit hop. It // returns a boolean indicating whether the commitment tx needs an update. func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, - obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, heightNow uint32) ( - bool, error) { + obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, + heightNow uint32, eob []byte) (bool, error) { // If hodl.ExitSettle is requested, we will not validate the final hop's // ADD, nor will we settle the corresponding invoice or respond with the @@ -2861,7 +2880,7 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, event, err := l.cfg.Registry.NotifyExitHopHtlc( invoiceHash, pd.Amount, pd.Timeout, int32(heightNow), - l.hodlQueue.ChanIn(), + l.hodlQueue.ChanIn(), eob, ) switch err { diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 9c5335a7..073e825d 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -275,10 +275,14 @@ func newMockHopIterator(hops ...ForwardingInfo) HopIterator { return &mockHopIterator{hops: hops} } -func (r *mockHopIterator) ForwardingInstructions() ForwardingInfo { +func (r *mockHopIterator) ForwardingInstructions() (ForwardingInfo, error) { h := r.hops[0] r.hops = r.hops[1:] - return h + return h, nil +} + +func (r *mockHopIterator) ExtraOnionBlob() []byte { + return nil } func (r *mockHopIterator) ExtractErrorEncrypter( @@ -789,10 +793,10 @@ func (i *mockInvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash, amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32, - hodlChan chan<- interface{}) (*invoices.HodlEvent, error) { + hodlChan chan<- interface{}, eob []byte) (*invoices.HodlEvent, error) { event, err := i.registry.NotifyExitHopHtlc( - rhash, amt, expiry, currentHeight, hodlChan, + rhash, amt, expiry, currentHeight, hodlChan, eob, ) if err != nil { return nil, err diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 46166c4d..a5570a3c 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -489,7 +489,7 @@ func (i *InvoiceRegistry) checkHtlcParameters(invoice *channeldb.Invoice, // prevent deadlock. func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32, - hodlChan chan<- interface{}) (*HodlEvent, error) { + hodlChan chan<- interface{}, eob []byte) (*HodlEvent, error) { i.Lock() defer i.Unlock() diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go index ff3c04be..c72db33e 100644 --- a/invoices/invoiceregistry_test.go +++ b/invoices/invoiceregistry_test.go @@ -119,7 +119,7 @@ func TestSettleInvoice(t *testing.T) { // Settle invoice with a slightly higher amount. amtPaid := lnwire.MilliSatoshi(100500) _, err = registry.NotifyExitHopHtlc( - hash, amtPaid, testInvoiceExpiry, 0, hodlChan, + hash, amtPaid, testInvoiceExpiry, 0, hodlChan, nil, ) if err != nil { t.Fatal(err) @@ -155,6 +155,7 @@ func TestSettleInvoice(t *testing.T) { // restart. event, err := registry.NotifyExitHopHtlc( hash, amtPaid, testInvoiceExpiry, testCurrentHeight, hodlChan, + nil, ) if err != nil { t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) @@ -168,7 +169,7 @@ func TestSettleInvoice(t *testing.T) { // same. New HTLCs with a different amount should be rejected. event, err = registry.NotifyExitHopHtlc( hash, amtPaid+600, testInvoiceExpiry, testCurrentHeight, - hodlChan, + hodlChan, nil, ) if err != nil { t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) @@ -181,7 +182,7 @@ func TestSettleInvoice(t *testing.T) { // behaviour as settling with a higher amount. event, err = registry.NotifyExitHopHtlc( hash, amtPaid-600, testInvoiceExpiry, testCurrentHeight, - hodlChan, + hodlChan, nil, ) if err != nil { t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) @@ -304,7 +305,7 @@ func TestCancelInvoice(t *testing.T) { // succeed. hodlChan := make(chan interface{}) event, err := registry.NotifyExitHopHtlc( - hash, amt, testInvoiceExpiry, testCurrentHeight, hodlChan, + hash, amt, testInvoiceExpiry, testCurrentHeight, hodlChan, nil, ) if err != nil { t.Fatal("expected settlement of a canceled invoice to succeed") @@ -381,6 +382,7 @@ func TestHoldInvoice(t *testing.T) { // should be possible. event, err := registry.NotifyExitHopHtlc( hash, amtPaid, testInvoiceExpiry, testCurrentHeight, hodlChan, + nil, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -392,6 +394,7 @@ func TestHoldInvoice(t *testing.T) { // Test idempotency. event, err = registry.NotifyExitHopHtlc( hash, amtPaid, testInvoiceExpiry, testCurrentHeight, hodlChan, + nil, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -487,7 +490,7 @@ func TestUnknownInvoice(t *testing.T) { hodlChan := make(chan interface{}) amt := lnwire.MilliSatoshi(100000) _, err := registry.NotifyExitHopHtlc( - hash, amt, testInvoiceExpiry, testCurrentHeight, hodlChan, + hash, amt, testInvoiceExpiry, testCurrentHeight, hodlChan, nil, ) if err != channeldb.ErrInvoiceNotFound { t.Fatal("expected invoice not found error") diff --git a/lncfg/protocol_legacy_off.go b/lncfg/protocol_legacy_off.go new file mode 100644 index 00000000..6f5660e9 --- /dev/null +++ b/lncfg/protocol_legacy_off.go @@ -0,0 +1,16 @@ +// +build !dev + +package lncfg + +// LegacyProtocol is a struct that we use to be able to test backwards +// compatibility of protocol additions, while defaulting to the latest within +// lnd. +type LegacyProtocol struct { +} + +// LegacyOnion returns true if the old legacy onion format should be used when +// we're an intermediate or final hop. This controls if we set the +// TLVOnionPayloadOptional bit or not. +func (l *LegacyProtocol) LegacyOnion() bool { + return false +} diff --git a/lncfg/protocol_legacy_on.go b/lncfg/protocol_legacy_on.go new file mode 100644 index 00000000..52bd9388 --- /dev/null +++ b/lncfg/protocol_legacy_on.go @@ -0,0 +1,20 @@ +// +build dev + +package lncfg + +// LegacyProtocol is a struct that we use to be able to test backwards +// compatibility of protocol additions, while defaulting to the latest within +// lnd. +type LegacyProtocol struct { + // Onion if set to true, then we won't signal TLVOnionPayloadOptional. + // As a result, nodes that include us in the route won't use the new + // modern onion framing. + Onion bool `long:"onion" description:"force node to not advertise the new modern TLV onion format"` +} + +// LegacyOnion returns true if the old legacy onion format should be used when +// we're an intermediate or final hop. This controls if we set the +// TLVOnionPayloadOptional bit or not. +func (l *LegacyProtocol) LegacyOnion() bool { + return l.Onion +} diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 0372e0ce..86331e85 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -214,10 +214,15 @@ type SendPaymentRequest struct { CltvLimit int32 `protobuf:"varint,9,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` //* //Optional route hints to reach the destination through private channels. - RouteHints []*lnrpc.RouteHint `protobuf:"bytes,10,rep,name=route_hints,proto3" json:"route_hints,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + RouteHints []*lnrpc.RouteHint `protobuf:"bytes,10,rep,name=route_hints,proto3" json:"route_hints,omitempty"` + //* + //An optional field that can be used to pass an arbitrary set of TLV records + //to a peer which understands the new records. This can be used to pass + //application specific data during the payment attempt. + DestTlv map[uint64][]byte `protobuf:"bytes,11,rep,name=dest_tlv,json=destTlv,proto3" json:"dest_tlv,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *SendPaymentRequest) Reset() { *m = SendPaymentRequest{} } @@ -315,6 +320,13 @@ func (m *SendPaymentRequest) GetRouteHints() []*lnrpc.RouteHint { return nil } +func (m *SendPaymentRequest) GetDestTlv() map[uint64][]byte { + if m != nil { + return m.DestTlv + } + return nil +} + type TrackPaymentRequest struct { /// The hash of the payment to look up. PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"` @@ -1171,6 +1183,7 @@ func init() { proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value) proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value) proto.RegisterType((*SendPaymentRequest)(nil), "routerrpc.SendPaymentRequest") + proto.RegisterMapType((map[uint64][]byte)(nil), "routerrpc.SendPaymentRequest.DestTlvEntry") proto.RegisterType((*TrackPaymentRequest)(nil), "routerrpc.TrackPaymentRequest") proto.RegisterType((*PaymentStatus)(nil), "routerrpc.PaymentStatus") proto.RegisterType((*RouteFeeRequest)(nil), "routerrpc.RouteFeeRequest") @@ -1190,112 +1203,116 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1677 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x77, 0x22, 0xc7, - 0x11, 0x37, 0x02, 0x84, 0x28, 0xfe, 0xcd, 0xb6, 0xb4, 0xda, 0x59, 0xb4, 0xb2, 0xe5, 0xb1, 0xb3, - 0xd6, 0xdb, 0xe7, 0x48, 0x0e, 0x79, 0xf6, 0xcb, 0x29, 0x79, 0x2c, 0x34, 0xd6, 0xec, 0xc2, 0x8c, - 0xdc, 0xc0, 0xda, 0x9b, 0x1c, 0xfa, 0xb5, 0xa0, 0x05, 0xf3, 0x04, 0x33, 0x78, 0xa6, 0x71, 0x56, - 0x39, 0xe4, 0x92, 0x97, 0x63, 0xee, 0xf9, 0x16, 0xf9, 0x14, 0x39, 0xe4, 0x8b, 0x24, 0xdf, 0x21, - 0xa7, 0xbc, 0xee, 0x9e, 0x81, 0x01, 0xb1, 0x1b, 0x9f, 0x98, 0xfe, 0xd5, 0xaf, 0xab, 0xaa, 0xab, - 0xba, 0xaa, 0x0b, 0x38, 0x0e, 0x83, 0xa5, 0xe0, 0x61, 0xb8, 0x18, 0x5d, 0xea, 0xaf, 0x8b, 0x45, - 0x18, 0x88, 0x00, 0x15, 0x57, 0x78, 0xbd, 0x18, 0x2e, 0x46, 0x1a, 0xb5, 0xfe, 0xbb, 0x07, 0xa8, - 0xcf, 0xfd, 0xf1, 0x35, 0xbb, 0x9f, 0x73, 0x5f, 0x10, 0xfe, 0xe3, 0x92, 0x47, 0x02, 0x21, 0xc8, - 0x8d, 0x79, 0x24, 0xcc, 0xcc, 0x59, 0xe6, 0xbc, 0x4c, 0xd4, 0x37, 0x32, 0x20, 0xcb, 0xe6, 0xc2, - 0xdc, 0x3b, 0xcb, 0x9c, 0x67, 0x89, 0xfc, 0x44, 0x9f, 0x42, 0x79, 0xa1, 0xf7, 0xd1, 0x29, 0x8b, - 0xa6, 0x66, 0x56, 0xb1, 0x4b, 0x31, 0x76, 0xc5, 0xa2, 0x29, 0x3a, 0x07, 0xe3, 0xd6, 0xf3, 0xd9, - 0x8c, 0x8e, 0x66, 0xe2, 0x27, 0x3a, 0xe6, 0x33, 0xc1, 0xcc, 0xdc, 0x59, 0xe6, 0x3c, 0x4f, 0xaa, - 0x0a, 0x6f, 0xcd, 0xc4, 0x4f, 0x6d, 0x89, 0xa2, 0x2f, 0xa0, 0x96, 0x28, 0x0b, 0xb5, 0x17, 0x66, - 0xfe, 0x2c, 0x73, 0x5e, 0x24, 0xd5, 0xc5, 0xa6, 0x6f, 0x5f, 0x40, 0x4d, 0x78, 0x73, 0x1e, 0x2c, - 0x05, 0x8d, 0xf8, 0x28, 0xf0, 0xc7, 0x91, 0xb9, 0xaf, 0x35, 0xc6, 0x70, 0x5f, 0xa3, 0xc8, 0x82, - 0xca, 0x2d, 0xe7, 0x74, 0xe6, 0xcd, 0x3d, 0x41, 0x23, 0x26, 0xcc, 0x82, 0x72, 0xbd, 0x74, 0xcb, - 0x79, 0x57, 0x62, 0x7d, 0x26, 0xa4, 0x7f, 0xc1, 0x52, 0x4c, 0x02, 0xcf, 0x9f, 0xd0, 0xd1, 0x94, - 0xf9, 0xd4, 0x1b, 0x9b, 0x07, 0x67, 0x99, 0xf3, 0x1c, 0xa9, 0x26, 0x78, 0x6b, 0xca, 0x7c, 0x7b, - 0x8c, 0x4e, 0x01, 0xd4, 0x19, 0x94, 0x3a, 0xb3, 0xa8, 0x2c, 0x16, 0x25, 0xa2, 0x74, 0xa1, 0x06, - 0x94, 0x54, 0x80, 0xe9, 0xd4, 0xf3, 0x45, 0x64, 0xc2, 0x59, 0xf6, 0xbc, 0xd4, 0x30, 0x2e, 0x66, - 0xbe, 0x8c, 0x35, 0x91, 0x92, 0x2b, 0xcf, 0x17, 0x24, 0x4d, 0xb2, 0x7e, 0x03, 0x87, 0x83, 0x90, - 0x8d, 0xee, 0xb6, 0x82, 0xbf, 0x1d, 0xd6, 0xcc, 0x83, 0xb0, 0x5a, 0x7f, 0x86, 0x4a, 0xbc, 0xa9, - 0x2f, 0x98, 0x58, 0x46, 0xe8, 0x97, 0x90, 0x8f, 0x04, 0x13, 0x5c, 0x91, 0xab, 0x8d, 0x27, 0x17, - 0xab, 0x6c, 0x5f, 0xa4, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0xc3, 0xc1, 0x22, 0xe4, 0xde, 0x9c, 0x4d, - 0xb8, 0x4a, 0x68, 0x99, 0xac, 0xd6, 0xc8, 0x82, 0xbc, 0xda, 0xac, 0xd2, 0x59, 0x6a, 0x94, 0xd3, - 0x67, 0x20, 0x5a, 0x64, 0xfd, 0x16, 0x6a, 0x6a, 0xdd, 0xe1, 0xfc, 0x43, 0x57, 0xe6, 0x09, 0x14, - 0xd8, 0x5c, 0xc7, 0x5e, 0x5f, 0x9b, 0x7d, 0x36, 0x97, 0x61, 0xb7, 0xc6, 0x60, 0xac, 0xf7, 0x47, - 0x8b, 0xc0, 0x8f, 0xb8, 0x4c, 0x85, 0x54, 0x2e, 0x33, 0x21, 0xd3, 0x36, 0x97, 0xbb, 0x32, 0x6a, - 0x57, 0x35, 0xc6, 0x3b, 0x9c, 0xf7, 0x22, 0x26, 0xd0, 0x73, 0x7d, 0x03, 0xe8, 0x2c, 0x18, 0xdd, - 0xc9, 0x3b, 0xc5, 0xee, 0x63, 0xf5, 0x15, 0x09, 0x77, 0x83, 0xd1, 0x5d, 0x5b, 0x82, 0xd6, 0x1f, - 0xf4, 0xdd, 0x1e, 0x04, 0xda, 0xf7, 0x9f, 0x1d, 0xde, 0x75, 0x08, 0xf6, 0xde, 0x1f, 0x02, 0x0a, - 0x87, 0x1b, 0xca, 0xe3, 0x53, 0xa4, 0x23, 0x9b, 0xd9, 0x8a, 0xec, 0x97, 0x50, 0xb8, 0x65, 0xde, - 0x6c, 0x19, 0x26, 0x8a, 0x51, 0x2a, 0x4d, 0x1d, 0x2d, 0x21, 0x09, 0xc5, 0xfa, 0x57, 0x01, 0x0a, - 0x31, 0x88, 0x1a, 0x90, 0x1b, 0x05, 0xe3, 0x24, 0xbb, 0x1f, 0x3f, 0xdc, 0x96, 0xfc, 0xb6, 0x82, - 0x31, 0x27, 0x8a, 0x8b, 0x7e, 0x07, 0x55, 0x79, 0xa3, 0x7d, 0x3e, 0xa3, 0xcb, 0xc5, 0x98, 0xad, - 0x12, 0x6a, 0xa6, 0x76, 0xb7, 0x34, 0x61, 0xa8, 0xe4, 0xa4, 0x32, 0x4a, 0x2f, 0xd1, 0x09, 0x14, - 0xa7, 0x62, 0x36, 0xd2, 0x99, 0xc8, 0xa9, 0xa2, 0x38, 0x90, 0x80, 0xca, 0x81, 0x05, 0x95, 0xc0, - 0xf7, 0x02, 0x9f, 0x46, 0x53, 0x46, 0x1b, 0x5f, 0x7f, 0xa3, 0x8a, 0xb5, 0x4c, 0x4a, 0x0a, 0xec, - 0x4f, 0x59, 0xe3, 0xeb, 0x6f, 0xd0, 0x27, 0x50, 0x52, 0x25, 0xc3, 0xdf, 0x2d, 0xbc, 0xf0, 0x5e, - 0x55, 0x69, 0x85, 0xa8, 0x2a, 0xc2, 0x0a, 0x41, 0x47, 0x90, 0xbf, 0x9d, 0xb1, 0x49, 0xa4, 0x2a, - 0xb3, 0x42, 0xf4, 0x02, 0x7d, 0x05, 0x47, 0x71, 0x0c, 0x68, 0x14, 0x2c, 0xc3, 0x11, 0xa7, 0x9e, - 0x3f, 0xe6, 0xef, 0x54, 0x5d, 0x56, 0x08, 0x8a, 0x65, 0x7d, 0x25, 0xb2, 0xa5, 0xc4, 0xfa, 0x7b, - 0x1e, 0x4a, 0xa9, 0x00, 0xa0, 0x32, 0x1c, 0x10, 0xdc, 0xc7, 0xe4, 0x0d, 0x6e, 0x1b, 0x1f, 0xa1, - 0x73, 0xf8, 0xdc, 0x76, 0x5a, 0x2e, 0x21, 0xb8, 0x35, 0xa0, 0x2e, 0xa1, 0x43, 0xe7, 0xb5, 0xe3, - 0x7e, 0xef, 0xd0, 0xeb, 0xe6, 0xdb, 0x1e, 0x76, 0x06, 0xb4, 0x8d, 0x07, 0x4d, 0xbb, 0xdb, 0x37, - 0x32, 0xe8, 0x19, 0x98, 0x6b, 0x66, 0x22, 0x6e, 0xf6, 0xdc, 0xa1, 0x33, 0x30, 0xf6, 0xd0, 0x27, - 0x70, 0xd2, 0xb1, 0x9d, 0x66, 0x97, 0xae, 0x39, 0xad, 0xee, 0xe0, 0x0d, 0xc5, 0x3f, 0x5c, 0xdb, - 0xe4, 0xad, 0x91, 0xdd, 0x45, 0xb8, 0x1a, 0x74, 0x5b, 0x89, 0x86, 0x1c, 0x7a, 0x0a, 0x8f, 0x35, - 0x41, 0x6f, 0xa1, 0x03, 0xd7, 0xa5, 0x7d, 0xd7, 0x75, 0x8c, 0x3c, 0x7a, 0x04, 0x15, 0xdb, 0x79, - 0xd3, 0xec, 0xda, 0x6d, 0x4a, 0x70, 0xb3, 0xdb, 0x33, 0xf6, 0xd1, 0x21, 0xd4, 0xb6, 0x79, 0x05, - 0xa9, 0x22, 0xe1, 0xb9, 0x8e, 0xed, 0x3a, 0xf4, 0x0d, 0x26, 0x7d, 0xdb, 0x75, 0x8c, 0x03, 0x74, - 0x0c, 0x68, 0x53, 0x74, 0xd5, 0x6b, 0xb6, 0x8c, 0x22, 0x7a, 0x0c, 0x8f, 0x36, 0xf1, 0xd7, 0xf8, - 0xad, 0x01, 0xc8, 0x84, 0x23, 0xed, 0x18, 0x7d, 0x89, 0xbb, 0xee, 0xf7, 0xb4, 0x67, 0x3b, 0x76, - 0x6f, 0xd8, 0x33, 0x4a, 0xe8, 0x08, 0x8c, 0x0e, 0xc6, 0xd4, 0x76, 0xfa, 0xc3, 0x4e, 0xc7, 0x6e, - 0xd9, 0xd8, 0x19, 0x18, 0x65, 0x6d, 0x79, 0xd7, 0xc1, 0x2b, 0x72, 0x43, 0xeb, 0xaa, 0xe9, 0x38, - 0xb8, 0x4b, 0xdb, 0x76, 0xbf, 0xf9, 0xb2, 0x8b, 0xdb, 0x46, 0x15, 0x9d, 0xc2, 0xd3, 0x01, 0xee, - 0x5d, 0xbb, 0xa4, 0x49, 0xde, 0xd2, 0x44, 0xde, 0x69, 0xda, 0xdd, 0x21, 0xc1, 0x46, 0x0d, 0x7d, - 0x0a, 0xa7, 0x04, 0x7f, 0x37, 0xb4, 0x09, 0x6e, 0x53, 0xc7, 0x6d, 0x63, 0xda, 0xc1, 0xcd, 0xc1, - 0x90, 0x60, 0xda, 0xb3, 0xfb, 0x7d, 0xdb, 0xf9, 0xd6, 0x30, 0xd0, 0xe7, 0x70, 0xb6, 0xa2, 0xac, - 0x14, 0x6c, 0xb1, 0x1e, 0xc9, 0xf3, 0x25, 0x29, 0x75, 0xf0, 0x0f, 0x03, 0x7a, 0x8d, 0x31, 0x31, - 0x10, 0xaa, 0xc3, 0xf1, 0xda, 0xbc, 0x36, 0x10, 0xdb, 0x3e, 0x94, 0xb2, 0x6b, 0x4c, 0x7a, 0x4d, - 0x47, 0x26, 0x78, 0x43, 0x76, 0x24, 0xdd, 0x5e, 0xcb, 0xb6, 0xdd, 0x7e, 0x8c, 0x8e, 0xa0, 0x96, - 0x58, 0x4b, 0xc0, 0x7f, 0x17, 0xd0, 0x13, 0x40, 0x43, 0x87, 0xe0, 0x66, 0x5b, 0x1e, 0x7e, 0x25, - 0xf8, 0x4f, 0xe1, 0x55, 0xee, 0x60, 0xcf, 0xc8, 0x5a, 0xff, 0xc8, 0x42, 0x65, 0xa3, 0xd6, 0xd0, - 0x33, 0x28, 0x46, 0xde, 0xc4, 0x67, 0x42, 0x76, 0x03, 0xdd, 0x28, 0xd6, 0x80, 0x7a, 0x6c, 0xa6, - 0xcc, 0xf3, 0x75, 0x87, 0xd2, 0x1d, 0xba, 0xa8, 0x10, 0xd5, 0x9f, 0x9e, 0x40, 0x21, 0x79, 0xac, - 0xb2, 0xaa, 0x2e, 0xf7, 0x47, 0xfa, 0x91, 0x7a, 0x06, 0x45, 0xd9, 0x02, 0x23, 0xc1, 0xe6, 0x0b, - 0x55, 0xb2, 0x15, 0xb2, 0x06, 0xd0, 0x67, 0x50, 0x99, 0xf3, 0x28, 0x62, 0x13, 0x4e, 0x75, 0xd9, - 0x81, 0x62, 0x94, 0x63, 0xb0, 0xa3, 0xaa, 0xef, 0x33, 0x48, 0xda, 0x40, 0x4c, 0xca, 0x6b, 0x52, - 0x0c, 0x6a, 0xd2, 0x76, 0x07, 0x16, 0x2c, 0xae, 0xee, 0x74, 0x07, 0x16, 0x0c, 0xbd, 0x80, 0x47, - 0xba, 0x85, 0x78, 0xbe, 0x37, 0x5f, 0xce, 0x75, 0x2b, 0x29, 0x28, 0x97, 0x6b, 0xaa, 0x95, 0x68, - 0x5c, 0x75, 0x94, 0xa7, 0x70, 0x70, 0xc3, 0x22, 0x2e, 0x9b, 0x7f, 0x5c, 0xea, 0x05, 0xb9, 0xee, - 0x70, 0x2e, 0x45, 0xf2, 0x49, 0x08, 0x65, 0x13, 0x2b, 0x6a, 0xd1, 0x2d, 0xe7, 0x44, 0xc6, 0x71, - 0x65, 0x81, 0xbd, 0x5b, 0x5b, 0x28, 0xa5, 0x2c, 0x68, 0x5c, 0x59, 0x78, 0x01, 0x8f, 0xf8, 0x3b, - 0x11, 0x32, 0x1a, 0x2c, 0xd8, 0x8f, 0x4b, 0x4e, 0xc7, 0x4c, 0x30, 0xb3, 0xac, 0x82, 0x5b, 0x53, - 0x02, 0x57, 0xe1, 0x6d, 0x26, 0x98, 0xf5, 0x0c, 0xea, 0x84, 0x47, 0x5c, 0xf4, 0xbc, 0x28, 0xf2, - 0x02, 0xbf, 0x15, 0xf8, 0x22, 0x0c, 0x66, 0xf1, 0x1b, 0x62, 0x9d, 0xc2, 0xc9, 0x4e, 0xa9, 0x7e, - 0x04, 0xe4, 0xe6, 0xef, 0x96, 0x3c, 0xbc, 0xdf, 0xbd, 0xf9, 0x1e, 0x4e, 0x76, 0x4a, 0xe3, 0x17, - 0xe4, 0x4b, 0xc8, 0xfb, 0xc1, 0x98, 0x47, 0x66, 0x46, 0xcd, 0x10, 0xc7, 0xa9, 0x76, 0xed, 0x04, - 0x63, 0x7e, 0xe5, 0x45, 0x22, 0x08, 0xef, 0x89, 0x26, 0x49, 0xf6, 0x82, 0x79, 0x61, 0x64, 0xee, - 0x3d, 0x60, 0x5f, 0x33, 0x2f, 0x5c, 0xb1, 0x15, 0xc9, 0xfa, 0x4b, 0x06, 0x4a, 0x29, 0x25, 0xe8, - 0x18, 0xf6, 0x17, 0xcb, 0x9b, 0x3b, 0x7e, 0x1f, 0x5f, 0xc1, 0x78, 0x85, 0x9e, 0x43, 0x75, 0xc6, - 0x22, 0x41, 0x65, 0xaf, 0xa5, 0x32, 0xa5, 0xf1, 0x03, 0xbb, 0x85, 0xa2, 0x0b, 0x40, 0x81, 0x98, - 0xf2, 0x90, 0x46, 0xcb, 0xd1, 0x88, 0x47, 0x11, 0x5d, 0x84, 0xc1, 0x8d, 0xba, 0x93, 0x7b, 0x64, - 0x87, 0xe4, 0x55, 0xee, 0x20, 0x67, 0xe4, 0xad, 0x7f, 0x66, 0xa0, 0x94, 0x72, 0x4e, 0xde, 0x5a, - 0x79, 0x18, 0x7a, 0x1b, 0x06, 0xf3, 0xa4, 0x16, 0x56, 0x00, 0x32, 0xa1, 0xa0, 0x16, 0x22, 0x88, - 0x0b, 0x21, 0x59, 0xee, 0xf0, 0x32, 0xbb, 0xd3, 0xcb, 0x06, 0x1c, 0xcd, 0x3d, 0x9f, 0x2e, 0xb8, - 0xcf, 0x66, 0xde, 0x9f, 0x38, 0x4d, 0x66, 0x92, 0x9c, 0x62, 0xef, 0x94, 0x21, 0x0b, 0xca, 0x1b, - 0x67, 0xca, 0xab, 0x33, 0x6d, 0x60, 0x2f, 0xfe, 0x96, 0x81, 0x72, 0x7a, 0xba, 0x42, 0x15, 0x28, - 0xda, 0x0e, 0xed, 0x74, 0xed, 0x6f, 0xaf, 0x06, 0xc6, 0x47, 0x72, 0xd9, 0x1f, 0xb6, 0x5a, 0x18, - 0xb7, 0x71, 0xdb, 0xc8, 0x20, 0x04, 0x55, 0xd9, 0x18, 0x70, 0x9b, 0x0e, 0xec, 0x1e, 0x76, 0x87, - 0xf2, 0x4d, 0x39, 0x84, 0x5a, 0x8c, 0x39, 0x2e, 0x25, 0xee, 0x70, 0x80, 0x8d, 0x2c, 0x32, 0xa0, - 0x1c, 0x83, 0x98, 0x10, 0x97, 0x18, 0x39, 0xd9, 0x08, 0x63, 0xe4, 0xe1, 0xfb, 0x94, 0x3c, 0x5f, - 0xf9, 0xc6, 0x5f, 0x73, 0xb0, 0xaf, 0xa6, 0x91, 0x10, 0x5d, 0x41, 0x29, 0x35, 0xd6, 0xa3, 0xd3, - 0xd4, 0xb5, 0x78, 0x38, 0xee, 0xd7, 0xcd, 0xdd, 0xe3, 0xe2, 0x32, 0xfa, 0x2a, 0x83, 0x5e, 0x41, - 0x39, 0x3d, 0xa4, 0xa2, 0xf4, 0xf0, 0xb1, 0x63, 0x7a, 0xfd, 0xa0, 0xae, 0xd7, 0x60, 0xe0, 0x48, - 0x78, 0x73, 0x39, 0x6c, 0xc4, 0xe3, 0x1f, 0xaa, 0xa7, 0xf8, 0x5b, 0x33, 0x65, 0xfd, 0x64, 0xa7, - 0x2c, 0xae, 0x93, 0xae, 0x3e, 0x62, 0x3c, 0x80, 0x3d, 0x38, 0xe2, 0xe6, 0xd4, 0x57, 0xff, 0xf8, - 0x7d, 0xe2, 0x58, 0xdb, 0x18, 0x0e, 0x77, 0x54, 0x34, 0xfa, 0x45, 0xda, 0x83, 0xf7, 0xf6, 0x83, - 0xfa, 0xf3, 0xff, 0x47, 0x5b, 0x5b, 0xd9, 0x51, 0xfa, 0x1b, 0x56, 0xde, 0xdf, 0x38, 0x36, 0xac, - 0x7c, 0xa0, 0x83, 0xbc, 0xfc, 0xd5, 0xef, 0x2f, 0x27, 0x9e, 0x98, 0x2e, 0x6f, 0x2e, 0x46, 0xc1, - 0xfc, 0x72, 0xe6, 0x4d, 0xa6, 0xc2, 0xf7, 0xfc, 0x89, 0xcf, 0xc5, 0x1f, 0x83, 0xf0, 0xee, 0x72, - 0xe6, 0x8f, 0x2f, 0xd5, 0x40, 0x7b, 0xb9, 0x52, 0x77, 0xb3, 0xaf, 0xfe, 0x0e, 0xfe, 0xfa, 0x7f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xa6, 0xd9, 0x94, 0x3e, 0x0e, 0x00, 0x00, + // 1736 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x41, 0x73, 0x22, 0xc7, + 0x15, 0x36, 0x02, 0x84, 0x78, 0x80, 0x98, 0x6d, 0x69, 0xb5, 0xb3, 0x68, 0x65, 0xcb, 0xd8, 0x59, + 0xab, 0xb6, 0x1c, 0xc9, 0x21, 0x65, 0x97, 0xcb, 0x87, 0xa4, 0x58, 0x68, 0xac, 0xd9, 0x85, 0x19, + 0xb9, 0x81, 0xb5, 0x37, 0x39, 0x74, 0xb5, 0xa0, 0x05, 0x53, 0x1a, 0x66, 0xf0, 0x4c, 0xa3, 0xac, + 0x72, 0xc8, 0x25, 0x95, 0x63, 0xee, 0xf9, 0x17, 0xf9, 0x15, 0x39, 0xe4, 0x8f, 0x24, 0xbf, 0x22, + 0x55, 0xa9, 0xee, 0x9e, 0x81, 0x01, 0xb1, 0x9b, 0x9c, 0x98, 0xfe, 0xde, 0xd7, 0xaf, 0x5f, 0xf7, + 0xeb, 0xf7, 0xf5, 0x03, 0x8e, 0xc2, 0x60, 0x21, 0x78, 0x18, 0xce, 0x47, 0x17, 0xfa, 0xeb, 0x7c, + 0x1e, 0x06, 0x22, 0x40, 0xc5, 0x25, 0x5e, 0x2b, 0x86, 0xf3, 0x91, 0x46, 0xeb, 0xff, 0xc9, 0x02, + 0xea, 0x73, 0x7f, 0x7c, 0xc5, 0xee, 0x67, 0xdc, 0x17, 0x84, 0xff, 0xbc, 0xe0, 0x91, 0x40, 0x08, + 0x72, 0x63, 0x1e, 0x09, 0x33, 0x73, 0x9a, 0x39, 0x2b, 0x13, 0xf5, 0x8d, 0x0c, 0xc8, 0xb2, 0x99, + 0x30, 0x77, 0x4e, 0x33, 0x67, 0x59, 0x22, 0x3f, 0xd1, 0xa7, 0x50, 0x9e, 0xeb, 0x79, 0x74, 0xca, + 0xa2, 0xa9, 0x99, 0x55, 0xec, 0x52, 0x8c, 0x5d, 0xb2, 0x68, 0x8a, 0xce, 0xc0, 0xb8, 0x71, 0x7d, + 0xe6, 0xd1, 0x91, 0x27, 0xee, 0xe8, 0x98, 0x7b, 0x82, 0x99, 0xb9, 0xd3, 0xcc, 0x59, 0x9e, 0xec, + 0x2b, 0xbc, 0xe5, 0x89, 0xbb, 0xb6, 0x44, 0xd1, 0x17, 0x50, 0x4d, 0x9c, 0x85, 0x3a, 0x0a, 0x33, + 0x7f, 0x9a, 0x39, 0x2b, 0x92, 0xfd, 0xf9, 0x7a, 0x6c, 0x5f, 0x40, 0x55, 0xb8, 0x33, 0x1e, 0x2c, + 0x04, 0x8d, 0xf8, 0x28, 0xf0, 0xc7, 0x91, 0xb9, 0xab, 0x3d, 0xc6, 0x70, 0x5f, 0xa3, 0xa8, 0x0e, + 0x95, 0x1b, 0xce, 0xa9, 0xe7, 0xce, 0x5c, 0x41, 0x23, 0x26, 0xcc, 0x82, 0x0a, 0xbd, 0x74, 0xc3, + 0x79, 0x57, 0x62, 0x7d, 0x26, 0x64, 0x7c, 0xc1, 0x42, 0x4c, 0x02, 0xd7, 0x9f, 0xd0, 0xd1, 0x94, + 0xf9, 0xd4, 0x1d, 0x9b, 0x7b, 0xa7, 0x99, 0xb3, 0x1c, 0xd9, 0x4f, 0xf0, 0xd6, 0x94, 0xf9, 0xd6, + 0x18, 0x9d, 0x00, 0xa8, 0x3d, 0x28, 0x77, 0x66, 0x51, 0xad, 0x58, 0x94, 0x88, 0xf2, 0x85, 0x1a, + 0x50, 0x52, 0x07, 0x4c, 0xa7, 0xae, 0x2f, 0x22, 0x13, 0x4e, 0xb3, 0x67, 0xa5, 0x86, 0x71, 0xee, + 0xf9, 0xf2, 0xac, 0x89, 0xb4, 0x5c, 0xba, 0xbe, 0x20, 0x69, 0x12, 0xc2, 0xb0, 0x27, 0x4f, 0x96, + 0x0a, 0xef, 0xce, 0x2c, 0xa9, 0x09, 0x2f, 0xce, 0x97, 0x59, 0x3a, 0x7f, 0x98, 0x96, 0xf3, 0x36, + 0x8f, 0xc4, 0xc0, 0xbb, 0xc3, 0xbe, 0x08, 0xef, 0x49, 0x61, 0xac, 0x47, 0xb5, 0xef, 0xa0, 0x9c, + 0x36, 0xc8, 0x44, 0xdd, 0xf2, 0x7b, 0x95, 0xbb, 0x1c, 0x91, 0x9f, 0xe8, 0x10, 0xf2, 0x77, 0xcc, + 0x5b, 0x70, 0x95, 0xbc, 0x32, 0xd1, 0x83, 0xef, 0x76, 0xbe, 0xcd, 0xd4, 0xbf, 0x85, 0x83, 0x41, + 0xc8, 0x46, 0xb7, 0x1b, 0xf9, 0xdf, 0xcc, 0x6c, 0xe6, 0x41, 0x66, 0xeb, 0x7f, 0x82, 0x4a, 0x3c, + 0xa9, 0x2f, 0x98, 0x58, 0x44, 0xe8, 0x97, 0x90, 0x8f, 0x04, 0x13, 0x5c, 0x91, 0xf7, 0x1b, 0x4f, + 0x52, 0x5b, 0x49, 0x11, 0x39, 0xd1, 0x2c, 0x54, 0x83, 0xbd, 0x79, 0xc8, 0xdd, 0x19, 0x9b, 0x24, + 0x61, 0x2d, 0xc7, 0xa8, 0x0e, 0x79, 0x35, 0x59, 0xdd, 0xa8, 0x52, 0xa3, 0x9c, 0x3e, 0x46, 0xa2, + 0x4d, 0xf5, 0xdf, 0x40, 0x55, 0x8d, 0x3b, 0x9c, 0x7f, 0xe8, 0xd6, 0x3e, 0x81, 0x02, 0x9b, 0xe9, + 0xf4, 0xeb, 0x9b, 0xbb, 0xcb, 0x66, 0x32, 0xf3, 0xf5, 0x31, 0x18, 0xab, 0xf9, 0xd1, 0x3c, 0xf0, + 0x23, 0x2e, 0x6f, 0x83, 0x74, 0x2e, 0x2f, 0x83, 0xbc, 0x39, 0x33, 0x39, 0x2b, 0xa3, 0x66, 0xed, + 0xc7, 0x78, 0x87, 0xf3, 0x5e, 0xc4, 0x04, 0x7a, 0xae, 0x2f, 0x21, 0xf5, 0x82, 0xd1, 0xad, 0xbc, + 0xd6, 0xec, 0x3e, 0x76, 0x5f, 0x91, 0x70, 0x37, 0x18, 0xdd, 0xb6, 0x25, 0x58, 0xff, 0xbd, 0x2e, + 0xaf, 0x41, 0xa0, 0x63, 0xff, 0xbf, 0x8f, 0x77, 0x75, 0x04, 0x3b, 0xef, 0x3f, 0x02, 0x0a, 0x07, + 0x6b, 0xce, 0xe3, 0x5d, 0xa4, 0x4f, 0x36, 0xb3, 0x71, 0xb2, 0x5f, 0x42, 0xe1, 0x86, 0xb9, 0xde, + 0x22, 0x4c, 0x1c, 0xa3, 0x54, 0x9a, 0x3a, 0xda, 0x42, 0x12, 0x4a, 0xfd, 0x9f, 0x05, 0x28, 0xc4, + 0x20, 0x6a, 0x40, 0x6e, 0x14, 0x8c, 0x93, 0xec, 0x7e, 0xfc, 0x70, 0x5a, 0xf2, 0xdb, 0x0a, 0xc6, + 0x9c, 0x28, 0x2e, 0xfa, 0x2d, 0xec, 0xcb, 0xa2, 0xf2, 0xb9, 0x47, 0x17, 0xf3, 0x31, 0x5b, 0x26, + 0xd4, 0x4c, 0xcd, 0x6e, 0x69, 0xc2, 0x50, 0xd9, 0x49, 0x65, 0x94, 0x1e, 0xa2, 0x63, 0x28, 0x4e, + 0x85, 0x37, 0xd2, 0x99, 0xc8, 0xa9, 0x0b, 0xbd, 0x27, 0x01, 0x95, 0x83, 0x3a, 0x54, 0x02, 0xdf, + 0x0d, 0x7c, 0x1a, 0x4d, 0x19, 0x6d, 0x7c, 0xfd, 0x8d, 0xd2, 0x8b, 0x32, 0x29, 0x29, 0xb0, 0x3f, + 0x65, 0x8d, 0xaf, 0xbf, 0x41, 0x9f, 0x40, 0x49, 0x55, 0x2d, 0x7f, 0x37, 0x77, 0xc3, 0x7b, 0x25, + 0x14, 0x15, 0xa2, 0x0a, 0x19, 0x2b, 0x44, 0x96, 0xc6, 0x8d, 0xc7, 0x26, 0x91, 0x12, 0x87, 0x0a, + 0xd1, 0x03, 0xf4, 0x15, 0x1c, 0xc6, 0x67, 0x40, 0xa3, 0x60, 0x11, 0x8e, 0x38, 0x75, 0xfd, 0x31, + 0x7f, 0xa7, 0xa4, 0xa1, 0x42, 0x50, 0x6c, 0xeb, 0x2b, 0x93, 0x25, 0x2d, 0xf5, 0xbf, 0xe5, 0xa1, + 0x94, 0x3a, 0x00, 0x54, 0x86, 0x3d, 0x82, 0xfb, 0x98, 0xbc, 0xc1, 0x6d, 0xe3, 0x23, 0x74, 0x06, + 0x9f, 0x5b, 0x76, 0xcb, 0x21, 0x04, 0xb7, 0x06, 0xd4, 0x21, 0x74, 0x68, 0xbf, 0xb6, 0x9d, 0x1f, + 0x6d, 0x7a, 0xd5, 0x7c, 0xdb, 0xc3, 0xf6, 0x80, 0xb6, 0xf1, 0xa0, 0x69, 0x75, 0xfb, 0x46, 0x06, + 0x3d, 0x03, 0x73, 0xc5, 0x4c, 0xcc, 0xcd, 0x9e, 0x33, 0xb4, 0x07, 0xc6, 0x0e, 0xfa, 0x04, 0x8e, + 0x3b, 0x96, 0xdd, 0xec, 0xd2, 0x15, 0xa7, 0xd5, 0x1d, 0xbc, 0xa1, 0xf8, 0xa7, 0x2b, 0x8b, 0xbc, + 0x35, 0xb2, 0xdb, 0x08, 0x97, 0x83, 0x6e, 0x2b, 0xf1, 0x90, 0x43, 0x4f, 0xe1, 0xb1, 0x26, 0xe8, + 0x29, 0x74, 0xe0, 0x38, 0xb4, 0xef, 0x38, 0xb6, 0x91, 0x47, 0x8f, 0xa0, 0x62, 0xd9, 0x6f, 0x9a, + 0x5d, 0xab, 0x4d, 0x09, 0x6e, 0x76, 0x7b, 0xc6, 0x2e, 0x3a, 0x80, 0xea, 0x26, 0xaf, 0x20, 0x5d, + 0x24, 0x3c, 0xc7, 0xb6, 0x1c, 0x9b, 0xbe, 0xc1, 0xa4, 0x6f, 0x39, 0xb6, 0xb1, 0x87, 0x8e, 0x00, + 0xad, 0x9b, 0x2e, 0x7b, 0xcd, 0x96, 0x51, 0x44, 0x8f, 0xe1, 0xd1, 0x3a, 0xfe, 0x1a, 0xbf, 0x35, + 0x00, 0x99, 0x70, 0xa8, 0x03, 0xa3, 0x2f, 0x71, 0xd7, 0xf9, 0x91, 0xf6, 0x2c, 0xdb, 0xea, 0x0d, + 0x7b, 0x46, 0x09, 0x1d, 0x82, 0xd1, 0xc1, 0x98, 0x5a, 0x76, 0x7f, 0xd8, 0xe9, 0x58, 0x2d, 0x0b, + 0xdb, 0x03, 0xa3, 0xac, 0x57, 0xde, 0xb6, 0xf1, 0x8a, 0x9c, 0xd0, 0xba, 0x6c, 0xda, 0x36, 0xee, + 0xd2, 0xb6, 0xd5, 0x6f, 0xbe, 0xec, 0xe2, 0xb6, 0xb1, 0x8f, 0x4e, 0xe0, 0xe9, 0x00, 0xf7, 0xae, + 0x1c, 0xd2, 0x24, 0x6f, 0x69, 0x62, 0xef, 0x34, 0xad, 0xee, 0x90, 0x60, 0xa3, 0x8a, 0x3e, 0x85, + 0x13, 0x82, 0x7f, 0x18, 0x5a, 0x04, 0xb7, 0xa9, 0xed, 0xb4, 0x31, 0xed, 0xe0, 0xe6, 0x60, 0x48, + 0x30, 0xed, 0x59, 0xfd, 0xbe, 0x65, 0x7f, 0x6f, 0x18, 0xe8, 0x73, 0x38, 0x5d, 0x52, 0x96, 0x0e, + 0x36, 0x58, 0x8f, 0xe4, 0xfe, 0x92, 0x94, 0xda, 0xf8, 0xa7, 0x01, 0xbd, 0xc2, 0x98, 0x18, 0x08, + 0xd5, 0xe0, 0x68, 0xb5, 0xbc, 0x5e, 0x20, 0x5e, 0xfb, 0x40, 0xda, 0xae, 0x30, 0xe9, 0x35, 0x6d, + 0x99, 0xe0, 0x35, 0xdb, 0xa1, 0x0c, 0x7b, 0x65, 0xdb, 0x0c, 0xfb, 0x31, 0x3a, 0x84, 0x6a, 0xb2, + 0x5a, 0x02, 0xfe, 0xab, 0x80, 0x9e, 0x00, 0x1a, 0xda, 0x04, 0x37, 0xdb, 0x72, 0xf3, 0x4b, 0xc3, + 0xbf, 0x0b, 0xaf, 0x72, 0x7b, 0x3b, 0x46, 0xb6, 0xfe, 0xf7, 0x2c, 0x54, 0xd6, 0x6a, 0x0d, 0x3d, + 0x83, 0x62, 0xe4, 0x4e, 0x7c, 0x26, 0xa4, 0x1a, 0x68, 0xa1, 0x58, 0x01, 0xea, 0xbd, 0x9b, 0x32, + 0xd7, 0xd7, 0x0a, 0xa5, 0x15, 0xba, 0xa8, 0x10, 0xa5, 0x4f, 0x4f, 0xa0, 0x90, 0xbc, 0x97, 0x59, + 0x55, 0x97, 0xbb, 0x23, 0xfd, 0x4e, 0x3e, 0x83, 0xa2, 0x94, 0xc0, 0x48, 0xb0, 0xd9, 0x5c, 0x95, + 0x6c, 0x85, 0xac, 0x00, 0xf4, 0x19, 0x54, 0x66, 0x3c, 0x8a, 0xd8, 0x84, 0x53, 0x5d, 0x76, 0xa0, + 0x18, 0xe5, 0x18, 0xec, 0xa8, 0xea, 0xfb, 0x0c, 0x12, 0x19, 0x88, 0x49, 0x79, 0x4d, 0x8a, 0x41, + 0x4d, 0xda, 0x54, 0x60, 0xc1, 0xe2, 0xea, 0x4e, 0x2b, 0xb0, 0x60, 0xe8, 0x05, 0x3c, 0xd2, 0x12, + 0xe2, 0xfa, 0xee, 0x6c, 0x31, 0xd3, 0x52, 0x52, 0x50, 0x21, 0x57, 0x95, 0x94, 0x68, 0x5c, 0x29, + 0xca, 0x53, 0xd8, 0xbb, 0x66, 0x11, 0x97, 0xe2, 0x1f, 0x97, 0x7a, 0x41, 0x8e, 0x3b, 0x9c, 0x4b, + 0x93, 0x7c, 0x12, 0x42, 0x29, 0x62, 0x45, 0x6d, 0xba, 0xe1, 0x9c, 0xc8, 0x73, 0x5c, 0xae, 0xc0, + 0xde, 0xad, 0x56, 0x28, 0xa5, 0x56, 0xd0, 0xb8, 0x5a, 0xe1, 0x05, 0x3c, 0xe2, 0xef, 0x44, 0xc8, + 0x68, 0x30, 0x67, 0x3f, 0x2f, 0x38, 0x1d, 0x33, 0xc1, 0xcc, 0xb2, 0x3a, 0xdc, 0xaa, 0x32, 0x38, + 0x0a, 0x6f, 0x33, 0xc1, 0xea, 0xcf, 0xa0, 0x46, 0x78, 0xc4, 0x45, 0xcf, 0x8d, 0x22, 0x37, 0xf0, + 0x5b, 0x81, 0x2f, 0xc2, 0xc0, 0x8b, 0xdf, 0x90, 0xfa, 0x09, 0x1c, 0x6f, 0xb5, 0xea, 0x47, 0x40, + 0x4e, 0xfe, 0x61, 0xc1, 0xc3, 0xfb, 0xed, 0x93, 0xef, 0xe1, 0x78, 0xab, 0x35, 0x7e, 0x41, 0xbe, + 0x84, 0xbc, 0x1f, 0x8c, 0x79, 0x64, 0x66, 0x54, 0x57, 0x72, 0x94, 0x92, 0x6b, 0x3b, 0x18, 0xf3, + 0x4b, 0x37, 0x12, 0x41, 0x78, 0x4f, 0x34, 0x49, 0xb2, 0xe7, 0xcc, 0x0d, 0x23, 0x73, 0xe7, 0x01, + 0xfb, 0x8a, 0xb9, 0xe1, 0x92, 0xad, 0x48, 0xf5, 0x3f, 0x67, 0xa0, 0x94, 0x72, 0x82, 0x8e, 0x60, + 0x77, 0xbe, 0xb8, 0x4e, 0x1a, 0x96, 0x32, 0x89, 0x47, 0xe8, 0x39, 0xec, 0x7b, 0x2c, 0x12, 0x54, + 0x6a, 0x2d, 0x95, 0x29, 0x8d, 0x1f, 0xd8, 0x0d, 0x14, 0x9d, 0x03, 0x0a, 0xc4, 0x94, 0x87, 0x34, + 0x5a, 0x8c, 0x46, 0x3c, 0x8a, 0xe8, 0x3c, 0x0c, 0xae, 0xd5, 0x9d, 0xdc, 0x21, 0x5b, 0x2c, 0xaf, + 0x72, 0x7b, 0x39, 0x23, 0x5f, 0xff, 0x47, 0x06, 0x4a, 0xa9, 0xe0, 0xe4, 0xad, 0x95, 0x9b, 0xa1, + 0x37, 0x61, 0x30, 0x4b, 0x6a, 0x61, 0x09, 0x20, 0x13, 0x0a, 0x6a, 0x20, 0x82, 0xb8, 0x10, 0x92, + 0xe1, 0x96, 0x28, 0xb3, 0x5b, 0xa3, 0x6c, 0xc0, 0xe1, 0xcc, 0xf5, 0xe9, 0x9c, 0xfb, 0xcc, 0x73, + 0xff, 0xc8, 0x69, 0xd2, 0x93, 0xe4, 0x14, 0x7b, 0xab, 0x0d, 0xd5, 0xa1, 0xbc, 0xb6, 0xa7, 0xbc, + 0xda, 0xd3, 0x1a, 0xf6, 0xe2, 0xaf, 0x19, 0x28, 0xa7, 0xbb, 0x2b, 0x54, 0x81, 0xa2, 0x65, 0xd3, + 0x4e, 0xd7, 0xfa, 0xfe, 0x72, 0x60, 0x7c, 0x24, 0x87, 0xfd, 0x61, 0xab, 0x85, 0x71, 0x1b, 0xb7, + 0x8d, 0x0c, 0x42, 0xb0, 0x2f, 0x85, 0x01, 0xb7, 0xe9, 0xc0, 0xea, 0x61, 0x67, 0x28, 0xdf, 0x94, + 0x03, 0xa8, 0xc6, 0x98, 0xed, 0x50, 0xe2, 0x0c, 0x07, 0xd8, 0xc8, 0x22, 0x03, 0xca, 0x31, 0x88, + 0x09, 0x71, 0x88, 0x91, 0x93, 0x42, 0x18, 0x23, 0x0f, 0xdf, 0xa7, 0xe4, 0xf9, 0xca, 0x37, 0xfe, + 0x92, 0x83, 0x5d, 0xd5, 0x8d, 0x84, 0xe8, 0x12, 0x4a, 0xa9, 0x16, 0x16, 0x9d, 0x7c, 0xb0, 0xb5, + 0xad, 0x99, 0xdb, 0xdb, 0xc5, 0x45, 0xf4, 0x55, 0x06, 0xbd, 0x82, 0x72, 0xba, 0x49, 0x45, 0xe9, + 0xe6, 0x63, 0x4b, 0xf7, 0xfa, 0x41, 0x5f, 0xaf, 0xc1, 0xc0, 0x91, 0x70, 0x67, 0xb2, 0xd9, 0x88, + 0xdb, 0x3f, 0x54, 0x4b, 0xf1, 0x37, 0x7a, 0xca, 0xda, 0xf1, 0x56, 0x5b, 0x5c, 0x27, 0x5d, 0xbd, + 0xc5, 0xb8, 0x01, 0x7b, 0xb0, 0xc5, 0xf5, 0xae, 0xaf, 0xf6, 0xf1, 0xfb, 0xcc, 0xb1, 0xb7, 0x31, + 0x1c, 0x6c, 0xa9, 0x68, 0xf4, 0x8b, 0x74, 0x04, 0xef, 0xd5, 0x83, 0xda, 0xf3, 0xff, 0x45, 0x5b, + 0xad, 0xb2, 0xa5, 0xf4, 0xd7, 0x56, 0x79, 0xbf, 0x70, 0xac, 0xad, 0xf2, 0x01, 0x05, 0x79, 0xf9, + 0xab, 0xdf, 0x5d, 0x4c, 0x5c, 0x31, 0x5d, 0x5c, 0x9f, 0x8f, 0x82, 0xd9, 0x85, 0xe7, 0x4e, 0xa6, + 0xc2, 0x77, 0xfd, 0x89, 0xcf, 0xc5, 0x1f, 0x82, 0xf0, 0xf6, 0xc2, 0xf3, 0xc7, 0x17, 0xaa, 0xa1, + 0xbd, 0x58, 0xba, 0xbb, 0xde, 0x55, 0xff, 0x48, 0x7f, 0xfd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x54, 0xde, 0xd4, 0xfd, 0xc1, 0x0e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 6c3eaed1..af1c45d1 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -63,6 +63,13 @@ message SendPaymentRequest { Optional route hints to reach the destination through private channels. */ repeated lnrpc.RouteHint route_hints = 10 [json_name = "route_hints"]; + + /** + An optional field that can be used to pass an arbitrary set of TLV records + to a peer which understands the new records. This can be used to pass + application specific data during the payment attempt. + */ + map dest_tlv = 11; } message TrackPaymentRequest { diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 814d6bed..60ac2c18 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -14,6 +14,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/zpay32" context "golang.org/x/net/context" ) @@ -40,6 +41,7 @@ type RouterBackend struct { // routes. FindRoute func(source, target route.Vertex, amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams, + destTlvRecords []tlv.Record, finalExpiry ...uint16) (*route.Route, error) MissionControl MissionControl @@ -188,6 +190,15 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context, fromNode, toNode, amt, ) }, + DestPayloadTLV: len(in.DestTlv) != 0, + } + + // If we have any TLV records destined for the final hop, then we'll + // attempt to decode them now into a form that the router can more + // easily manipulate. + destTlvRecords, err := tlv.MapToRecords(in.DestTlv) + if err != nil { + return nil, err } // Query the channel router for a possible path to the destination that @@ -201,11 +212,12 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context, if in.FinalCltvDelta == 0 { route, findErr = r.FindRoute( sourcePubKey, targetPubKey, amtMSat, restrictions, + destTlvRecords, ) } else { route, findErr = r.FindRoute( sourcePubKey, targetPubKey, amtMSat, restrictions, - uint16(in.FinalCltvDelta), + destTlvRecords, uint16(in.FinalCltvDelta), ) } if findErr != nil { @@ -214,8 +226,10 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context, // For each valid route, we'll convert the result into the format // required by the RPC system. - - rpcRoute := r.MarshallRoute(route) + rpcRoute, err := r.MarshallRoute(route) + if err != nil { + return nil, err + } routeResp := &lnrpc.QueryRoutesResponse{ Routes: []*lnrpc.Route{rpcRoute}, @@ -266,7 +280,7 @@ func calculateFeeLimit(feeLimit *lnrpc.FeeLimit, } // MarshallRoute marshalls an internal route to an rpc route struct. -func (r *RouterBackend) MarshallRoute(route *route.Route) *lnrpc.Route { +func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error) { resp := &lnrpc.Route{ TotalTimeLock: route.TotalTimeLock, TotalFees: int64(route.TotalFees().ToSatoshis()), @@ -290,6 +304,11 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) *lnrpc.Route { chanCapacity = incomingAmt.ToSatoshis() } + tlvMap, err := tlv.RecordsToMap(hop.TLVRecords) + if err != nil { + return nil, err + } + resp.Hops[i] = &lnrpc.Hop{ ChanId: hop.ChannelID, ChanCapacity: int64(chanCapacity), @@ -301,11 +320,13 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) *lnrpc.Route { PubKey: hex.EncodeToString( hop.PubKeyBytes[:], ), + TlvRecords: tlvMap, + TlvPayload: !hop.LegacyPayload, } incomingAmt = hop.AmtToForward } - return resp + return resp, nil } // UnmarshallHopByChannelLookup unmarshalls an rpc hop for which the pub key is @@ -331,11 +352,18 @@ func (r *RouterBackend) UnmarshallHopByChannelLookup(hop *lnrpc.Hop, return nil, fmt.Errorf("channel edge does not match expected node") } + tlvRecords, err := tlv.MapToRecords(hop.TlvRecords) + if err != nil { + return nil, err + } + return &route.Hop{ OutgoingTimeLock: hop.Expiry, AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), PubKeyBytes: pubKeyBytes, ChannelID: hop.ChanId, + TLVRecords: tlvRecords, + LegacyPayload: !hop.TlvPayload, }, nil } @@ -351,11 +379,21 @@ func UnmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*route.Hop, error) { var pubKeyBytes [33]byte copy(pubKeyBytes[:], pubKey) + var tlvRecords []tlv.Record + if hop.TlvRecords != nil { + tlvRecords, err = tlv.MapToRecords(hop.TlvRecords) + if err != nil { + return nil, err + } + } + return &route.Hop{ OutgoingTimeLock: hop.Expiry, AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), PubKeyBytes: pubKeyBytes, ChannelID: hop.ChanId, + TLVRecords: tlvRecords, + LegacyPayload: !hop.TlvPayload, }, nil } @@ -435,6 +473,16 @@ func (r *RouterBackend) extractIntentFromSendRequest( return nil, errors.New("timeout_seconds must be specified") } + if len(rpcPayReq.DestTlv) != 0 { + var err error + payIntent.FinalDestRecords, err = tlv.MapToRecords( + rpcPayReq.DestTlv, + ) + if err != nil { + return nil, err + } + } + payIntent.PayAttemptTimeout = time.Second * time.Duration(rpcPayReq.TimeoutSeconds) diff --git a/lnrpc/routerrpc/router_backend_test.go b/lnrpc/routerrpc/router_backend_test.go index 3f20f505..329200dc 100644 --- a/lnrpc/routerrpc/router_backend_test.go +++ b/lnrpc/routerrpc/router_backend_test.go @@ -10,6 +10,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/lnrpc" ) @@ -77,6 +78,7 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) { findRoute := func(source, target route.Vertex, amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams, + _ []tlv.Record, finalExpiry ...uint16) (*route.Route, error) { if int64(amt) != request.Amt*1000 { diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 04c9f957..1f9bc248 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -249,7 +249,7 @@ func (s *Server) EstimateRouteFee(ctx context.Context, s.cfg.RouterBackend.SelfNode, destNode, amtMsat, &routing.RestrictParams{ FeeLimit: feeLimit, - }, + }, nil, ) if err != nil { return nil, err @@ -550,9 +550,12 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash, status.State = PaymentState_SUCCEEDED status.Preimage = result.Preimage[:] - status.Route = router.MarshallRoute( + status.Route, err = router.MarshallRoute( result.Route, ) + if err != nil { + return err + } } else { state, err := marshallFailureReason( result.FailureReason, @@ -562,9 +565,12 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash, } status.State = state if result.Route != nil { - status.Route = router.MarshallRoute( + status.Route, err = router.MarshallRoute( result.Route, ) + if err != nil { + return err + } } } diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index d107cb85..aec2dedd 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -1026,10 +1026,15 @@ type SendRequest struct { //* //An optional maximum total time lock for the route. If zero, there is no //maximum enforced. - CltvLimit uint32 `protobuf:"varint,10,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + CltvLimit uint32 `protobuf:"varint,10,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` + //* + //An optional field that can be used to pass an arbitrary set of TLV records + //to a peer which understands the new records. This can be used to pass + //application specific data during the payment attempt. + DestTlv map[uint64][]byte `protobuf:"bytes,11,rep,name=dest_tlv,json=destTlv,proto3" json:"dest_tlv,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *SendRequest) Reset() { *m = SendRequest{} } @@ -1127,6 +1132,13 @@ func (m *SendRequest) GetCltvLimit() uint32 { return 0 } +func (m *SendRequest) GetDestTlv() map[uint64][]byte { + if m != nil { + return m.DestTlv + } + return nil +} + type SendResponse struct { PaymentError string `protobuf:"bytes,1,opt,name=payment_error,proto3" json:"payment_error,omitempty"` PaymentPreimage []byte `protobuf:"bytes,2,opt,name=payment_preimage,proto3" json:"payment_preimage,omitempty"` @@ -4817,10 +4829,16 @@ type QueryRoutesRequest struct { UseMissionControl bool `protobuf:"varint,9,opt,name=use_mission_control,json=useMissionControl,proto3" json:"use_mission_control,omitempty"` //* //A list of directed node pairs that will be ignored during path finding. - IgnoredPairs []*NodePair `protobuf:"bytes,10,rep,name=ignored_pairs,json=ignoredPairs,proto3" json:"ignored_pairs,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + IgnoredPairs []*NodePair `protobuf:"bytes,10,rep,name=ignored_pairs,json=ignoredPairs,proto3" json:"ignored_pairs,omitempty"` + //* + //An optional field that can be used to pass an arbitrary set of TLV records + //to a peer which understands the new records. This can be used to pass + //application specific data during the payment attempt. If the destination + //does not support the specified recrods, and error will be returned. + DestTlv map[uint64][]byte `protobuf:"bytes,11,rep,name=dest_tlv,json=destTlv,proto3" json:"dest_tlv,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *QueryRoutesRequest) Reset() { *m = QueryRoutesRequest{} } @@ -4912,6 +4930,13 @@ func (m *QueryRoutesRequest) GetIgnoredPairs() []*NodePair { return nil } +func (m *QueryRoutesRequest) GetDestTlv() map[uint64][]byte { + if m != nil { + return m.DestTlv + } + return nil +} + type NodePair struct { /// The sending node of the pair. From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` @@ -5068,10 +5093,20 @@ type Hop struct { //* //An optional public key of the hop. If the public key is given, the payment //can be executed without relying on a copy of the channel graph. - PubKey string `protobuf:"bytes,8,opt,name=pub_key,proto3" json:"pub_key,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PubKey string `protobuf:"bytes,8,opt,name=pub_key,proto3" json:"pub_key,omitempty"` + //* + //If set to true, then this hop will be encoded using the new variable length + //TLV format. Note that if any custom tlv_records below are specified, then + //this field MUST be set to true for them to be encoded properly. + TlvPayload bool `protobuf:"varint,9,opt,name=tlv_payload,proto3" json:"tlv_payload,omitempty"` + //* + //An optional set of key-value TLV records. This is useful within the context + //of the SendToRoute call as it allows callers to specify arbitrary K-V pairs + //to drop off at each hop within the onion. + TlvRecords map[uint64][]byte `protobuf:"bytes,10,rep,name=tlv_records,proto3" json:"tlv_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Hop) Reset() { *m = Hop{} } @@ -5157,6 +5192,20 @@ func (m *Hop) GetPubKey() string { return "" } +func (m *Hop) GetTlvPayload() bool { + if m != nil { + return m.TlvPayload + } + return false +} + +func (m *Hop) GetTlvRecords() map[uint64][]byte { + if m != nil { + return m.TlvRecords + } + return nil +} + //* //A path through the channel graph which runs over one or more channels in //succession. This struct carries all the information required to craft the @@ -8614,6 +8663,7 @@ func init() { proto.RegisterType((*TransactionDetails)(nil), "lnrpc.TransactionDetails") proto.RegisterType((*FeeLimit)(nil), "lnrpc.FeeLimit") proto.RegisterType((*SendRequest)(nil), "lnrpc.SendRequest") + proto.RegisterMapType((map[uint64][]byte)(nil), "lnrpc.SendRequest.DestTlvEntry") proto.RegisterType((*SendResponse)(nil), "lnrpc.SendResponse") proto.RegisterType((*SendToRouteRequest)(nil), "lnrpc.SendToRouteRequest") proto.RegisterType((*ChannelPoint)(nil), "lnrpc.ChannelPoint") @@ -8675,10 +8725,12 @@ func init() { proto.RegisterType((*ChannelBalanceRequest)(nil), "lnrpc.ChannelBalanceRequest") proto.RegisterType((*ChannelBalanceResponse)(nil), "lnrpc.ChannelBalanceResponse") proto.RegisterType((*QueryRoutesRequest)(nil), "lnrpc.QueryRoutesRequest") + proto.RegisterMapType((map[uint64][]byte)(nil), "lnrpc.QueryRoutesRequest.DestTlvEntry") proto.RegisterType((*NodePair)(nil), "lnrpc.NodePair") proto.RegisterType((*EdgeLocator)(nil), "lnrpc.EdgeLocator") proto.RegisterType((*QueryRoutesResponse)(nil), "lnrpc.QueryRoutesResponse") proto.RegisterType((*Hop)(nil), "lnrpc.Hop") + proto.RegisterMapType((map[uint64][]byte)(nil), "lnrpc.Hop.TlvRecordsEntry") proto.RegisterType((*Route)(nil), "lnrpc.Route") proto.RegisterType((*NodeInfoRequest)(nil), "lnrpc.NodeInfoRequest") proto.RegisterType((*NodeInfo)(nil), "lnrpc.NodeInfo") @@ -8740,508 +8792,515 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 8015 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7d, 0x5d, 0x6c, 0x24, 0x49, - 0xd2, 0x90, 0xab, 0x7f, 0xec, 0xee, 0xe8, 0x76, 0xbb, 0x9d, 0xfe, 0xeb, 0xe9, 0x9d, 0x9d, 0xf5, - 0xd6, 0xcd, 0x37, 0x33, 0xeb, 0xdd, 0xcf, 0x9e, 0xf5, 0xed, 0x2d, 0xf3, 0xed, 0xf0, 0xf1, 0xe1, - 0xbf, 0x19, 0xcf, 0xae, 0xc7, 0xe3, 0x2b, 0xcf, 0xdc, 0xb0, 0x7b, 0x1f, 0xea, 0x2b, 0x77, 0xa7, - 0xdb, 0xb5, 0xd3, 0x5d, 0xd5, 0x5b, 0x55, 0x6d, 0x8f, 0x77, 0x19, 0x1e, 0x10, 0x02, 0x04, 0x42, - 0x68, 0x41, 0x20, 0x40, 0x20, 0xa4, 0x3b, 0x1e, 0x38, 0xf1, 0x00, 0x3c, 0x80, 0x40, 0x3a, 0x89, - 0x47, 0x9e, 0x10, 0x42, 0xf7, 0x88, 0xc4, 0x09, 0x81, 0x84, 0x4e, 0x3c, 0x81, 0xc4, 0x3b, 0xca, - 0xc8, 0x9f, 0xca, 0xac, 0xaa, 0x1e, 0xcf, 0xde, 0x1d, 0xdf, 0x93, 0x3b, 0x23, 0xa2, 0xf2, 0x37, - 0x22, 0x32, 0x22, 0x32, 0x32, 0x0d, 0xd5, 0x70, 0xd4, 0x5d, 0x1f, 0x85, 0x41, 0x1c, 0x90, 0xf2, - 0xc0, 0x0f, 0x47, 0xdd, 0xf6, 0xf5, 0x7e, 0x10, 0xf4, 0x07, 0x74, 0xc3, 0x1d, 0x79, 0x1b, 0xae, - 0xef, 0x07, 0xb1, 0x1b, 0x7b, 0x81, 0x1f, 0x71, 0x22, 0xfb, 0x27, 0xd0, 0x78, 0x48, 0xfd, 0x63, - 0x4a, 0x7b, 0x0e, 0xfd, 0x6a, 0x4c, 0xa3, 0x98, 0xbc, 0x0f, 0xf3, 0x2e, 0xfd, 0x9a, 0xd2, 0x5e, - 0x67, 0xe4, 0x46, 0xd1, 0xe8, 0x2c, 0x74, 0x23, 0xda, 0xb2, 0x56, 0xad, 0x3b, 0x75, 0xa7, 0xc9, - 0x11, 0x47, 0x0a, 0x4e, 0xde, 0x85, 0x7a, 0xc4, 0x48, 0xa9, 0x1f, 0x87, 0xc1, 0xe8, 0xb2, 0x55, - 0x40, 0xba, 0x1a, 0x83, 0xed, 0x71, 0x90, 0x3d, 0x80, 0x39, 0xd5, 0x42, 0x34, 0x0a, 0xfc, 0x88, - 0x92, 0xbb, 0xb0, 0xd8, 0xf5, 0x46, 0x67, 0x34, 0xec, 0xe0, 0xc7, 0x43, 0x9f, 0x0e, 0x03, 0xdf, - 0xeb, 0xb6, 0xac, 0xd5, 0xe2, 0x9d, 0xaa, 0x43, 0x38, 0x8e, 0x7d, 0xf1, 0x58, 0x60, 0xc8, 0x6d, - 0x98, 0xa3, 0x3e, 0x87, 0xd3, 0x1e, 0x7e, 0x25, 0x9a, 0x6a, 0x24, 0x60, 0xf6, 0x81, 0xfd, 0xd7, - 0x0a, 0x30, 0xff, 0xc8, 0xf7, 0xe2, 0xe7, 0xee, 0x60, 0x40, 0x63, 0x39, 0xa6, 0xdb, 0x30, 0x77, - 0x81, 0x00, 0x1c, 0xd3, 0x45, 0x10, 0xf6, 0xc4, 0x88, 0x1a, 0x1c, 0x7c, 0x24, 0xa0, 0x13, 0x7b, - 0x56, 0x98, 0xd8, 0xb3, 0xdc, 0xe9, 0x2a, 0x4e, 0x98, 0xae, 0xdb, 0x30, 0x17, 0xd2, 0x6e, 0x70, - 0x4e, 0xc3, 0xcb, 0xce, 0x85, 0xe7, 0xf7, 0x82, 0x8b, 0x56, 0x69, 0xd5, 0xba, 0x53, 0x76, 0x1a, - 0x12, 0xfc, 0x1c, 0xa1, 0x64, 0x1b, 0xe6, 0xba, 0x67, 0xae, 0xef, 0xd3, 0x41, 0xe7, 0xc4, 0xed, - 0xbe, 0x18, 0x8f, 0xa2, 0x56, 0x79, 0xd5, 0xba, 0x53, 0xdb, 0xbc, 0xb6, 0x8e, 0xab, 0xba, 0xbe, - 0x73, 0xe6, 0xfa, 0xdb, 0x88, 0x39, 0xf6, 0xdd, 0x51, 0x74, 0x16, 0xc4, 0x4e, 0x43, 0x7c, 0xc1, - 0xc1, 0x91, 0xbd, 0x08, 0x44, 0x9f, 0x09, 0x3e, 0xf7, 0xf6, 0x3f, 0xb7, 0x60, 0xe1, 0x99, 0x3f, - 0x08, 0xba, 0x2f, 0x7e, 0xc3, 0x29, 0xca, 0x19, 0x43, 0xe1, 0x4d, 0xc7, 0x50, 0xfc, 0xae, 0x63, - 0x58, 0x86, 0x45, 0xb3, 0xb3, 0x62, 0x14, 0x14, 0x96, 0xd8, 0xd7, 0x7d, 0x2a, 0xbb, 0x25, 0x87, - 0xf1, 0x1e, 0x34, 0xbb, 0xe3, 0x30, 0xa4, 0x7e, 0x66, 0x1c, 0x73, 0x02, 0xae, 0x06, 0xf2, 0x2e, - 0xd4, 0x7d, 0x7a, 0x91, 0x90, 0x09, 0xde, 0xf5, 0xe9, 0x85, 0x24, 0xb1, 0x5b, 0xb0, 0x9c, 0x6e, - 0x46, 0x74, 0xe0, 0xbf, 0x59, 0x50, 0x7a, 0x16, 0xbf, 0x0c, 0xc8, 0x3a, 0x94, 0xe2, 0xcb, 0x11, - 0x97, 0x90, 0xc6, 0x26, 0x11, 0x43, 0xdb, 0xea, 0xf5, 0x42, 0x1a, 0x45, 0x4f, 0x2f, 0x47, 0xd4, - 0xa9, 0xbb, 0xbc, 0xd0, 0x61, 0x74, 0xa4, 0x05, 0x33, 0xa2, 0x8c, 0x0d, 0x56, 0x1d, 0x59, 0x24, - 0x37, 0x00, 0xdc, 0x61, 0x30, 0xf6, 0xe3, 0x4e, 0xe4, 0xc6, 0x38, 0x55, 0x45, 0x47, 0x83, 0x90, - 0xeb, 0x50, 0x1d, 0xbd, 0xe8, 0x44, 0xdd, 0xd0, 0x1b, 0xc5, 0xc8, 0x36, 0x55, 0x27, 0x01, 0x90, - 0xf7, 0xa1, 0x12, 0x8c, 0xe3, 0x51, 0xe0, 0xf9, 0xb1, 0x60, 0x95, 0x39, 0xd1, 0x97, 0x27, 0xe3, - 0xf8, 0x88, 0x81, 0x1d, 0x45, 0x40, 0x6e, 0xc2, 0x6c, 0x37, 0xf0, 0x4f, 0xbd, 0x70, 0xc8, 0x95, - 0x41, 0x6b, 0x1a, 0x5b, 0x33, 0x81, 0xf6, 0xbf, 0x2b, 0x40, 0xed, 0x69, 0xe8, 0xfa, 0x91, 0xdb, - 0x65, 0x00, 0xd6, 0xf5, 0xf8, 0x65, 0xe7, 0xcc, 0x8d, 0xce, 0x70, 0xb4, 0x55, 0x47, 0x16, 0xc9, - 0x32, 0x4c, 0xf3, 0x8e, 0xe2, 0x98, 0x8a, 0x8e, 0x28, 0x91, 0x0f, 0x60, 0xde, 0x1f, 0x0f, 0x3b, - 0x66, 0x5b, 0x45, 0xe4, 0x96, 0x2c, 0x82, 0x4d, 0xc0, 0x09, 0x5b, 0x6b, 0xde, 0x04, 0x1f, 0xa1, - 0x06, 0x21, 0x36, 0xd4, 0x45, 0x89, 0x7a, 0xfd, 0x33, 0x3e, 0xcc, 0xb2, 0x63, 0xc0, 0x58, 0x1d, - 0xb1, 0x37, 0xa4, 0x9d, 0x28, 0x76, 0x87, 0x23, 0x31, 0x2c, 0x0d, 0x82, 0xf8, 0x20, 0x76, 0x07, - 0x9d, 0x53, 0x4a, 0xa3, 0xd6, 0x8c, 0xc0, 0x2b, 0x08, 0xb9, 0x05, 0x8d, 0x1e, 0x8d, 0xe2, 0x8e, - 0x58, 0x14, 0x1a, 0xb5, 0x2a, 0x28, 0xfa, 0x29, 0x28, 0xab, 0x27, 0x74, 0x2f, 0x3a, 0x6c, 0x02, - 0xe8, 0xcb, 0x56, 0x95, 0xf7, 0x35, 0x81, 0x30, 0xce, 0x79, 0x48, 0x63, 0x6d, 0xf6, 0x22, 0xc1, - 0xa1, 0xf6, 0x01, 0x10, 0x0d, 0xbc, 0x4b, 0x63, 0xd7, 0x1b, 0x44, 0xe4, 0x63, 0xa8, 0xc7, 0x1a, - 0x31, 0xaa, 0xc2, 0x9a, 0x62, 0x27, 0xed, 0x03, 0xc7, 0xa0, 0xb3, 0x1f, 0x42, 0xe5, 0x01, 0xa5, - 0x07, 0xde, 0xd0, 0x8b, 0xc9, 0x32, 0x94, 0x4f, 0xbd, 0x97, 0x94, 0x33, 0x7c, 0x71, 0x7f, 0xca, - 0xe1, 0x45, 0xd2, 0x86, 0x99, 0x11, 0x0d, 0xbb, 0x54, 0x2e, 0xcf, 0xfe, 0x94, 0x23, 0x01, 0xdb, - 0x33, 0x50, 0x1e, 0xb0, 0x8f, 0xed, 0xff, 0x5d, 0x80, 0xda, 0x31, 0xf5, 0x95, 0x20, 0x11, 0x28, - 0xb1, 0x21, 0x0b, 0xe1, 0xc1, 0xdf, 0xe4, 0x1d, 0xa8, 0xe1, 0x34, 0x44, 0x71, 0xe8, 0xf9, 0x7d, - 0xc1, 0xbf, 0xc0, 0x40, 0xc7, 0x08, 0x21, 0x4d, 0x28, 0xba, 0x43, 0xc9, 0xbb, 0xec, 0x27, 0x13, - 0xb2, 0x91, 0x7b, 0x39, 0x64, 0xf2, 0xa8, 0x56, 0xb5, 0xee, 0xd4, 0x04, 0x6c, 0x9f, 0x2d, 0xeb, - 0x3a, 0x2c, 0xe8, 0x24, 0xb2, 0xf6, 0x32, 0xd6, 0x3e, 0xaf, 0x51, 0x8a, 0x46, 0x6e, 0xc3, 0x9c, - 0xa4, 0x0f, 0x79, 0x67, 0x71, 0x9d, 0xab, 0x4e, 0x43, 0x80, 0xe5, 0x10, 0xee, 0x40, 0xf3, 0xd4, - 0xf3, 0xdd, 0x41, 0xa7, 0x3b, 0x88, 0xcf, 0x3b, 0x3d, 0x3a, 0x88, 0x5d, 0x5c, 0xf1, 0xb2, 0xd3, - 0x40, 0xf8, 0xce, 0x20, 0x3e, 0xdf, 0x65, 0x50, 0xf2, 0x01, 0x54, 0x4f, 0x29, 0xed, 0xe0, 0x4c, - 0xb4, 0x2a, 0x86, 0xf4, 0xc8, 0xd9, 0x75, 0x2a, 0xa7, 0x72, 0x9e, 0xef, 0x40, 0x33, 0x18, 0xc7, - 0xfd, 0xc0, 0xf3, 0xfb, 0x1d, 0xa6, 0xaf, 0x3a, 0x5e, 0x0f, 0x39, 0xa0, 0xe4, 0x34, 0x24, 0x9c, - 0x69, 0x8d, 0x47, 0x3d, 0xf2, 0x36, 0x00, 0xb6, 0xcd, 0x2b, 0x86, 0x55, 0xeb, 0xce, 0xac, 0x53, - 0x65, 0x10, 0xac, 0xc8, 0xfe, 0xb7, 0x16, 0xd4, 0xf9, 0x9c, 0x8b, 0x8d, 0xf1, 0x26, 0xcc, 0xca, - 0xa1, 0xd1, 0x30, 0x0c, 0x42, 0x21, 0x67, 0x26, 0x90, 0xac, 0x41, 0x53, 0x02, 0x46, 0x21, 0xf5, - 0x86, 0x6e, 0x9f, 0x0a, 0xe5, 0x95, 0x81, 0x93, 0xcd, 0xa4, 0xc6, 0x30, 0x18, 0xc7, 0x54, 0xa8, - 0xe0, 0xba, 0x18, 0x9d, 0xc3, 0x60, 0x8e, 0x49, 0xc2, 0xe4, 0x2c, 0x67, 0xcd, 0x0c, 0x98, 0xfd, - 0xad, 0x05, 0x84, 0x75, 0xfd, 0x69, 0xc0, 0xab, 0x10, 0x53, 0x9e, 0x5e, 0x6e, 0xeb, 0x8d, 0x97, - 0xbb, 0x30, 0x69, 0xb9, 0x6d, 0x28, 0xf3, 0x9e, 0x97, 0x72, 0x7a, 0xce, 0x51, 0x9f, 0x96, 0x2a, - 0xc5, 0x66, 0xc9, 0xfe, 0xa9, 0x05, 0xf5, 0x1d, 0xbe, 0x7f, 0xa0, 0xc2, 0x23, 0x77, 0x81, 0x9c, - 0x8e, 0xfd, 0x1e, 0x5b, 0xa7, 0xf8, 0xa5, 0xd7, 0xeb, 0x9c, 0x5c, 0xc6, 0x34, 0xe2, 0x7d, 0xda, - 0x9f, 0x72, 0x72, 0x70, 0xe4, 0x03, 0x68, 0x1a, 0xd0, 0x28, 0x0e, 0x79, 0xcf, 0xf6, 0xa7, 0x9c, - 0x0c, 0x86, 0x4d, 0x14, 0x53, 0xa9, 0xe3, 0xb8, 0xe3, 0xf9, 0x3d, 0xfa, 0x12, 0xe7, 0x76, 0xd6, - 0x31, 0x60, 0xdb, 0x0d, 0xa8, 0xeb, 0xdf, 0xd9, 0x5f, 0x42, 0x45, 0x2a, 0x64, 0x54, 0x46, 0xa9, - 0x7e, 0x39, 0x1a, 0x84, 0xb4, 0xa1, 0x62, 0xf6, 0xc2, 0xa9, 0x7c, 0x97, 0xb6, 0xed, 0x3f, 0x03, - 0xcd, 0x03, 0xa6, 0x15, 0x7d, 0xcf, 0xef, 0x8b, 0x1d, 0x89, 0xa9, 0xea, 0xd1, 0xf8, 0xe4, 0x05, - 0xbd, 0x14, 0xbc, 0x25, 0x4a, 0x4c, 0xde, 0xcf, 0x82, 0x28, 0x16, 0xed, 0xe0, 0x6f, 0xfb, 0x3f, - 0x58, 0x40, 0xf6, 0xa2, 0xd8, 0x1b, 0xba, 0x31, 0x7d, 0x40, 0xd5, 0x22, 0x3f, 0x81, 0x3a, 0xab, - 0xed, 0x69, 0xb0, 0xc5, 0x75, 0x3e, 0xd7, 0x55, 0xef, 0x8b, 0x85, 0xc9, 0x7e, 0xb0, 0xae, 0x53, - 0x33, 0xb3, 0xf0, 0xd2, 0x31, 0x2a, 0x60, 0x7a, 0x25, 0x76, 0xc3, 0x3e, 0x8d, 0x71, 0x43, 0x10, - 0xe6, 0x04, 0x70, 0xd0, 0x4e, 0xe0, 0x9f, 0xb6, 0xff, 0x08, 0xe6, 0x33, 0x75, 0x30, 0x65, 0x93, - 0x0c, 0x83, 0xfd, 0x24, 0x8b, 0x50, 0x3e, 0x77, 0x07, 0x63, 0x2a, 0x76, 0x21, 0x5e, 0xf8, 0xa4, - 0x70, 0xcf, 0xb2, 0xbb, 0xb0, 0x60, 0xf4, 0x4b, 0xc8, 0x5b, 0x0b, 0x66, 0x98, 0xdc, 0xb3, 0xfd, - 0x16, 0x75, 0xa6, 0x23, 0x8b, 0x64, 0x13, 0x16, 0x4f, 0x29, 0x0d, 0xdd, 0x18, 0x8b, 0x9d, 0x11, - 0x0d, 0x71, 0x4d, 0x44, 0xcd, 0xb9, 0x38, 0xfb, 0xbf, 0x5b, 0x30, 0xc7, 0x64, 0xe2, 0xb1, 0xeb, - 0x5f, 0xca, 0xb9, 0x3a, 0xc8, 0x9d, 0xab, 0x3b, 0x62, 0xae, 0x52, 0xd4, 0xdf, 0x75, 0xa2, 0x8a, - 0xe9, 0x89, 0x22, 0xab, 0x50, 0x37, 0xba, 0x5b, 0xe6, 0x1b, 0x5c, 0xe4, 0xc6, 0x47, 0x34, 0xdc, - 0xbe, 0x8c, 0xe9, 0x6f, 0x3f, 0x95, 0xb7, 0xa0, 0x99, 0x74, 0x5b, 0xcc, 0x23, 0x81, 0x12, 0x63, - 0x4c, 0x51, 0x01, 0xfe, 0xb6, 0xff, 0x91, 0xc5, 0x09, 0x77, 0x02, 0x4f, 0x6d, 0x7e, 0x8c, 0x90, - 0xed, 0xa1, 0x92, 0x90, 0xfd, 0x9e, 0x68, 0x3c, 0xfc, 0xf6, 0x83, 0x25, 0xd7, 0xa0, 0x12, 0x51, - 0xbf, 0xd7, 0x71, 0x07, 0x03, 0xdc, 0x23, 0x2a, 0xce, 0x0c, 0x2b, 0x6f, 0x0d, 0x06, 0xf6, 0x6d, - 0x98, 0xd7, 0x7a, 0xf7, 0x9a, 0x71, 0x1c, 0x02, 0x39, 0xf0, 0xa2, 0xf8, 0x99, 0x1f, 0x8d, 0xb4, - 0xbd, 0xe5, 0x2d, 0xa8, 0x0e, 0x3d, 0x1f, 0x7b, 0xc6, 0x25, 0xb7, 0xec, 0x54, 0x86, 0x9e, 0xcf, - 0xfa, 0x15, 0x21, 0xd2, 0x7d, 0x29, 0x90, 0x05, 0x81, 0x74, 0x5f, 0x22, 0xd2, 0xbe, 0x07, 0x0b, - 0x46, 0x7d, 0xa2, 0xe9, 0x77, 0xa1, 0x3c, 0x8e, 0x5f, 0x06, 0x72, 0xe7, 0xaf, 0x09, 0x0e, 0x61, - 0x36, 0xa6, 0xc3, 0x31, 0xf6, 0x7d, 0x98, 0x3f, 0xa4, 0x17, 0x42, 0x90, 0x65, 0x47, 0x6e, 0x5d, - 0x69, 0x7f, 0x22, 0xde, 0x5e, 0x07, 0xa2, 0x7f, 0x9c, 0x08, 0x80, 0xb4, 0x46, 0x2d, 0xc3, 0x1a, - 0xb5, 0x6f, 0x01, 0x39, 0xf6, 0xfa, 0xfe, 0x63, 0x1a, 0x45, 0x6e, 0x5f, 0x89, 0x7e, 0x13, 0x8a, - 0xc3, 0xa8, 0x2f, 0x54, 0x15, 0xfb, 0x69, 0x7f, 0x1f, 0x16, 0x0c, 0x3a, 0x51, 0xf1, 0x75, 0xa8, - 0x46, 0x5e, 0xdf, 0x77, 0xe3, 0x71, 0x48, 0x45, 0xd5, 0x09, 0xc0, 0x7e, 0x00, 0x8b, 0x3f, 0xa2, - 0xa1, 0x77, 0x7a, 0x79, 0x55, 0xf5, 0x66, 0x3d, 0x85, 0x74, 0x3d, 0x7b, 0xb0, 0x94, 0xaa, 0x47, - 0x34, 0xcf, 0xd9, 0x57, 0xac, 0x64, 0xc5, 0xe1, 0x05, 0x4d, 0xf7, 0x15, 0x74, 0xdd, 0x67, 0x3f, - 0x03, 0xb2, 0x13, 0xf8, 0x3e, 0xed, 0xc6, 0x47, 0x94, 0x86, 0x89, 0x23, 0x9c, 0xf0, 0x6a, 0x6d, - 0x73, 0x45, 0xcc, 0x6c, 0x5a, 0xa1, 0x0a, 0x26, 0x26, 0x50, 0x1a, 0xd1, 0x70, 0x88, 0x15, 0x57, - 0x1c, 0xfc, 0x6d, 0x2f, 0xc1, 0x82, 0x51, 0xad, 0x70, 0x1d, 0x3e, 0x84, 0xa5, 0x5d, 0x2f, 0xea, - 0x66, 0x1b, 0x6c, 0xc1, 0xcc, 0x68, 0x7c, 0xd2, 0x49, 0x24, 0x51, 0x16, 0x99, 0x35, 0x99, 0xfe, - 0x44, 0x54, 0xf6, 0x57, 0x2c, 0x28, 0xed, 0x3f, 0x3d, 0xd8, 0x61, 0x7b, 0x85, 0xe7, 0x77, 0x83, - 0x21, 0xdb, 0x4b, 0xf9, 0xa0, 0x55, 0x79, 0xa2, 0x84, 0x5d, 0x87, 0x2a, 0x6e, 0xc1, 0xcc, 0x80, - 0x16, 0x3e, 0x6b, 0x02, 0x60, 0xc6, 0x3b, 0x7d, 0x39, 0xf2, 0x42, 0xb4, 0xce, 0xa5, 0xcd, 0x5d, - 0xc2, 0x6d, 0x26, 0x8b, 0xb0, 0x7f, 0x31, 0x0d, 0x33, 0x62, 0xf3, 0xc5, 0xf6, 0xba, 0xb1, 0x77, - 0x4e, 0x45, 0x4f, 0x44, 0x89, 0x99, 0x37, 0x21, 0x1d, 0x06, 0x31, 0xed, 0x18, 0xcb, 0x60, 0x02, - 0xd1, 0x39, 0x11, 0x7e, 0x23, 0x77, 0x67, 0x8a, 0x9c, 0xca, 0x00, 0xb2, 0xc9, 0x92, 0xb6, 0x57, - 0x09, 0x6d, 0x2f, 0x59, 0x64, 0x33, 0xd1, 0x75, 0x47, 0x6e, 0xd7, 0x8b, 0x2f, 0x85, 0x4a, 0x50, - 0x65, 0x56, 0xf7, 0x20, 0xe8, 0xba, 0xcc, 0x23, 0x1d, 0xb8, 0x7e, 0x97, 0x4a, 0xc7, 0xc7, 0x00, - 0x32, 0x27, 0x40, 0x74, 0x49, 0x92, 0x71, 0x47, 0x21, 0x05, 0x65, 0xfb, 0x77, 0x37, 0x18, 0x0e, - 0xbd, 0x98, 0xf9, 0x0e, 0x68, 0x37, 0x16, 0x1d, 0x0d, 0xc2, 0xdd, 0x2c, 0x2c, 0x5d, 0xf0, 0xd9, - 0xab, 0x4a, 0x37, 0x4b, 0x03, 0xb2, 0x5a, 0xd8, 0xae, 0xc3, 0xd4, 0xd8, 0x8b, 0x0b, 0x34, 0x12, - 0x8b, 0x8e, 0x06, 0x61, 0xeb, 0x30, 0xf6, 0x23, 0x1a, 0xc7, 0x03, 0xda, 0x53, 0x1d, 0xaa, 0x21, - 0x59, 0x16, 0x41, 0xee, 0xc2, 0x02, 0x77, 0x67, 0x22, 0x37, 0x0e, 0xa2, 0x33, 0x2f, 0xea, 0x44, - 0xcc, 0xf0, 0xaf, 0x23, 0x7d, 0x1e, 0x8a, 0xdc, 0x83, 0x95, 0x14, 0x38, 0xa4, 0x5d, 0xea, 0x9d, - 0xd3, 0x5e, 0x6b, 0x16, 0xbf, 0x9a, 0x84, 0x26, 0xab, 0x50, 0x63, 0x5e, 0xdc, 0x78, 0xd4, 0x73, - 0x99, 0x01, 0xd3, 0xc0, 0x75, 0xd0, 0x41, 0xe4, 0x43, 0x98, 0x1d, 0x51, 0x6e, 0xfd, 0x9c, 0xc5, - 0x83, 0x6e, 0xd4, 0x9a, 0x33, 0xb4, 0x1b, 0xe3, 0x5c, 0xc7, 0xa4, 0x60, 0x4c, 0xd9, 0x8d, 0xd0, - 0x5c, 0x77, 0x2f, 0x5b, 0x4d, 0x61, 0x32, 0x4b, 0x00, 0xca, 0x48, 0xe8, 0x9d, 0xbb, 0x31, 0x6d, - 0xcd, 0x73, 0x85, 0x2e, 0x8a, 0xec, 0x3b, 0xcf, 0xf7, 0x62, 0xcf, 0x8d, 0x83, 0xb0, 0x45, 0x10, - 0x97, 0x00, 0xd8, 0x24, 0x22, 0x7f, 0x44, 0xb1, 0x1b, 0x8f, 0xa3, 0xce, 0xe9, 0xc0, 0xed, 0x47, - 0xad, 0x05, 0x6e, 0x73, 0x66, 0x10, 0xe4, 0x63, 0x58, 0xe6, 0x1c, 0x81, 0xa8, 0x90, 0x46, 0x34, - 0x3c, 0xe7, 0x66, 0xc2, 0x22, 0xce, 0xc8, 0x04, 0x2c, 0x9b, 0x4a, 0xc1, 0x22, 0x99, 0x0f, 0x97, - 0xf8, 0x54, 0x4e, 0x40, 0xdb, 0xff, 0xc4, 0xe2, 0xdb, 0x82, 0x10, 0x21, 0xa5, 0xde, 0xdf, 0x81, - 0x1a, 0x17, 0x9e, 0x4e, 0xe0, 0x0f, 0x2e, 0x85, 0x3c, 0x01, 0x07, 0x3d, 0xf1, 0x07, 0x97, 0xe4, - 0x7b, 0x30, 0xeb, 0xf9, 0x3a, 0x09, 0xd7, 0x40, 0x75, 0x09, 0x44, 0xa2, 0x77, 0xa0, 0x36, 0x1a, - 0x9f, 0x0c, 0xbc, 0x2e, 0x27, 0x29, 0xf2, 0x5a, 0x38, 0x08, 0x09, 0x98, 0xdd, 0xce, 0xe7, 0x91, - 0x53, 0x94, 0x90, 0xa2, 0x26, 0x60, 0x8c, 0xc4, 0xde, 0x86, 0x45, 0xb3, 0x83, 0x42, 0xd5, 0xae, - 0x41, 0x45, 0x48, 0x66, 0xd4, 0xaa, 0xe1, 0xea, 0x36, 0xb4, 0xf8, 0x8e, 0x4f, 0x07, 0x8e, 0xc2, - 0xdb, 0xff, 0xa6, 0x04, 0x0b, 0x02, 0xba, 0x33, 0x08, 0x22, 0x7a, 0x3c, 0x1e, 0x0e, 0xdd, 0x30, - 0x47, 0xe4, 0xad, 0x2b, 0x44, 0xbe, 0x60, 0x8a, 0x3c, 0x13, 0xc4, 0x33, 0xd7, 0xf3, 0xb9, 0xd3, - 0xc1, 0xf5, 0x85, 0x06, 0x21, 0x77, 0x60, 0xae, 0x3b, 0x08, 0x22, 0x6e, 0x84, 0xeb, 0xe1, 0x85, - 0x34, 0x38, 0xab, 0xa2, 0xca, 0x79, 0x2a, 0x4a, 0x57, 0x31, 0xd3, 0x29, 0x15, 0x63, 0x43, 0x9d, - 0x55, 0x4a, 0xa5, 0xc6, 0x9c, 0xe1, 0x86, 0xb9, 0x0e, 0x63, 0xfd, 0x49, 0x0b, 0x34, 0xd7, 0x1e, - 0x73, 0x79, 0xe2, 0xec, 0x0d, 0x29, 0x6a, 0x64, 0x8d, 0xba, 0x2a, 0xc4, 0x39, 0x8b, 0x22, 0x0f, - 0x98, 0xcf, 0xc9, 0xda, 0x42, 0xb3, 0x00, 0xd0, 0x2c, 0xb8, 0x65, 0xae, 0x88, 0x3e, 0xf7, 0xeb, - 0xac, 0x30, 0x0e, 0x29, 0x9a, 0x0a, 0xda, 0x97, 0xf6, 0x5f, 0xb7, 0xa0, 0xa6, 0xe1, 0xc8, 0x12, - 0xcc, 0xef, 0x3c, 0x79, 0x72, 0xb4, 0xe7, 0x6c, 0x3d, 0x7d, 0xf4, 0xa3, 0xbd, 0xce, 0xce, 0xc1, - 0x93, 0xe3, 0xbd, 0xe6, 0x14, 0x03, 0x1f, 0x3c, 0xd9, 0xd9, 0x3a, 0xe8, 0x3c, 0x78, 0xe2, 0xec, - 0x48, 0xb0, 0x45, 0x96, 0x81, 0x38, 0x7b, 0x8f, 0x9f, 0x3c, 0xdd, 0x33, 0xe0, 0x05, 0xd2, 0x84, - 0xfa, 0xb6, 0xb3, 0xb7, 0xb5, 0xb3, 0x2f, 0x20, 0x45, 0xb2, 0x08, 0xcd, 0x07, 0xcf, 0x0e, 0x77, - 0x1f, 0x1d, 0x3e, 0xec, 0xec, 0x6c, 0x1d, 0xee, 0xec, 0x1d, 0xec, 0xed, 0x36, 0x4b, 0x64, 0x16, - 0xaa, 0x5b, 0xdb, 0x5b, 0x87, 0xbb, 0x4f, 0x0e, 0xf7, 0x76, 0x9b, 0x65, 0xfb, 0xbf, 0x5a, 0xb0, - 0x84, 0xbd, 0xee, 0xa5, 0x05, 0x64, 0x15, 0x6a, 0xdd, 0x20, 0x18, 0x31, 0x73, 0x3c, 0xd9, 0x70, - 0x74, 0x10, 0x63, 0x7e, 0x2e, 0xae, 0xa7, 0x41, 0xd8, 0xa5, 0x42, 0x3e, 0x00, 0x41, 0x0f, 0x18, - 0x84, 0x31, 0xbf, 0x58, 0x5e, 0x4e, 0xc1, 0xc5, 0xa3, 0xc6, 0x61, 0x9c, 0x64, 0x19, 0xa6, 0x4f, - 0x42, 0xea, 0x76, 0xcf, 0x84, 0x64, 0x88, 0x12, 0x79, 0x2f, 0xf1, 0x17, 0xbb, 0x6c, 0xf6, 0x07, - 0xb4, 0x87, 0x1c, 0x53, 0x71, 0xe6, 0x04, 0x7c, 0x47, 0x80, 0x99, 0x7e, 0x72, 0x4f, 0x5c, 0xbf, - 0x17, 0xf8, 0xb4, 0x27, 0x8c, 0xd1, 0x04, 0x60, 0x1f, 0xc1, 0x72, 0x7a, 0x7c, 0x42, 0xbe, 0x3e, - 0xd6, 0xe4, 0x8b, 0xdb, 0x86, 0xed, 0xc9, 0xab, 0xa9, 0xc9, 0xda, 0xaf, 0x0a, 0x50, 0x62, 0xa6, - 0xc2, 0x64, 0xb3, 0x42, 0xb7, 0xfe, 0x8a, 0x99, 0x58, 0x24, 0xba, 0xa0, 0x7c, 0xf3, 0xe0, 0x1b, - 0xac, 0x06, 0x49, 0xf0, 0x21, 0xed, 0x9e, 0xe3, 0x88, 0x15, 0x9e, 0x41, 0x98, 0x80, 0x30, 0xd3, - 0x1c, 0xbf, 0x16, 0x02, 0x22, 0xcb, 0x12, 0x87, 0x5f, 0xce, 0x24, 0x38, 0xfc, 0xae, 0x05, 0x33, - 0x9e, 0x7f, 0x12, 0x8c, 0xfd, 0x1e, 0x0a, 0x44, 0xc5, 0x91, 0x45, 0x8c, 0x7e, 0xa2, 0xa0, 0x7a, - 0x43, 0xc9, 0xfe, 0x09, 0x80, 0x6c, 0x42, 0x35, 0xba, 0xf4, 0xbb, 0x3a, 0xcf, 0x2f, 0x8a, 0x59, - 0x62, 0x73, 0xb0, 0x7e, 0x7c, 0xe9, 0x77, 0x91, 0xc3, 0x13, 0x32, 0xfb, 0x8f, 0xa0, 0x22, 0xc1, - 0x8c, 0x2d, 0x9f, 0x1d, 0x7e, 0x76, 0xf8, 0xe4, 0xf9, 0x61, 0xe7, 0xf8, 0xf3, 0xc3, 0x9d, 0xe6, - 0x14, 0x99, 0x83, 0xda, 0xd6, 0x0e, 0x72, 0x3a, 0x02, 0x2c, 0x46, 0x72, 0xb4, 0x75, 0x7c, 0xac, - 0x20, 0x05, 0x9b, 0x30, 0xf7, 0x3a, 0x42, 0x7b, 0x4c, 0x45, 0xf7, 0x3e, 0x86, 0x79, 0x0d, 0x96, - 0xd8, 0xf6, 0x23, 0x06, 0x48, 0xd9, 0xf6, 0x68, 0xc8, 0x71, 0x8c, 0xdd, 0x84, 0xc6, 0x43, 0x1a, - 0x3f, 0xf2, 0x4f, 0x03, 0x59, 0xd3, 0xff, 0x2c, 0xc1, 0x9c, 0x02, 0x89, 0x8a, 0xee, 0xc0, 0x9c, - 0xd7, 0xa3, 0x7e, 0xec, 0xc5, 0x97, 0x1d, 0xc3, 0x8b, 0x4f, 0x83, 0x99, 0x01, 0xec, 0x0e, 0x3c, - 0x57, 0x06, 0x99, 0x79, 0x81, 0x79, 0xb5, 0x6c, 0x77, 0x96, 0x1b, 0xae, 0xe2, 0x2b, 0x1e, 0x3c, - 0xc8, 0xc5, 0x31, 0x0d, 0xc4, 0xe0, 0x62, 0x8b, 0x51, 0x9f, 0x70, 0x43, 0x30, 0x0f, 0xc5, 0x96, - 0x8a, 0xd7, 0xc4, 0x86, 0x5c, 0xe6, 0x3b, 0xb8, 0x02, 0x64, 0xa2, 0xb8, 0xd3, 0x5c, 0x3f, 0xa6, - 0xa3, 0xb8, 0x5a, 0x24, 0xb8, 0x92, 0x89, 0x04, 0x33, 0xfd, 0x79, 0xe9, 0x77, 0x69, 0xaf, 0x13, - 0x07, 0x1d, 0xd4, 0xf3, 0xc8, 0x12, 0x15, 0x27, 0x0d, 0x26, 0xd7, 0x61, 0x26, 0xa6, 0x51, 0xec, - 0x53, 0x1e, 0x7e, 0xab, 0x6c, 0x17, 0x5a, 0x96, 0x23, 0x41, 0xcc, 0x6a, 0x1f, 0x87, 0x5e, 0xd4, - 0xaa, 0x63, 0x8c, 0x17, 0x7f, 0x93, 0x8f, 0x60, 0xe9, 0x84, 0x46, 0x71, 0xe7, 0x8c, 0xba, 0x3d, - 0x1a, 0x22, 0x7b, 0xf1, 0x60, 0x32, 0x37, 0x86, 0xf2, 0x91, 0x8c, 0x71, 0xcf, 0x69, 0x18, 0x79, - 0x81, 0x8f, 0x66, 0x50, 0xd5, 0x91, 0x45, 0x56, 0x1f, 0x1b, 0xbc, 0xda, 0xa4, 0xd5, 0x0c, 0xce, - 0xe1, 0xc0, 0xf3, 0x91, 0xe4, 0x26, 0x4c, 0xe3, 0x00, 0xa2, 0x56, 0x13, 0x79, 0xa6, 0x9e, 0xc8, - 0xbc, 0xe7, 0x3b, 0x02, 0xc7, 0x56, 0xb9, 0x1b, 0x0c, 0x82, 0x10, 0x6d, 0xa1, 0xaa, 0xc3, 0x0b, - 0xe6, 0xec, 0xf4, 0x43, 0x77, 0x74, 0x26, 0xec, 0xa1, 0x34, 0xf8, 0xd3, 0x52, 0xa5, 0xd6, 0xac, - 0xdb, 0x7f, 0x0a, 0xca, 0x58, 0x2d, 0x56, 0x87, 0x93, 0x69, 0x89, 0xea, 0x10, 0xda, 0x82, 0x19, - 0x9f, 0xc6, 0x17, 0x41, 0xf8, 0x42, 0x9e, 0x58, 0x88, 0xa2, 0xfd, 0x35, 0xfa, 0x4d, 0x2a, 0x82, - 0xff, 0x0c, 0x8d, 0x3e, 0xe6, 0xfd, 0xf2, 0xa5, 0x8a, 0xce, 0x5c, 0xe1, 0xca, 0x55, 0x10, 0x70, - 0x7c, 0xe6, 0x32, 0x5d, 0x6b, 0xac, 0x3e, 0xf7, 0x8e, 0x6b, 0x08, 0xdb, 0xe7, 0x8b, 0x7f, 0x13, - 0x1a, 0xf2, 0x6c, 0x20, 0xea, 0x0c, 0xe8, 0x69, 0x2c, 0x63, 0x5b, 0xfe, 0x78, 0x88, 0x2e, 0xf4, - 0x01, 0x3d, 0x8d, 0xed, 0x43, 0x98, 0x17, 0xfa, 0xef, 0xc9, 0x88, 0xca, 0xa6, 0xff, 0x20, 0xcf, - 0x8e, 0xa8, 0x6d, 0x2e, 0x98, 0x0a, 0x93, 0x9f, 0x86, 0x98, 0x94, 0xb6, 0x03, 0x44, 0xd7, 0xa7, - 0xa2, 0x42, 0xb1, 0x99, 0xcb, 0xe8, 0x9d, 0x18, 0x8e, 0x01, 0x63, 0xf3, 0x13, 0x8d, 0xbb, 0x5d, - 0x79, 0xa2, 0x53, 0x71, 0x64, 0xd1, 0xfe, 0x67, 0x16, 0x2c, 0x60, 0x6d, 0xd2, 0x12, 0x12, 0x7b, - 0xd6, 0xbd, 0xef, 0xd0, 0xcd, 0x7a, 0x57, 0x8f, 0x68, 0x2e, 0x42, 0x59, 0xdf, 0xc5, 0x78, 0xe1, - 0xbb, 0x47, 0x4a, 0x4a, 0xe9, 0x48, 0x89, 0xfd, 0xf7, 0x2d, 0x98, 0xe7, 0x1b, 0x09, 0xda, 0xc1, - 0x62, 0xf8, 0x7f, 0x1a, 0x66, 0xb9, 0x45, 0x20, 0xb4, 0x82, 0xe8, 0x68, 0xa2, 0x5a, 0x11, 0xca, - 0x89, 0xf7, 0xa7, 0x1c, 0x93, 0x98, 0xdc, 0x47, 0xab, 0xcc, 0xef, 0x20, 0x34, 0xe7, 0xec, 0xcf, - 0x9c, 0xeb, 0xfd, 0x29, 0x47, 0x23, 0xdf, 0xae, 0xc0, 0x34, 0x77, 0x22, 0xec, 0x87, 0x30, 0x6b, - 0x34, 0x64, 0x44, 0x69, 0xea, 0x3c, 0x4a, 0x93, 0x09, 0x87, 0x16, 0x72, 0xc2, 0xa1, 0xff, 0xaa, - 0x08, 0x84, 0x31, 0x4b, 0x6a, 0x35, 0x98, 0x17, 0x13, 0xf4, 0x0c, 0x9f, 0xb4, 0xee, 0xe8, 0x20, - 0xb2, 0x0e, 0x44, 0x2b, 0xca, 0x88, 0x35, 0xdf, 0x32, 0x73, 0x30, 0x4c, 0xcd, 0x0a, 0x8b, 0x43, - 0xd8, 0x06, 0xc2, 0xfb, 0xe6, 0xd3, 0x9e, 0x8b, 0x63, 0xbb, 0xe2, 0x68, 0x1c, 0x9d, 0xa1, 0xaf, - 0x20, 0xbc, 0x56, 0x59, 0x4e, 0xaf, 0xef, 0xf4, 0x95, 0xeb, 0x3b, 0x93, 0x89, 0x84, 0x69, 0x7e, - 0x53, 0xc5, 0xf4, 0x9b, 0x6e, 0xc2, 0xec, 0x90, 0xd9, 0xc9, 0xf1, 0xa0, 0xdb, 0x19, 0xb2, 0xd6, - 0x85, 0x93, 0x6a, 0x00, 0xc9, 0x1a, 0x34, 0xa5, 0xeb, 0xa2, 0x9c, 0x33, 0x7e, 0x9e, 0x91, 0x81, - 0x33, 0xfd, 0x9f, 0xc4, 0xc6, 0x6a, 0xd8, 0xd9, 0x04, 0xc0, 0x3c, 0xb1, 0x88, 0x71, 0x48, 0x67, - 0xec, 0x8b, 0xe3, 0x3f, 0xda, 0x43, 0xf7, 0xb4, 0xe2, 0x64, 0x11, 0xf6, 0xdf, 0xb6, 0xa0, 0xc9, - 0xd6, 0xcc, 0x60, 0xcb, 0x4f, 0x00, 0xa5, 0xe2, 0x0d, 0xb9, 0xd2, 0xa0, 0x25, 0xf7, 0xa0, 0x8a, - 0xe5, 0x60, 0x44, 0x7d, 0xc1, 0x93, 0x2d, 0x93, 0x27, 0x13, 0x7d, 0xb2, 0x3f, 0xe5, 0x24, 0xc4, - 0x1a, 0x47, 0xfe, 0x27, 0x0b, 0x6a, 0xa2, 0x95, 0xdf, 0x38, 0xf6, 0xd2, 0xd6, 0xce, 0x6b, 0x39, - 0x27, 0x25, 0xc7, 0xb3, 0x77, 0x60, 0x6e, 0xe8, 0xc6, 0xe3, 0x90, 0xed, 0xe7, 0x46, 0xdc, 0x25, - 0x0d, 0x66, 0x9b, 0x33, 0xaa, 0xce, 0xa8, 0x13, 0x7b, 0x83, 0x8e, 0xc4, 0x8a, 0x93, 0xd1, 0x3c, - 0x14, 0xd3, 0x20, 0x51, 0xec, 0xf6, 0xa9, 0xd8, 0x77, 0x79, 0xc1, 0x6e, 0xc1, 0xb2, 0x18, 0x50, - 0xca, 0xbe, 0xb6, 0xff, 0xc5, 0x2c, 0xac, 0x64, 0x50, 0x2a, 0x8f, 0x43, 0x04, 0x14, 0x06, 0xde, - 0xf0, 0x24, 0x50, 0xce, 0x89, 0xa5, 0xc7, 0x1a, 0x0c, 0x14, 0xe9, 0xc3, 0x92, 0x34, 0x30, 0xd8, - 0x9c, 0x26, 0x9b, 0x61, 0x01, 0x77, 0xb9, 0x0f, 0xcd, 0x25, 0x4c, 0x37, 0x28, 0xe1, 0xba, 0x10, - 0xe7, 0xd7, 0x47, 0xce, 0xa0, 0xa5, 0x2c, 0x19, 0xa1, 0xac, 0x35, 0x6b, 0x87, 0xb5, 0xf5, 0xc1, - 0x15, 0x6d, 0x19, 0xe6, 0xb8, 0x33, 0xb1, 0x36, 0x72, 0x09, 0x37, 0x24, 0x0e, 0xb5, 0x71, 0xb6, - 0xbd, 0xd2, 0x1b, 0x8d, 0x0d, 0x1d, 0x0d, 0xb3, 0xd1, 0x2b, 0x2a, 0x26, 0x5f, 0xc2, 0xf2, 0x85, - 0xeb, 0xc5, 0xb2, 0x5b, 0x9a, 0x6d, 0x51, 0xc6, 0x26, 0x37, 0xaf, 0x68, 0xf2, 0x39, 0xff, 0xd8, - 0xd8, 0xa2, 0x26, 0xd4, 0xd8, 0xfe, 0x45, 0x01, 0x1a, 0x66, 0x3d, 0x8c, 0x4d, 0x85, 0xec, 0x4b, - 0x1d, 0x28, 0xad, 0xd1, 0x14, 0x38, 0xeb, 0xdf, 0x17, 0xf2, 0xfc, 0x7b, 0xdd, 0xab, 0x2e, 0x5e, - 0x15, 0xb8, 0x2b, 0xbd, 0x59, 0xe0, 0xae, 0x9c, 0x1b, 0xb8, 0x9b, 0x1c, 0xdf, 0x99, 0xfe, 0x4d, - 0xe3, 0x3b, 0x33, 0xaf, 0x8d, 0xef, 0xb4, 0xff, 0xaf, 0x05, 0x24, 0xcb, 0xbd, 0xe4, 0x21, 0x0f, - 0x69, 0xf8, 0x74, 0x20, 0x94, 0xd8, 0xef, 0xbf, 0x99, 0x04, 0xc8, 0xd5, 0x92, 0x5f, 0x33, 0x51, - 0xd4, 0x93, 0x29, 0x74, 0xf3, 0x6a, 0xd6, 0xc9, 0x43, 0xa5, 0x82, 0x97, 0xa5, 0xab, 0x83, 0x97, - 0xe5, 0xab, 0x83, 0x97, 0xd3, 0xe9, 0xe0, 0x65, 0xfb, 0x2f, 0x5b, 0xb0, 0x90, 0xc3, 0x66, 0xbf, - 0xbb, 0x81, 0x33, 0xc6, 0x30, 0xb4, 0x4f, 0x41, 0x30, 0x86, 0x0e, 0x6c, 0xff, 0x05, 0x98, 0x35, - 0x44, 0xeb, 0x77, 0xd7, 0x7e, 0xda, 0x42, 0xe4, 0x9c, 0x6d, 0xc0, 0xda, 0xff, 0xab, 0x00, 0x24, - 0x2b, 0xde, 0x7f, 0xa2, 0x7d, 0xc8, 0xce, 0x53, 0x31, 0x67, 0x9e, 0xfe, 0xbf, 0xee, 0x3c, 0x1f, - 0xc0, 0xbc, 0xc8, 0x10, 0xd3, 0x02, 0x59, 0x9c, 0x63, 0xb2, 0x08, 0x66, 0x23, 0x9b, 0x91, 0xe3, - 0x8a, 0x91, 0x11, 0xa3, 0x6d, 0xbf, 0xa9, 0x00, 0xb2, 0xdd, 0x86, 0x96, 0x98, 0xa1, 0xbd, 0x73, - 0xea, 0xc7, 0xc7, 0xe3, 0x13, 0x9e, 0x22, 0xe5, 0x05, 0xbe, 0xfd, 0xaf, 0x8b, 0xca, 0xcc, 0x47, - 0xa4, 0x30, 0x28, 0x3e, 0x82, 0xba, 0xbe, 0x7d, 0x88, 0xe5, 0x48, 0xc5, 0x31, 0x99, 0x29, 0xa1, - 0x53, 0x91, 0x5d, 0x68, 0xa0, 0x92, 0xec, 0xa9, 0xef, 0x0a, 0xf8, 0xdd, 0x6b, 0xe2, 0x33, 0xfb, - 0x53, 0x4e, 0xea, 0x1b, 0xf2, 0x87, 0xd0, 0x30, 0x9d, 0x3f, 0x61, 0x95, 0xe4, 0x79, 0x03, 0xec, - 0x73, 0x93, 0x98, 0x6c, 0x41, 0x33, 0xed, 0x3d, 0x8a, 0x4c, 0x89, 0x09, 0x15, 0x64, 0xc8, 0xc9, - 0x3d, 0x71, 0x84, 0x58, 0xc6, 0xb8, 0xc9, 0x4d, 0xf3, 0x33, 0x6d, 0x9a, 0xd6, 0xf9, 0x1f, 0xed, - 0x50, 0xf1, 0x8f, 0x01, 0x12, 0x18, 0x69, 0x42, 0xfd, 0xc9, 0xd1, 0xde, 0x61, 0x67, 0x67, 0x7f, - 0xeb, 0xf0, 0x70, 0xef, 0xa0, 0x39, 0x45, 0x08, 0x34, 0x30, 0xcc, 0xb7, 0xab, 0x60, 0x16, 0x83, - 0x89, 0xc0, 0x8a, 0x84, 0x15, 0xc8, 0x22, 0x34, 0x1f, 0x1d, 0xa6, 0xa0, 0xc5, 0xed, 0xaa, 0x92, - 0x0f, 0x7b, 0x19, 0x16, 0x79, 0x06, 0xe0, 0x36, 0x67, 0x0f, 0x69, 0x9d, 0xfc, 0x63, 0x0b, 0x96, - 0x52, 0x88, 0x24, 0x95, 0x86, 0x1b, 0x20, 0xa6, 0x55, 0x62, 0x02, 0xf1, 0x58, 0x40, 0xda, 0x9a, - 0x29, 0x0d, 0x92, 0x45, 0x30, 0x9e, 0xd7, 0x6c, 0xd3, 0x94, 0x24, 0xe5, 0xa1, 0xec, 0x15, 0x9e, - 0xa7, 0x88, 0x19, 0x8d, 0x46, 0xc7, 0x4f, 0x79, 0x66, 0xa1, 0x8e, 0x48, 0x8e, 0x64, 0xcd, 0x2e, - 0xcb, 0x22, 0x73, 0x2b, 0x0c, 0x63, 0xc7, 0xec, 0x6f, 0x2e, 0xce, 0xfe, 0x1b, 0x45, 0x20, 0x3f, - 0x1c, 0xd3, 0xf0, 0x12, 0xf3, 0x65, 0x54, 0xd4, 0x74, 0x25, 0x1d, 0x13, 0x9c, 0x1e, 0x8d, 0x4f, - 0x3e, 0xa3, 0x97, 0x32, 0x83, 0xab, 0x90, 0x64, 0x70, 0xe5, 0x65, 0x51, 0x95, 0xae, 0xce, 0xa2, - 0x2a, 0x5f, 0x95, 0x45, 0xf5, 0x3d, 0x98, 0xf5, 0xfa, 0x7e, 0xc0, 0x64, 0x9e, 0xd9, 0x09, 0x51, - 0x6b, 0x7a, 0xb5, 0xc8, 0x7c, 0x6b, 0x01, 0x3c, 0x64, 0x30, 0x72, 0x3f, 0x21, 0xa2, 0xbd, 0x3e, - 0x66, 0xec, 0xe9, 0x5a, 0x60, 0xaf, 0xd7, 0xa7, 0x07, 0x41, 0xd7, 0x8d, 0x83, 0x10, 0x03, 0x3b, - 0xf2, 0x63, 0x06, 0x8f, 0xc8, 0x4d, 0x68, 0x44, 0xc1, 0x98, 0x59, 0x4e, 0x72, 0xac, 0x3c, 0x92, - 0x54, 0xe7, 0xd0, 0x23, 0x3e, 0xe2, 0x75, 0x58, 0x18, 0x47, 0xb4, 0x33, 0xf4, 0xa2, 0x88, 0xed, - 0x8e, 0xdd, 0xc0, 0x8f, 0xc3, 0x60, 0x20, 0xe2, 0x49, 0xf3, 0xe3, 0x88, 0x3e, 0xe6, 0x98, 0x1d, - 0x8e, 0x20, 0x1f, 0x25, 0x5d, 0x1a, 0xb9, 0x5e, 0x18, 0xb5, 0x00, 0xbb, 0x24, 0x47, 0xca, 0xfa, - 0x7d, 0xe4, 0x7a, 0xa1, 0xea, 0x0b, 0x2b, 0x44, 0x22, 0x43, 0x69, 0x1d, 0x2a, 0x12, 0xcf, 0xbc, - 0xd8, 0xd3, 0x30, 0x18, 0x4a, 0x2f, 0x96, 0xfd, 0x26, 0x0d, 0x28, 0xc4, 0x81, 0xf0, 0x40, 0x0b, - 0x71, 0x60, 0x7f, 0x0e, 0x35, 0x6d, 0x88, 0x98, 0x4e, 0x26, 0x2c, 0x26, 0xe1, 0xfe, 0x96, 0xb8, - 0x83, 0xe2, 0xd3, 0xc1, 0xa3, 0x1e, 0x79, 0x1f, 0xe6, 0x7b, 0x5e, 0x48, 0x31, 0x33, 0xb0, 0x13, - 0xd2, 0x73, 0x1a, 0x46, 0x32, 0x50, 0xd0, 0x54, 0x08, 0x87, 0xc3, 0xed, 0xfb, 0xb0, 0x60, 0xf0, - 0x85, 0x12, 0x9b, 0x69, 0x4c, 0xa9, 0x92, 0xb1, 0x4a, 0x33, 0xdd, 0x4a, 0xe0, 0xec, 0xbf, 0x5a, - 0x80, 0xe2, 0x7e, 0x30, 0xd2, 0x4f, 0x64, 0x2c, 0xf3, 0x44, 0x46, 0x58, 0x7c, 0x1d, 0x65, 0xd0, - 0x89, 0x6d, 0xd9, 0x00, 0x92, 0x35, 0x68, 0xb8, 0xc3, 0xb8, 0x13, 0x07, 0xcc, 0xc2, 0xbd, 0x70, - 0xc3, 0x1e, 0x97, 0x25, 0x5c, 0xcb, 0x14, 0x86, 0x2c, 0x42, 0x51, 0x19, 0x2a, 0x48, 0xc0, 0x8a, - 0xcc, 0xbd, 0xc2, 0xb3, 0xe8, 0x4b, 0x11, 0x68, 0x14, 0x25, 0x26, 0xaa, 0xe6, 0xf7, 0xdc, 0xb7, - 0xe5, 0xdb, 0x4d, 0x1e, 0x8a, 0x59, 0x9f, 0x8c, 0x7b, 0x87, 0x89, 0x31, 0xa7, 0xca, 0x7a, 0x08, - 0xbd, 0x62, 0x9e, 0xcc, 0xff, 0xda, 0x82, 0x32, 0xce, 0x0d, 0xdb, 0x3a, 0xb9, 0x6e, 0x51, 0x87, - 0x32, 0x38, 0x27, 0xb3, 0x4e, 0x1a, 0x4c, 0x6c, 0x23, 0x07, 0xb5, 0xa0, 0x06, 0xa4, 0xe7, 0xa1, - 0xae, 0x42, 0x95, 0x97, 0x54, 0x3e, 0x25, 0x92, 0x24, 0x40, 0x72, 0x03, 0x4a, 0x67, 0xc1, 0x48, - 0x7a, 0x17, 0x20, 0x4f, 0x54, 0x83, 0x91, 0x83, 0xf0, 0xa4, 0x3f, 0xac, 0x3e, 0x3e, 0x2c, 0x6e, - 0xc1, 0xa5, 0xc1, 0xcc, 0x6a, 0x56, 0xd5, 0xea, 0xd3, 0x94, 0x82, 0xda, 0xcf, 0x60, 0x8e, 0x71, - 0xaf, 0x16, 0xa4, 0x9e, 0xac, 0x47, 0xde, 0x63, 0xdb, 0x52, 0x77, 0x30, 0xee, 0x51, 0xdd, 0xc7, - 0xc3, 0x20, 0xa4, 0x80, 0x4b, 0xeb, 0xc6, 0xfe, 0x97, 0x16, 0x97, 0x0a, 0x56, 0x2f, 0xb9, 0x03, - 0x25, 0xa6, 0x0d, 0x52, 0x2e, 0xbd, 0x4a, 0xba, 0x60, 0x74, 0x0e, 0x52, 0x30, 0xa3, 0x07, 0xc3, - 0x84, 0x7a, 0xed, 0x3c, 0x48, 0x98, 0x38, 0x48, 0x6a, 0x64, 0x29, 0xbf, 0x22, 0x05, 0x25, 0xeb, - 0xda, 0x19, 0x4b, 0xc9, 0xd0, 0x30, 0x72, 0x17, 0xec, 0xf5, 0xa9, 0x76, 0xb6, 0xf2, 0x73, 0x0b, - 0x66, 0x8d, 0x3e, 0x91, 0x55, 0xa8, 0x0d, 0xdc, 0x28, 0x16, 0x07, 0xdf, 0x62, 0xe5, 0x75, 0x90, - 0xce, 0x43, 0x05, 0xf3, 0x18, 0x46, 0xc5, 0xea, 0x8b, 0x7a, 0xac, 0xfe, 0x2e, 0x54, 0x93, 0x24, - 0x64, 0xb3, 0x53, 0xac, 0x45, 0x99, 0x7e, 0x92, 0x10, 0x25, 0xd1, 0xe0, 0xb2, 0x16, 0x0d, 0xb6, - 0xef, 0x43, 0x4d, 0xa3, 0xd7, 0xa3, 0xb9, 0x96, 0x11, 0xcd, 0x55, 0xb9, 0x59, 0x85, 0x24, 0x37, - 0xcb, 0xfe, 0xb6, 0x00, 0xb3, 0x8c, 0xbd, 0x3d, 0xbf, 0x7f, 0x14, 0x0c, 0xbc, 0xee, 0x25, 0xb2, - 0x95, 0xe4, 0x64, 0xb1, 0x1b, 0x48, 0x36, 0x37, 0xc1, 0x4c, 0xa0, 0x64, 0x0c, 0x49, 0x48, 0xbf, - 0x2a, 0x33, 0xf5, 0xc0, 0x84, 0xeb, 0xc4, 0x8d, 0x84, 0xc4, 0x09, 0x6b, 0xd4, 0x00, 0x32, 0x21, - 0x66, 0x00, 0xcc, 0xb4, 0x1b, 0x7a, 0x83, 0x81, 0xc7, 0x69, 0xb9, 0xaf, 0x92, 0x87, 0x62, 0x6d, - 0xf6, 0xbc, 0xc8, 0x3d, 0x49, 0xce, 0xe1, 0x54, 0x19, 0x03, 0x5d, 0xee, 0x4b, 0x2d, 0xd0, 0x35, - 0x8d, 0x2a, 0xcb, 0x04, 0xa6, 0x17, 0x72, 0x26, 0xb3, 0x90, 0xf6, 0xbf, 0x2f, 0x40, 0x4d, 0x63, - 0x0b, 0x71, 0xf8, 0x6c, 0x6a, 0x65, 0x0d, 0x22, 0xf1, 0x86, 0xe7, 0xab, 0x41, 0xc8, 0x4d, 0xb3, - 0x45, 0x0c, 0x76, 0xa3, 0xb0, 0x1b, 0xec, 0x73, 0x1d, 0xaa, 0x8c, 0xed, 0x3f, 0x44, 0x37, 0x5b, - 0x64, 0xff, 0x2b, 0x80, 0xc4, 0x6e, 0x22, 0xb6, 0x9c, 0x60, 0x11, 0xf0, 0xda, 0xe3, 0xea, 0x7b, - 0x50, 0x17, 0xd5, 0xe0, 0xfa, 0xe2, 0x80, 0x13, 0xc1, 0x33, 0xd6, 0xde, 0x31, 0x28, 0xe5, 0x97, - 0x9b, 0xf2, 0xcb, 0xca, 0x55, 0x5f, 0x4a, 0x4a, 0xfb, 0xa1, 0xca, 0x02, 0x78, 0x18, 0xba, 0xa3, - 0x33, 0xa9, 0x4c, 0xee, 0xc2, 0x82, 0xd4, 0x19, 0x63, 0xdf, 0xf5, 0xfd, 0x60, 0xec, 0x77, 0xa9, - 0x4c, 0xe1, 0xca, 0x43, 0xd9, 0x3d, 0x95, 0xf0, 0x8b, 0x15, 0x91, 0x35, 0x28, 0x73, 0x5b, 0x82, - 0x6f, 0x5e, 0xf9, 0xea, 0x83, 0x93, 0x90, 0x3b, 0x50, 0xe6, 0x26, 0x45, 0x61, 0xa2, 0xc0, 0x73, - 0x02, 0x7b, 0x0d, 0xe6, 0x30, 0x9f, 0xdb, 0xd4, 0x7b, 0xe6, 0xc6, 0x37, 0xdd, 0xc5, 0x8c, 0x6f, - 0x7b, 0x11, 0xc8, 0x21, 0x97, 0x27, 0xfd, 0x2c, 0xef, 0xd7, 0x45, 0xa8, 0x69, 0x60, 0xa6, 0x97, - 0xf0, 0x00, 0xa6, 0xd3, 0xf3, 0xdc, 0x21, 0x8d, 0x69, 0x28, 0x64, 0x28, 0x05, 0x65, 0x74, 0xee, - 0x79, 0xbf, 0x13, 0x8c, 0xe3, 0x4e, 0x8f, 0xf6, 0x43, 0xca, 0xb7, 0x73, 0xb6, 0x37, 0x1a, 0x50, - 0x46, 0xc7, 0xb8, 0x58, 0xa3, 0xe3, 0x47, 0x26, 0x29, 0xa8, 0x3c, 0x99, 0xe3, 0x73, 0x54, 0x4a, - 0x4e, 0xe6, 0xf8, 0x8c, 0xa4, 0x35, 0x6a, 0x39, 0x47, 0xa3, 0x7e, 0x0c, 0xcb, 0x5c, 0x77, 0x0a, - 0xad, 0xd1, 0x49, 0x31, 0xd6, 0x04, 0x2c, 0x59, 0x83, 0x26, 0xeb, 0xb3, 0x14, 0x8b, 0xc8, 0xfb, - 0x9a, 0xcb, 0x96, 0xe5, 0x64, 0xe0, 0x8c, 0x16, 0xc3, 0xc5, 0x3a, 0x2d, 0x4f, 0x8f, 0xc8, 0xc0, - 0x91, 0xd6, 0x7d, 0x69, 0xd2, 0x56, 0x05, 0x6d, 0x0a, 0x4e, 0xee, 0xc1, 0xca, 0x90, 0xf6, 0x3c, - 0xd7, 0xac, 0x02, 0xa3, 0x37, 0x3c, 0xeb, 0x6a, 0x12, 0x9a, 0xb5, 0xc2, 0x66, 0xe1, 0xeb, 0x60, - 0x78, 0xe2, 0xf1, 0x0d, 0x8d, 0x07, 0xb6, 0x4b, 0x4e, 0x06, 0x6e, 0xcf, 0x42, 0xed, 0x38, 0x0e, - 0x46, 0x72, 0xe9, 0x1b, 0x50, 0xe7, 0x45, 0x91, 0xb0, 0xf7, 0x16, 0x5c, 0x43, 0x5e, 0x7d, 0x1a, - 0x8c, 0x82, 0x41, 0xd0, 0xbf, 0x34, 0xdc, 0xd3, 0xff, 0x68, 0xc1, 0x82, 0x81, 0x4d, 0xfc, 0x53, - 0x8c, 0xa5, 0xc9, 0x4c, 0x2b, 0xce, 0xde, 0xf3, 0xda, 0x76, 0xc0, 0x09, 0xf9, 0xb1, 0xc5, 0x33, - 0x91, 0x7c, 0xb5, 0x95, 0x5c, 0xc0, 0x92, 0x1f, 0x72, 0x5e, 0x6f, 0x65, 0x79, 0x5d, 0x7c, 0x2f, - 0xef, 0x5f, 0xc9, 0x2a, 0xfe, 0x50, 0x24, 0xb3, 0xf4, 0xc4, 0xa0, 0x8b, 0x66, 0x02, 0x82, 0x1e, - 0xce, 0x90, 0x3d, 0xe8, 0x2a, 0x60, 0x64, 0xff, 0xd4, 0x02, 0x48, 0x7a, 0x87, 0x29, 0x10, 0x6a, - 0x4b, 0xe3, 0x97, 0xfd, 0xb4, 0xed, 0xeb, 0x5d, 0xa8, 0xab, 0x53, 0xec, 0x64, 0x97, 0xac, 0x49, - 0x18, 0xb3, 0x2a, 0x6e, 0xc3, 0x5c, 0x7f, 0x10, 0x9c, 0xa0, 0xf5, 0x82, 0x19, 0xa0, 0x91, 0x48, - 0x5b, 0x6c, 0x70, 0xf0, 0x03, 0x01, 0x4d, 0xb6, 0xd4, 0x92, 0xbe, 0xa5, 0xe6, 0x6f, 0x90, 0x7f, - 0xb3, 0xa0, 0x8e, 0x12, 0x93, 0x99, 0x98, 0x28, 0xe1, 0x64, 0x33, 0xa3, 0xce, 0x27, 0x9c, 0xdc, - 0xa1, 0x65, 0x7e, 0x74, 0x65, 0x64, 0xf3, 0x3e, 0x34, 0x42, 0xae, 0x2b, 0xa5, 0x22, 0x2d, 0xbd, - 0x46, 0x91, 0xce, 0x86, 0xc6, 0x6e, 0xfc, 0x1e, 0x34, 0xdd, 0xde, 0x39, 0x0d, 0x63, 0x0f, 0x23, - 0x3d, 0x68, 0x3a, 0xf1, 0xc1, 0xcd, 0x69, 0x70, 0xb4, 0x50, 0x6e, 0xc3, 0x9c, 0x48, 0x20, 0x55, - 0x94, 0xe2, 0xda, 0x4c, 0x02, 0x66, 0x84, 0xf6, 0xcf, 0xe4, 0xa9, 0xa5, 0xb9, 0xb2, 0x93, 0x67, - 0x44, 0x1f, 0x5d, 0x21, 0x35, 0xba, 0xef, 0x89, 0x13, 0xc4, 0x9e, 0x0c, 0x27, 0x15, 0xb5, 0x74, - 0xa8, 0x9e, 0x38, 0xf1, 0x35, 0xa7, 0xb4, 0xf4, 0x26, 0x53, 0x6a, 0xff, 0xd2, 0x82, 0x99, 0xfd, - 0x60, 0xb4, 0x2f, 0x12, 0xc3, 0x50, 0x3c, 0x54, 0xe6, 0xb6, 0x2c, 0xbe, 0x26, 0x65, 0x2c, 0xd7, - 0x02, 0x99, 0x4d, 0x5b, 0x20, 0x7f, 0x16, 0xde, 0xc2, 0x60, 0x66, 0x18, 0x8c, 0x82, 0x90, 0x89, - 0xa8, 0x3b, 0xe0, 0xe6, 0x46, 0xe0, 0xc7, 0x67, 0x52, 0x85, 0xbe, 0x8e, 0x04, 0x23, 0x0c, 0xcc, - 0x71, 0xe6, 0x7e, 0x89, 0xb0, 0x98, 0xb8, 0x66, 0xcd, 0x22, 0xec, 0x3f, 0x80, 0x2a, 0x7a, 0x13, - 0x38, 0xac, 0x0f, 0xa0, 0x7a, 0x16, 0x8c, 0x3a, 0x67, 0x9e, 0x1f, 0x4b, 0x91, 0x6f, 0x24, 0x66, - 0xfe, 0x3e, 0x4e, 0x88, 0x22, 0xb0, 0xff, 0xee, 0x34, 0xcc, 0x3c, 0xf2, 0xcf, 0x03, 0xaf, 0x8b, - 0x27, 0xa4, 0x43, 0x3a, 0x0c, 0x64, 0x1e, 0x3b, 0xfb, 0x4d, 0xae, 0xc3, 0x0c, 0x26, 0x6e, 0x8e, - 0x38, 0xd3, 0xd6, 0x79, 0x26, 0x84, 0x00, 0xe1, 0x7d, 0xb6, 0xe4, 0x36, 0x11, 0x17, 0x2a, 0x0d, - 0xc2, 0xfc, 0xac, 0x50, 0xbf, 0x0d, 0x24, 0x4a, 0xc9, 0x3d, 0x81, 0xb2, 0x76, 0x4f, 0x80, 0xb5, - 0x25, 0x12, 0xd9, 0x78, 0xa6, 0x13, 0x6f, 0x4b, 0x80, 0xd0, 0x37, 0x0c, 0x29, 0x0f, 0x46, 0x2b, - 0x23, 0x8b, 0xf9, 0x86, 0x3a, 0x90, 0x19, 0x62, 0xfc, 0x03, 0x4e, 0xc3, 0x37, 0x00, 0x1d, 0xc4, - 0x4c, 0xd1, 0xf4, 0x45, 0x30, 0x7e, 0x11, 0x2f, 0x0d, 0x66, 0xfa, 0xbb, 0x47, 0x95, 0x9a, 0xe5, - 0xe3, 0x00, 0x7e, 0x63, 0x2a, 0x0d, 0xd7, 0x3c, 0x4a, 0x9e, 0x63, 0x2b, 0x3d, 0x4a, 0xc6, 0x30, - 0xee, 0x60, 0x70, 0xe2, 0x76, 0x5f, 0xe0, 0x3d, 0x40, 0x3c, 0xb3, 0xac, 0x3a, 0x26, 0x10, 0xd3, - 0xd1, 0x92, 0x55, 0xc5, 0x9c, 0x91, 0x92, 0xa3, 0x83, 0xc8, 0x26, 0xd4, 0xd0, 0x8b, 0x16, 0xeb, - 0xda, 0xc0, 0x75, 0x6d, 0xea, 0x6e, 0x36, 0xae, 0xac, 0x4e, 0xa4, 0x9f, 0xde, 0xce, 0x65, 0xb2, - 0x5e, 0xdd, 0x5e, 0x4f, 0x1c, 0x7a, 0x37, 0x79, 0x44, 0x40, 0x01, 0xd8, 0x8e, 0x2e, 0x26, 0x8c, - 0x13, 0xcc, 0x23, 0x81, 0x01, 0x23, 0x37, 0xa0, 0xc2, 0x3c, 0xbc, 0x91, 0xeb, 0xf5, 0x30, 0x4d, - 0x84, 0x3b, 0x9a, 0x0a, 0xc6, 0xea, 0x90, 0xbf, 0x71, 0xab, 0x5c, 0xc0, 0x59, 0x31, 0x60, 0x6c, - 0x6e, 0x54, 0x79, 0x98, 0xa4, 0xc9, 0x9a, 0x40, 0xf2, 0x21, 0x1e, 0x3d, 0xc6, 0x14, 0x73, 0x61, - 0x1b, 0x9b, 0x6f, 0x89, 0x31, 0x0b, 0xa6, 0x95, 0x7f, 0x8f, 0x19, 0x89, 0xc3, 0x29, 0xed, 0x2d, - 0xa8, 0xeb, 0x60, 0x52, 0x81, 0xd2, 0x93, 0xa3, 0xbd, 0xc3, 0xe6, 0x14, 0xa9, 0xc1, 0xcc, 0xf1, - 0xde, 0xd3, 0xa7, 0x07, 0x7b, 0xbb, 0x4d, 0x8b, 0xd4, 0xa1, 0xa2, 0x72, 0x07, 0x0b, 0xac, 0xb4, - 0xb5, 0xb3, 0xb3, 0x77, 0xf4, 0x74, 0x6f, 0xb7, 0x59, 0xb4, 0x63, 0x20, 0x5b, 0xbd, 0x9e, 0xa8, - 0x45, 0xc5, 0x39, 0x12, 0x7e, 0xb6, 0x0c, 0x7e, 0xce, 0xe1, 0xa9, 0x42, 0x3e, 0x4f, 0xbd, 0x76, - 0xe6, 0xed, 0x3d, 0xa8, 0x1d, 0x69, 0x97, 0xde, 0x50, 0xbc, 0xe4, 0x75, 0x37, 0x21, 0x96, 0x1a, - 0x44, 0xeb, 0x4e, 0x41, 0xef, 0x8e, 0xfd, 0x4f, 0x2d, 0x7e, 0xfb, 0x44, 0x75, 0x9f, 0xb7, 0x6d, - 0x43, 0x5d, 0x45, 0xfb, 0x92, 0xb4, 0x60, 0x03, 0xc6, 0x68, 0xb0, 0x2b, 0x9d, 0xe0, 0xf4, 0x34, - 0xa2, 0x32, 0x89, 0xcf, 0x80, 0x49, 0xbb, 0x86, 0x59, 0x4a, 0x1e, 0x6f, 0x21, 0x12, 0xc9, 0x7c, - 0x19, 0x38, 0xd3, 0xf2, 0x22, 0xa6, 0x24, 0xd3, 0x17, 0x55, 0x59, 0x65, 0x2f, 0xa7, 0x67, 0x79, - 0x0d, 0x2a, 0xaa, 0x5e, 0x53, 0x81, 0x49, 0x4a, 0x85, 0x67, 0x8a, 0x12, 0xfd, 0x1d, 0xa3, 0xd3, - 0x5c, 0x69, 0x67, 0x11, 0x64, 0x1d, 0xc8, 0xa9, 0x17, 0xa6, 0xc9, 0x8b, 0x48, 0x9e, 0x83, 0xb1, - 0x9f, 0xc3, 0x82, 0x64, 0x24, 0xcd, 0xe0, 0x32, 0x17, 0xd1, 0xba, 0x4a, 0x7c, 0x0a, 0x59, 0xf1, - 0xb1, 0x7f, 0x55, 0x84, 0x19, 0xb1, 0xd2, 0x99, 0x8b, 0x93, 0x7c, 0x9d, 0x0d, 0x18, 0x69, 0x19, - 0x17, 0xab, 0x50, 0xd6, 0x84, 0xd2, 0xcc, 0xa8, 0xc5, 0x62, 0x9e, 0x5a, 0x24, 0x50, 0x1a, 0xb9, - 0xf1, 0x19, 0x46, 0x04, 0xaa, 0x0e, 0xfe, 0x96, 0xa1, 0xb1, 0xb2, 0x19, 0x1a, 0xcb, 0xbb, 0x26, - 0xca, 0x77, 0xfc, 0xec, 0x35, 0xd1, 0xeb, 0x50, 0xc5, 0x4e, 0x68, 0x47, 0x99, 0x09, 0x80, 0x71, - 0x2f, 0x2f, 0xa0, 0x6c, 0x8b, 0x7b, 0x0e, 0x09, 0xe4, 0x3b, 0x28, 0xe2, 0x8f, 0x60, 0x9a, 0x27, - 0xda, 0x8b, 0x24, 0xcd, 0xeb, 0xf2, 0x38, 0x87, 0xd3, 0xc9, 0xbf, 0x3c, 0xdb, 0xc3, 0x11, 0xb4, - 0xfa, 0x35, 0xbd, 0x9a, 0x79, 0x4d, 0x4f, 0x0f, 0xda, 0xd5, 0xcd, 0xa0, 0x9d, 0xfd, 0x00, 0x66, - 0x8d, 0xea, 0x98, 0xca, 0x10, 0x49, 0x9e, 0xcd, 0x29, 0x32, 0x0b, 0xd5, 0x47, 0x87, 0x9d, 0x07, - 0x07, 0x8f, 0x1e, 0xee, 0x3f, 0x6d, 0x5a, 0xac, 0x78, 0xfc, 0x6c, 0x67, 0x67, 0x6f, 0x6f, 0x17, - 0x55, 0x08, 0xc0, 0xf4, 0x83, 0xad, 0x47, 0x07, 0xa8, 0x40, 0x76, 0x39, 0x6f, 0x8b, 0xba, 0x54, - 0x08, 0xfd, 0xf7, 0x81, 0x48, 0x97, 0x14, 0x93, 0x3d, 0x46, 0x03, 0x1a, 0xcb, 0xfc, 0xe3, 0x79, - 0x81, 0x79, 0xa4, 0x10, 0x32, 0x7d, 0x3e, 0xa9, 0x25, 0x11, 0x11, 0x31, 0x49, 0x69, 0x11, 0x11, - 0xa4, 0x8e, 0xc2, 0xdb, 0x6d, 0x68, 0xed, 0x52, 0x56, 0xdb, 0xd6, 0x60, 0x90, 0xea, 0x0e, 0xf3, - 0x2b, 0x72, 0x70, 0xc2, 0xe9, 0xf8, 0x21, 0x2c, 0x6d, 0xf1, 0x54, 0xe3, 0xdf, 0x55, 0x26, 0x9a, - 0xdd, 0x82, 0xe5, 0x74, 0x95, 0xa2, 0xb1, 0x07, 0x30, 0xbf, 0x4b, 0x4f, 0xc6, 0xfd, 0x03, 0x7a, - 0x9e, 0x34, 0x44, 0xa0, 0x14, 0x9d, 0x05, 0x17, 0x62, 0x7e, 0xf0, 0x37, 0x79, 0x1b, 0x60, 0xc0, - 0x68, 0x3a, 0xd1, 0x88, 0x76, 0xe5, 0xe5, 0x2e, 0x84, 0x1c, 0x8f, 0x68, 0xd7, 0xfe, 0x18, 0x88, - 0x5e, 0x8f, 0x98, 0x2f, 0x66, 0x16, 0x8c, 0x4f, 0x3a, 0xd1, 0x65, 0x14, 0xd3, 0xa1, 0xbc, 0xb5, - 0xa6, 0x83, 0xec, 0xdb, 0x50, 0x3f, 0x72, 0x2f, 0x1d, 0xfa, 0x95, 0xb8, 0x40, 0xbc, 0x02, 0x33, - 0x23, 0xf7, 0x92, 0xb1, 0xa0, 0x8a, 0x51, 0x22, 0xda, 0xfe, 0x3f, 0x05, 0x98, 0xe6, 0x94, 0xac, - 0xd6, 0x1e, 0x8d, 0x62, 0xcf, 0x47, 0x49, 0x93, 0xb5, 0x6a, 0xa0, 0x8c, 0x6c, 0x17, 0x72, 0x64, - 0x5b, 0x38, 0xd0, 0xf2, 0xa2, 0x8c, 0x10, 0x60, 0x03, 0xc6, 0x24, 0x2d, 0x49, 0x29, 0xe5, 0x91, - 0xac, 0x04, 0x90, 0x0a, 0x67, 0x27, 0xc6, 0x07, 0xef, 0x9f, 0x54, 0x5b, 0x42, 0x8c, 0x75, 0x50, - 0xae, 0x89, 0x33, 0xc3, 0xa5, 0x3d, 0x63, 0xe2, 0x64, 0x4c, 0x99, 0xca, 0x1b, 0x98, 0x32, 0xdc, - 0xab, 0x7e, 0x9d, 0x29, 0x03, 0x6f, 0x60, 0xca, 0xd8, 0x04, 0x9a, 0x78, 0x03, 0x97, 0x19, 0xcb, - 0x92, 0x77, 0xff, 0x81, 0x05, 0x4d, 0xc1, 0x45, 0x0a, 0x47, 0xde, 0x35, 0x9c, 0x82, 0xdc, 0x0b, - 0x21, 0x37, 0x61, 0x16, 0x4d, 0x75, 0xa5, 0x02, 0xc4, 0x21, 0x83, 0x01, 0x64, 0xe3, 0x90, 0x09, - 0x09, 0x43, 0x6f, 0x20, 0x16, 0x45, 0x07, 0x49, 0x2d, 0x12, 0xba, 0x22, 0x35, 0xd2, 0x72, 0x54, - 0xd9, 0xfe, 0x85, 0x05, 0xf3, 0x5a, 0x87, 0x05, 0x17, 0xde, 0x07, 0x29, 0x0d, 0x3c, 0x88, 0xcf, - 0x25, 0x77, 0xc5, 0x14, 0x9b, 0xe4, 0x33, 0x83, 0x18, 0x17, 0xd3, 0xbd, 0xc4, 0x0e, 0x46, 0xe3, - 0xa1, 0xd8, 0x55, 0x74, 0x10, 0x63, 0xa4, 0x0b, 0x4a, 0x5f, 0x28, 0x12, 0xbe, 0xaf, 0x19, 0x30, - 0x0c, 0x67, 0x32, 0x17, 0x43, 0x11, 0x95, 0x44, 0x38, 0x53, 0x07, 0xda, 0xff, 0xc5, 0x82, 0x05, - 0xee, 0x2b, 0x0a, 0xff, 0x5c, 0xdd, 0x35, 0x9c, 0xe6, 0x2e, 0x33, 0x97, 0xc8, 0xfd, 0x29, 0x47, - 0x94, 0xc9, 0x0f, 0xde, 0xd0, 0xbf, 0x55, 0xf9, 0x9a, 0x13, 0xd6, 0xa2, 0x98, 0xb7, 0x16, 0xaf, - 0x99, 0xe9, 0xbc, 0xc8, 0x72, 0x39, 0x37, 0xb2, 0xbc, 0x3d, 0x03, 0xe5, 0xa8, 0x1b, 0x8c, 0xa8, - 0xbd, 0x0c, 0x8b, 0xe6, 0xe0, 0x84, 0x0a, 0xfa, 0xa9, 0x05, 0xad, 0x07, 0xfc, 0x70, 0xc7, 0xf3, - 0xfb, 0xfb, 0x5e, 0x14, 0x07, 0xa1, 0xba, 0x92, 0x7d, 0x03, 0x20, 0x8a, 0xdd, 0x30, 0xe6, 0x57, - 0x09, 0x44, 0xbc, 0x36, 0x81, 0xb0, 0x3e, 0x52, 0xbf, 0xc7, 0xb1, 0x7c, 0x6d, 0x54, 0x39, 0x63, - 0x54, 0x09, 0x6f, 0xd6, 0x30, 0x4d, 0x6e, 0xf1, 0xfc, 0x65, 0x66, 0x3c, 0xd1, 0x73, 0xd4, 0xeb, - 0xdc, 0x4d, 0x4c, 0x41, 0xed, 0xff, 0x6c, 0xc1, 0x5c, 0xd2, 0x49, 0x3c, 0x67, 0x37, 0xb5, 0x83, - 0xb0, 0x47, 0x12, 0xed, 0x20, 0x23, 0xc9, 0x1e, 0x33, 0x50, 0x44, 0xdf, 0x34, 0x08, 0x4a, 0xac, - 0x28, 0x05, 0x63, 0x69, 0xf1, 0xe9, 0x20, 0x9e, 0x8d, 0xc8, 0x4c, 0x23, 0x61, 0xe6, 0x89, 0x12, - 0xde, 0x04, 0x19, 0xc6, 0xf8, 0x15, 0x8f, 0x8a, 0xcb, 0x22, 0x69, 0x72, 0xdb, 0x62, 0x06, 0xa1, - 0x68, 0x57, 0xe8, 0x7b, 0x6e, 0x85, 0xcf, 0x8f, 0xda, 0x73, 0xff, 0x96, 0x05, 0xd7, 0x72, 0x26, - 0x5e, 0x48, 0xcd, 0x2e, 0xcc, 0x9f, 0x2a, 0xa4, 0x9c, 0x1c, 0x2e, 0x3a, 0xcb, 0xf2, 0xa0, 0xd8, - 0x9c, 0x10, 0x27, 0xfb, 0x81, 0x32, 0x14, 0xf9, 0x74, 0x1b, 0xf9, 0xbe, 0x59, 0x84, 0x7d, 0x04, - 0xed, 0xbd, 0x97, 0x4c, 0x08, 0x77, 0xf4, 0x97, 0x85, 0x24, 0x2f, 0x6c, 0x66, 0x94, 0xcc, 0xd5, - 0x91, 0x87, 0x53, 0x98, 0x35, 0xea, 0x22, 0xdf, 0x7f, 0xd3, 0x4a, 0x74, 0x79, 0x91, 0x6b, 0xc5, - 0x9f, 0x46, 0x92, 0x59, 0xc7, 0x1a, 0xc8, 0x3e, 0x87, 0xb9, 0xc7, 0xe3, 0x41, 0xec, 0x25, 0xcf, - 0x24, 0x91, 0x1f, 0x88, 0x8f, 0xb0, 0x0a, 0x39, 0x75, 0xb9, 0x4d, 0xe9, 0x74, 0x6c, 0xc6, 0x86, - 0xac, 0xa6, 0x4e, 0xb6, 0xc5, 0x2c, 0xc2, 0xbe, 0x06, 0x2b, 0x49, 0x93, 0x7c, 0xee, 0xa4, 0xa2, - 0xfe, 0x99, 0xc5, 0xd3, 0x67, 0xcc, 0x57, 0x9b, 0xc8, 0x43, 0x58, 0x88, 0x3c, 0xbf, 0x3f, 0xa0, - 0x7a, 0x3d, 0x91, 0x98, 0x89, 0x25, 0xb3, 0x7b, 0xe2, 0x65, 0x27, 0x27, 0xef, 0x0b, 0xc6, 0x20, - 0xf9, 0x1d, 0x4d, 0x18, 0x24, 0x35, 0x25, 0x79, 0x03, 0xf8, 0x14, 0x1a, 0x66, 0x63, 0xe4, 0x9e, - 0x48, 0x18, 0x4e, 0x7a, 0xa6, 0x1f, 0x0f, 0x98, 0x9c, 0x61, 0x50, 0xda, 0xdf, 0x5a, 0xd0, 0x72, - 0x28, 0x63, 0x63, 0xaa, 0x35, 0x2a, 0xb8, 0xe7, 0x7e, 0xa6, 0xda, 0xc9, 0x03, 0x56, 0x89, 0xc8, - 0x72, 0xac, 0xeb, 0x13, 0x17, 0x65, 0x7f, 0x2a, 0x67, 0x54, 0xdb, 0x15, 0x98, 0x16, 0xe3, 0x5b, - 0x81, 0x25, 0xd1, 0x25, 0xd9, 0x9d, 0x24, 0xb6, 0x6c, 0x34, 0x6a, 0xc4, 0x96, 0xdb, 0xd0, 0xe2, - 0x77, 0xe5, 0xf5, 0x71, 0xf0, 0x0f, 0xd7, 0x5e, 0x41, 0x4d, 0x7b, 0x31, 0x80, 0xac, 0xc0, 0xc2, - 0xf3, 0x47, 0x4f, 0x0f, 0xf7, 0x8e, 0x8f, 0x3b, 0x47, 0xcf, 0xb6, 0x3f, 0xdb, 0xfb, 0xbc, 0xb3, - 0xbf, 0x75, 0xbc, 0xdf, 0x9c, 0x22, 0xcb, 0x40, 0x0e, 0xf7, 0x8e, 0x9f, 0xee, 0xed, 0x1a, 0x70, - 0x8b, 0xdc, 0x80, 0xf6, 0xb3, 0xc3, 0x67, 0xc7, 0x7b, 0xbb, 0x9d, 0xbc, 0xef, 0x0a, 0xe4, 0x6d, - 0xb8, 0x26, 0xf0, 0x39, 0x9f, 0x17, 0x37, 0xbf, 0x2d, 0x42, 0x83, 0x67, 0xf1, 0xf0, 0xc7, 0xbe, - 0x68, 0x48, 0x1e, 0xc3, 0x8c, 0x78, 0x35, 0x8e, 0xc8, 0xf9, 0x34, 0xdf, 0xa9, 0x6b, 0x2f, 0xa7, - 0xc1, 0x62, 0x12, 0x16, 0xfe, 0xd2, 0x2f, 0xff, 0xc7, 0xdf, 0x29, 0xcc, 0x92, 0xda, 0xc6, 0xf9, - 0x87, 0x1b, 0x7d, 0xea, 0x47, 0xac, 0x8e, 0x3f, 0x06, 0x48, 0xde, 0x42, 0x23, 0x2d, 0xe5, 0x84, - 0xa6, 0x1e, 0x8a, 0x6b, 0x5f, 0xcb, 0xc1, 0x88, 0x7a, 0xaf, 0x61, 0xbd, 0x0b, 0x76, 0x83, 0xd5, - 0xeb, 0xf9, 0x5e, 0xcc, 0xdf, 0x45, 0xfb, 0xc4, 0x5a, 0x23, 0x3d, 0xa8, 0xeb, 0xaf, 0x94, 0x11, - 0x19, 0x1f, 0xcf, 0x79, 0x67, 0xad, 0xfd, 0x56, 0x2e, 0x4e, 0x2e, 0x20, 0xb6, 0xb1, 0x64, 0x37, - 0x59, 0x1b, 0x63, 0xa4, 0x48, 0x5a, 0x19, 0x70, 0xb6, 0x4e, 0x1e, 0x23, 0x23, 0xd7, 0x35, 0x4e, - 0xcb, 0x3c, 0x85, 0xd6, 0x7e, 0x7b, 0x02, 0x56, 0xb4, 0xf5, 0x36, 0xb6, 0xb5, 0x62, 0x13, 0xd6, - 0x56, 0x17, 0x69, 0xe4, 0x53, 0x68, 0x9f, 0x58, 0x6b, 0x9b, 0x7f, 0xef, 0x16, 0x54, 0xd5, 0xb9, - 0x19, 0xf9, 0x12, 0x66, 0x8d, 0x34, 0x2b, 0x22, 0x87, 0x91, 0x97, 0x95, 0xd5, 0xbe, 0x9e, 0x8f, - 0x14, 0x0d, 0xdf, 0xc0, 0x86, 0x5b, 0x64, 0x99, 0x35, 0x2c, 0xf2, 0x94, 0x36, 0x30, 0x61, 0x90, - 0xdf, 0x37, 0x7a, 0xa1, 0x89, 0x2f, 0x6f, 0xec, 0x7a, 0x5a, 0xa2, 0x8c, 0xd6, 0xde, 0x9e, 0x80, - 0x15, 0xcd, 0x5d, 0xc7, 0xe6, 0x96, 0xc9, 0xa2, 0xde, 0x9c, 0x3a, 0xcf, 0xa2, 0x78, 0xc9, 0x4e, - 0x7f, 0xa7, 0x8b, 0xbc, 0xad, 0x18, 0x2b, 0xef, 0xfd, 0x2e, 0xc5, 0x22, 0xd9, 0x47, 0xbc, 0xec, - 0x16, 0x36, 0x45, 0x08, 0x2e, 0x9f, 0xfe, 0x4c, 0x17, 0x39, 0x81, 0x9a, 0xf6, 0xfe, 0x0c, 0xb9, - 0x36, 0xf1, 0xad, 0x9c, 0x76, 0x3b, 0x0f, 0x95, 0x37, 0x14, 0xbd, 0xfe, 0x0d, 0xb6, 0x2f, 0xff, - 0x18, 0xaa, 0xea, 0x45, 0x13, 0xb2, 0xa2, 0xbd, 0x30, 0xa3, 0xbf, 0xc0, 0xd2, 0x6e, 0x65, 0x11, - 0x79, 0xcc, 0xa7, 0xd7, 0xce, 0x98, 0xef, 0x39, 0xd4, 0xb4, 0x57, 0x4b, 0xd4, 0x00, 0xb2, 0x2f, - 0xa3, 0xa8, 0x01, 0xe4, 0x3c, 0x72, 0x62, 0xcf, 0x63, 0x13, 0x35, 0x52, 0x45, 0xfe, 0x8e, 0x5f, - 0x06, 0x11, 0x39, 0x80, 0x25, 0xa1, 0xa6, 0x4e, 0xe8, 0x77, 0x59, 0x86, 0x9c, 0xa7, 0xd1, 0xee, - 0x5a, 0xe4, 0x3e, 0x54, 0xe4, 0xe3, 0x34, 0x64, 0x39, 0xff, 0x91, 0x9d, 0xf6, 0x4a, 0x06, 0x2e, - 0xcc, 0x93, 0xcf, 0x01, 0x92, 0x27, 0x52, 0x94, 0x92, 0xc8, 0x3c, 0xb9, 0xa2, 0x38, 0x20, 0xfb, - 0x9e, 0x8a, 0xbd, 0x8c, 0x03, 0x6c, 0x12, 0x54, 0x12, 0x3e, 0xbd, 0x90, 0xf7, 0x69, 0x7f, 0x02, - 0x35, 0xed, 0x95, 0x14, 0x35, 0x7d, 0xd9, 0x17, 0x56, 0xd4, 0xf4, 0xe5, 0x3c, 0xaa, 0x62, 0xb7, - 0xb1, 0xf6, 0x45, 0x7b, 0x8e, 0xd5, 0x1e, 0x79, 0x7d, 0x7f, 0xc8, 0x09, 0xd8, 0x02, 0x9d, 0xc1, - 0xac, 0xf1, 0x14, 0x8a, 0x92, 0xd0, 0xbc, 0x87, 0x56, 0x94, 0x84, 0xe6, 0xbe, 0x9e, 0x22, 0xf9, - 0xcc, 0x9e, 0x67, 0xed, 0x9c, 0x23, 0x89, 0xd6, 0xd2, 0x17, 0x50, 0xd3, 0x9e, 0x35, 0x51, 0x63, - 0xc9, 0xbe, 0xa0, 0xa2, 0xc6, 0x92, 0xf7, 0x0a, 0xca, 0x22, 0xb6, 0xd1, 0xb0, 0x91, 0x15, 0xf0, - 0x66, 0x28, 0xab, 0xfb, 0x4b, 0x68, 0x98, 0x0f, 0x9d, 0x28, 0xd9, 0xcf, 0x7d, 0x32, 0x45, 0xc9, - 0xfe, 0x84, 0xd7, 0x51, 0x04, 0x4b, 0xaf, 0x2d, 0xa8, 0x46, 0x36, 0xbe, 0x11, 0x59, 0x37, 0xaf, - 0xc8, 0x0f, 0x99, 0x82, 0x13, 0x57, 0x75, 0xc9, 0x8a, 0xc6, 0xb5, 0xfa, 0x85, 0x5e, 0x25, 0x2f, - 0x99, 0x5b, 0xbd, 0x26, 0x33, 0xf3, 0xbb, 0xad, 0xb8, 0x6b, 0xe1, 0x95, 0x5d, 0x6d, 0xd7, 0xd2, - 0x6f, 0xf5, 0x6a, 0xbb, 0x96, 0x71, 0xb3, 0x37, 0xbd, 0x6b, 0xc5, 0x1e, 0xab, 0xc3, 0x87, 0xb9, - 0x54, 0x2a, 0xb8, 0x92, 0x8a, 0xfc, 0xdb, 0x3a, 0xed, 0x1b, 0xaf, 0xcf, 0x20, 0x37, 0x35, 0x88, - 0x54, 0x82, 0x1b, 0xf2, 0x6e, 0xd4, 0x9f, 0x87, 0xba, 0xfe, 0xc4, 0x03, 0xd1, 0x45, 0x39, 0xdd, - 0xd2, 0x5b, 0xb9, 0x38, 0x73, 0x71, 0x49, 0x5d, 0x6f, 0x86, 0xfc, 0x08, 0x96, 0x95, 0xa8, 0xeb, - 0xd9, 0xc5, 0x11, 0x79, 0x27, 0x27, 0xe7, 0x58, 0x37, 0x5e, 0xda, 0xd7, 0x26, 0x26, 0x25, 0xdf, - 0xb5, 0x18, 0xd3, 0x98, 0x77, 0xe7, 0x93, 0x0d, 0x23, 0xef, 0xc9, 0x80, 0x64, 0xc3, 0xc8, 0xbd, - 0x70, 0x2f, 0x99, 0x86, 0x2c, 0x18, 0x73, 0xc4, 0x0f, 0x2c, 0xc9, 0x17, 0x30, 0xa7, 0xdd, 0xdf, - 0x38, 0xbe, 0xf4, 0xbb, 0x4a, 0x00, 0xb2, 0x57, 0x0b, 0xdb, 0x79, 0xa6, 0xb9, 0xbd, 0x82, 0xf5, - 0xcf, 0xdb, 0xc6, 0xe4, 0x30, 0xe6, 0xdf, 0x81, 0x9a, 0x7e, 0x37, 0xe4, 0x35, 0xf5, 0xae, 0x68, - 0x28, 0xfd, 0x66, 0xdc, 0x5d, 0x8b, 0xfc, 0x43, 0x0b, 0xea, 0xc6, 0x4d, 0x0b, 0xe3, 0xb0, 0x3e, - 0x55, 0x4f, 0x4b, 0xc7, 0xe9, 0x15, 0xd9, 0x0e, 0x76, 0xf2, 0x60, 0xed, 0x53, 0x63, 0x12, 0xbe, - 0x31, 0xe2, 0x2f, 0xeb, 0xe9, 0x07, 0xf1, 0x5e, 0xa5, 0x09, 0xf4, 0xeb, 0x97, 0xaf, 0xee, 0x5a, - 0xe4, 0xe7, 0x16, 0x34, 0xcc, 0xa8, 0xa1, 0x5a, 0xaa, 0xdc, 0xf8, 0xa4, 0x5a, 0xaa, 0x09, 0xa1, - 0xc6, 0x2f, 0xb0, 0x97, 0x4f, 0xd7, 0x1c, 0xa3, 0x97, 0xe2, 0x55, 0x85, 0xdf, 0xae, 0xb7, 0xe4, - 0x13, 0xfe, 0x1e, 0xa6, 0x8c, 0xed, 0x13, 0x6d, 0xd7, 0x48, 0x2f, 0xaf, 0xfe, 0x86, 0xe3, 0x1d, - 0xeb, 0xae, 0x45, 0x7e, 0xc2, 0x1f, 0x82, 0x93, 0xe1, 0x67, 0xc6, 0x25, 0x6f, 0xfa, 0xbd, 0x7d, - 0x13, 0xc7, 0x74, 0xc3, 0xbe, 0x66, 0x8c, 0x29, 0xbd, 0x1f, 0x6f, 0xf1, 0xde, 0x89, 0xe7, 0x17, - 0x93, 0x0d, 0x25, 0xf3, 0x24, 0xe3, 0xe4, 0x4e, 0x0e, 0x79, 0x27, 0x05, 0xb9, 0xc1, 0xca, 0x6f, - 0x58, 0x8d, 0xbd, 0x86, 0x7d, 0xbd, 0x69, 0xbf, 0x33, 0xb1, 0xaf, 0x1b, 0x18, 0xfb, 0x63, 0x3d, - 0x3e, 0x02, 0x48, 0xce, 0xe1, 0x48, 0xea, 0x1c, 0x48, 0x09, 0x78, 0xf6, 0xa8, 0xce, 0x94, 0x17, - 0x79, 0x5c, 0xc4, 0x6a, 0xfc, 0x31, 0x57, 0x57, 0x8f, 0xe4, 0x09, 0x92, 0x6e, 0x94, 0x98, 0x07, - 0x66, 0x86, 0x51, 0x92, 0xae, 0xdf, 0x50, 0x56, 0xea, 0x38, 0xea, 0x19, 0xcc, 0x1e, 0x04, 0xc1, - 0x8b, 0xf1, 0x48, 0x9d, 0xa9, 0x9b, 0x61, 0xf9, 0x7d, 0x37, 0x3a, 0x6b, 0xa7, 0x46, 0x61, 0xaf, - 0x62, 0x55, 0x6d, 0xd2, 0xd2, 0xaa, 0xda, 0xf8, 0x26, 0x39, 0xe7, 0x7b, 0x45, 0x5c, 0x98, 0x57, - 0x3a, 0x50, 0x75, 0xbc, 0x6d, 0x56, 0x63, 0x68, 0xbe, 0x74, 0x13, 0x86, 0xf5, 0x2c, 0x7b, 0xbb, - 0x11, 0xc9, 0x3a, 0xef, 0x5a, 0xe4, 0x08, 0xea, 0xbb, 0xb4, 0x8b, 0x69, 0xe6, 0x18, 0xdb, 0x5e, - 0x48, 0x3a, 0xae, 0x82, 0xe2, 0xed, 0x59, 0x03, 0x68, 0xee, 0x0b, 0x23, 0xf7, 0x32, 0xa4, 0x5f, - 0x6d, 0x7c, 0x23, 0xa2, 0xe6, 0xaf, 0xe4, 0xbe, 0x20, 0x8f, 0x15, 0x8c, 0x7d, 0x21, 0x75, 0x0e, - 0x61, 0xec, 0x0b, 0x99, 0x73, 0x08, 0x63, 0xaa, 0xe5, 0xb1, 0x06, 0x19, 0xc0, 0x7c, 0xe6, 0xe8, - 0x42, 0x6d, 0x09, 0x93, 0x0e, 0x3c, 0xda, 0xab, 0x93, 0x09, 0xcc, 0xd6, 0xd6, 0xcc, 0xd6, 0x8e, - 0x61, 0x76, 0x97, 0xf2, 0xc9, 0xe2, 0x49, 0x83, 0xa9, 0xeb, 0x3a, 0x7a, 0x4a, 0x62, 0x5a, 0x81, - 0x23, 0xce, 0xdc, 0xf8, 0x31, 0x63, 0x8f, 0xfc, 0x18, 0x6a, 0x0f, 0x69, 0x2c, 0xb3, 0x04, 0x95, - 0xe9, 0x99, 0x4a, 0x1b, 0x6c, 0xe7, 0x24, 0x19, 0x9a, 0x3c, 0x83, 0xb5, 0x6d, 0xd0, 0x5e, 0x9f, - 0x72, 0xe5, 0xd4, 0xf1, 0x7a, 0xaf, 0xc8, 0x9f, 0xc3, 0xca, 0x55, 0x8a, 0xf4, 0xb2, 0x96, 0xf6, - 0xa5, 0x57, 0x3e, 0x97, 0x82, 0xe7, 0xd5, 0xec, 0x07, 0x3d, 0xaa, 0x99, 0x40, 0x3e, 0xd4, 0xb4, - 0x4b, 0x00, 0x4a, 0x80, 0xb2, 0x17, 0x46, 0x94, 0x00, 0xe5, 0xdc, 0x19, 0xb0, 0xef, 0x60, 0x3b, - 0x36, 0x59, 0x4d, 0xda, 0xe1, 0xf7, 0x04, 0x92, 0x96, 0x36, 0xbe, 0x71, 0x87, 0xf1, 0x2b, 0xf2, - 0x1c, 0x5f, 0x39, 0xd1, 0x33, 0x21, 0x13, 0x5b, 0x3a, 0x9d, 0x34, 0xa9, 0x26, 0x4b, 0x43, 0x99, - 0xf6, 0x35, 0x6f, 0x0a, 0x2d, 0xa5, 0x1f, 0x00, 0x1c, 0xc7, 0xc1, 0x68, 0xd7, 0xa5, 0xc3, 0xc0, - 0x4f, 0x74, 0x6d, 0x92, 0x87, 0x97, 0xe8, 0x2f, 0x2d, 0x19, 0x8f, 0x3c, 0xd7, 0x9c, 0x0f, 0x23, - 0x91, 0x54, 0x32, 0xd7, 0xc4, 0x54, 0x3d, 0x35, 0x21, 0x39, 0xe9, 0x7a, 0x77, 0x2d, 0xb2, 0x05, - 0x90, 0x9c, 0x5d, 0x29, 0x57, 0x22, 0x73, 0x2c, 0xa6, 0xd4, 0x5e, 0xce, 0x41, 0xd7, 0x11, 0x54, - 0x93, 0xc3, 0x90, 0x95, 0xe4, 0x1e, 0x8d, 0x71, 0x74, 0xa2, 0x76, 0xf0, 0xcc, 0x11, 0x85, 0xdd, - 0xc4, 0xa9, 0x02, 0x52, 0x61, 0x53, 0x85, 0xe7, 0x0e, 0x1e, 0x2c, 0xf0, 0x0e, 0x2a, 0x73, 0x04, - 0x73, 0xc8, 0xe4, 0x48, 0x72, 0x8e, 0x09, 0x94, 0x34, 0xe7, 0x46, 0xd9, 0x8d, 0x88, 0x08, 0xe3, - 0x56, 0x9e, 0xbf, 0xc6, 0x54, 0xf3, 0x10, 0xe6, 0x33, 0x61, 0x60, 0x25, 0xd2, 0x93, 0x22, 0xf3, - 0x4a, 0xa4, 0x27, 0x46, 0x90, 0xed, 0x25, 0x6c, 0x72, 0xce, 0x06, 0xf4, 0x80, 0x2e, 0xbc, 0xb8, - 0x7b, 0xc6, 0x9a, 0xfb, 0x99, 0x05, 0x0b, 0x39, 0x51, 0x5e, 0xf2, 0xae, 0x74, 0xa6, 0x27, 0x46, - 0x80, 0xdb, 0xb9, 0x41, 0x40, 0xfb, 0x18, 0xdb, 0x79, 0x4c, 0x3e, 0x33, 0x36, 0x36, 0x1e, 0x7f, - 0x13, 0x92, 0xf9, 0x5a, 0xa3, 0x22, 0xd7, 0xa2, 0xf8, 0x0a, 0x56, 0x78, 0x47, 0xb6, 0x06, 0x83, - 0x54, 0x80, 0xf2, 0x46, 0xe6, 0x49, 0x7c, 0x23, 0xf0, 0xda, 0x9e, 0xfc, 0x64, 0xfe, 0x04, 0x73, - 0x95, 0x77, 0x95, 0x8c, 0xa1, 0x99, 0x0e, 0xfa, 0x91, 0xc9, 0x75, 0xb5, 0xdf, 0x31, 0xdc, 0xc2, - 0x6c, 0xa0, 0xd0, 0xfe, 0x3d, 0x6c, 0xec, 0x1d, 0xbb, 0x9d, 0x37, 0x2f, 0xdc, 0x53, 0x64, 0xeb, - 0xf1, 0x17, 0x55, 0x84, 0x32, 0x35, 0x4e, 0xd9, 0xc0, 0xa4, 0x90, 0xaa, 0x72, 0x4c, 0xf3, 0x03, - 0x9c, 0xb7, 0xb0, 0xf9, 0x55, 0xfb, 0xad, 0xbc, 0xe6, 0x43, 0xfe, 0x09, 0x77, 0x51, 0x57, 0xd2, - 0x72, 0x2d, 0x7b, 0xb0, 0x9a, 0xb7, 0xde, 0x13, 0x7d, 0x8d, 0xd4, 0x5c, 0x4f, 0xdd, 0xb5, 0xb6, - 0x6f, 0x7f, 0xf1, 0x7b, 0x7d, 0x2f, 0x3e, 0x1b, 0x9f, 0xac, 0x77, 0x83, 0xe1, 0xc6, 0x40, 0x86, - 0xc8, 0x44, 0xc6, 0xf3, 0xc6, 0xc0, 0xef, 0x6d, 0xe0, 0xf7, 0x27, 0xd3, 0xf8, 0x1f, 0x36, 0xbe, - 0xff, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x82, 0x63, 0x17, 0xa9, 0x93, 0x63, 0x00, 0x00, + // 8125 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7d, 0x6d, 0x6c, 0x24, 0xd9, + 0xb5, 0x90, 0xab, 0x3f, 0xec, 0xee, 0xd3, 0xed, 0xee, 0xf6, 0xb5, 0xc7, 0xee, 0xe9, 0x9d, 0x9d, + 0x9d, 0xad, 0xcc, 0xdb, 0x99, 0x4c, 0xf6, 0xd9, 0xb3, 0x93, 0x64, 0x99, 0xb7, 0xf3, 0xc2, 0xc3, + 0x63, 0x7b, 0xc6, 0x93, 0x78, 0x3d, 0x4e, 0xd9, 0x93, 0x21, 0xc9, 0x43, 0x9d, 0x72, 0xf7, 0x75, + 0xbb, 0x32, 0xd5, 0x55, 0x9d, 0xaa, 0x6a, 0x7b, 0x3a, 0xcb, 0xf2, 0x03, 0x21, 0x84, 0x9e, 0x84, + 0x50, 0x40, 0x20, 0x40, 0x20, 0xa4, 0x04, 0x21, 0x9e, 0xf8, 0x01, 0xfc, 0x00, 0x81, 0x14, 0x89, + 0x9f, 0xfc, 0x42, 0x08, 0xbd, 0x9f, 0x48, 0x3c, 0x21, 0x90, 0x50, 0xc4, 0x3f, 0x24, 0xfe, 0xa3, + 0x7b, 0xee, 0x47, 0xdd, 0x5b, 0x55, 0x3d, 0x9e, 0x49, 0xc2, 0xfb, 0xe5, 0xbe, 0xe7, 0x9c, 0xba, + 0x9f, 0xe7, 0x9c, 0x7b, 0xce, 0xb9, 0xe7, 0x5e, 0x43, 0x3d, 0x9a, 0x0c, 0x36, 0x27, 0x51, 0x98, + 0x84, 0xa4, 0xea, 0x07, 0xd1, 0x64, 0xd0, 0xbb, 0x31, 0x0a, 0xc3, 0x91, 0x4f, 0xb7, 0xdc, 0x89, + 0xb7, 0xe5, 0x06, 0x41, 0x98, 0xb8, 0x89, 0x17, 0x06, 0x31, 0x27, 0xb2, 0x7f, 0x04, 0xad, 0xa7, + 0x34, 0x38, 0xa6, 0x74, 0xe8, 0xd0, 0x9f, 0x4c, 0x69, 0x9c, 0x90, 0xaf, 0xc1, 0x8a, 0x4b, 0x7f, + 0x4a, 0xe9, 0xb0, 0x3f, 0x71, 0xe3, 0x78, 0x72, 0x1e, 0xb9, 0x31, 0xed, 0x5a, 0xb7, 0xac, 0xbb, + 0x4d, 0xa7, 0xc3, 0x11, 0x47, 0x0a, 0x4e, 0x3e, 0x84, 0x66, 0xcc, 0x48, 0x69, 0x90, 0x44, 0xe1, + 0x64, 0xd6, 0x2d, 0x21, 0x5d, 0x83, 0xc1, 0xf6, 0x38, 0xc8, 0xf6, 0xa1, 0xad, 0x5a, 0x88, 0x27, + 0x61, 0x10, 0x53, 0x72, 0x1f, 0xd6, 0x06, 0xde, 0xe4, 0x9c, 0x46, 0x7d, 0xfc, 0x78, 0x1c, 0xd0, + 0x71, 0x18, 0x78, 0x83, 0xae, 0x75, 0xab, 0x7c, 0xb7, 0xee, 0x10, 0x8e, 0x63, 0x5f, 0x7c, 0x2e, + 0x30, 0xe4, 0x0e, 0xb4, 0x69, 0xc0, 0xe1, 0x74, 0x88, 0x5f, 0x89, 0xa6, 0x5a, 0x29, 0x98, 0x7d, + 0x60, 0xff, 0x8d, 0x12, 0xac, 0x3c, 0x0b, 0xbc, 0xe4, 0xa5, 0xeb, 0xfb, 0x34, 0x91, 0x63, 0xba, + 0x03, 0xed, 0x4b, 0x04, 0xe0, 0x98, 0x2e, 0xc3, 0x68, 0x28, 0x46, 0xd4, 0xe2, 0xe0, 0x23, 0x01, + 0x9d, 0xdb, 0xb3, 0xd2, 0xdc, 0x9e, 0x15, 0x4e, 0x57, 0x79, 0xce, 0x74, 0xdd, 0x81, 0x76, 0x44, + 0x07, 0xe1, 0x05, 0x8d, 0x66, 0xfd, 0x4b, 0x2f, 0x18, 0x86, 0x97, 0xdd, 0xca, 0x2d, 0xeb, 0x6e, + 0xd5, 0x69, 0x49, 0xf0, 0x4b, 0x84, 0x92, 0xc7, 0xd0, 0x1e, 0x9c, 0xbb, 0x41, 0x40, 0xfd, 0xfe, + 0xa9, 0x3b, 0x78, 0x35, 0x9d, 0xc4, 0xdd, 0xea, 0x2d, 0xeb, 0x6e, 0xe3, 0xc1, 0xf5, 0x4d, 0x5c, + 0xd5, 0xcd, 0x9d, 0x73, 0x37, 0x78, 0x8c, 0x98, 0xe3, 0xc0, 0x9d, 0xc4, 0xe7, 0x61, 0xe2, 0xb4, + 0xc4, 0x17, 0x1c, 0x1c, 0xdb, 0x6b, 0x40, 0xf4, 0x99, 0xe0, 0x73, 0x6f, 0xff, 0x0b, 0x0b, 0x56, + 0x5f, 0x04, 0x7e, 0x38, 0x78, 0xf5, 0x6b, 0x4e, 0x51, 0xc1, 0x18, 0x4a, 0x6f, 0x3b, 0x86, 0xf2, + 0xbb, 0x8e, 0x61, 0x1d, 0xd6, 0xcc, 0xce, 0x8a, 0x51, 0x50, 0xb8, 0xc6, 0xbe, 0x1e, 0x51, 0xd9, + 0x2d, 0x39, 0x8c, 0xaf, 0x42, 0x67, 0x30, 0x8d, 0x22, 0x1a, 0xe4, 0xc6, 0xd1, 0x16, 0x70, 0x35, + 0x90, 0x0f, 0xa1, 0x19, 0xd0, 0xcb, 0x94, 0x4c, 0xf0, 0x6e, 0x40, 0x2f, 0x25, 0x89, 0xdd, 0x85, + 0xf5, 0x6c, 0x33, 0xa2, 0x03, 0xff, 0xdd, 0x82, 0xca, 0x8b, 0xe4, 0x75, 0x48, 0x36, 0xa1, 0x92, + 0xcc, 0x26, 0x5c, 0x42, 0x5a, 0x0f, 0x88, 0x18, 0xda, 0xf6, 0x70, 0x18, 0xd1, 0x38, 0x3e, 0x99, + 0x4d, 0xa8, 0xd3, 0x74, 0x79, 0xa1, 0xcf, 0xe8, 0x48, 0x17, 0x96, 0x44, 0x19, 0x1b, 0xac, 0x3b, + 0xb2, 0x48, 0x6e, 0x02, 0xb8, 0xe3, 0x70, 0x1a, 0x24, 0xfd, 0xd8, 0x4d, 0x70, 0xaa, 0xca, 0x8e, + 0x06, 0x21, 0x37, 0xa0, 0x3e, 0x79, 0xd5, 0x8f, 0x07, 0x91, 0x37, 0x49, 0x90, 0x6d, 0xea, 0x4e, + 0x0a, 0x20, 0x5f, 0x83, 0x5a, 0x38, 0x4d, 0x26, 0xa1, 0x17, 0x24, 0x82, 0x55, 0xda, 0xa2, 0x2f, + 0xcf, 0xa7, 0xc9, 0x11, 0x03, 0x3b, 0x8a, 0x80, 0xdc, 0x86, 0xe5, 0x41, 0x18, 0x9c, 0x79, 0xd1, + 0x98, 0x2b, 0x83, 0xee, 0x22, 0xb6, 0x66, 0x02, 0xed, 0x7f, 0x5f, 0x82, 0xc6, 0x49, 0xe4, 0x06, + 0xb1, 0x3b, 0x60, 0x00, 0xd6, 0xf5, 0xe4, 0x75, 0xff, 0xdc, 0x8d, 0xcf, 0x71, 0xb4, 0x75, 0x47, + 0x16, 0xc9, 0x3a, 0x2c, 0xf2, 0x8e, 0xe2, 0x98, 0xca, 0x8e, 0x28, 0x91, 0x8f, 0x61, 0x25, 0x98, + 0x8e, 0xfb, 0x66, 0x5b, 0x65, 0xe4, 0x96, 0x3c, 0x82, 0x4d, 0xc0, 0x29, 0x5b, 0x6b, 0xde, 0x04, + 0x1f, 0xa1, 0x06, 0x21, 0x36, 0x34, 0x45, 0x89, 0x7a, 0xa3, 0x73, 0x3e, 0xcc, 0xaa, 0x63, 0xc0, + 0x58, 0x1d, 0x89, 0x37, 0xa6, 0xfd, 0x38, 0x71, 0xc7, 0x13, 0x31, 0x2c, 0x0d, 0x82, 0xf8, 0x30, + 0x71, 0xfd, 0xfe, 0x19, 0xa5, 0x71, 0x77, 0x49, 0xe0, 0x15, 0x84, 0x7c, 0x04, 0xad, 0x21, 0x8d, + 0x93, 0xbe, 0x58, 0x14, 0x1a, 0x77, 0x6b, 0x28, 0xfa, 0x19, 0x28, 0xab, 0x27, 0x72, 0x2f, 0xfb, + 0x6c, 0x02, 0xe8, 0xeb, 0x6e, 0x9d, 0xf7, 0x35, 0x85, 0x30, 0xce, 0x79, 0x4a, 0x13, 0x6d, 0xf6, + 0x62, 0xc1, 0xa1, 0xf6, 0x01, 0x10, 0x0d, 0xbc, 0x4b, 0x13, 0xd7, 0xf3, 0x63, 0xf2, 0x29, 0x34, + 0x13, 0x8d, 0x18, 0x55, 0x61, 0x43, 0xb1, 0x93, 0xf6, 0x81, 0x63, 0xd0, 0xd9, 0x4f, 0xa1, 0xf6, + 0x84, 0xd2, 0x03, 0x6f, 0xec, 0x25, 0x64, 0x1d, 0xaa, 0x67, 0xde, 0x6b, 0xca, 0x19, 0xbe, 0xbc, + 0xbf, 0xe0, 0xf0, 0x22, 0xe9, 0xc1, 0xd2, 0x84, 0x46, 0x03, 0x2a, 0x97, 0x67, 0x7f, 0xc1, 0x91, + 0x80, 0xc7, 0x4b, 0x50, 0xf5, 0xd9, 0xc7, 0xf6, 0xaf, 0xca, 0xd0, 0x38, 0xa6, 0x81, 0x12, 0x24, + 0x02, 0x15, 0x36, 0x64, 0x21, 0x3c, 0xf8, 0x9b, 0x7c, 0x00, 0x0d, 0x9c, 0x86, 0x38, 0x89, 0xbc, + 0x60, 0x24, 0xf8, 0x17, 0x18, 0xe8, 0x18, 0x21, 0xa4, 0x03, 0x65, 0x77, 0x2c, 0x79, 0x97, 0xfd, + 0x64, 0x42, 0x36, 0x71, 0x67, 0x63, 0x26, 0x8f, 0x6a, 0x55, 0x9b, 0x4e, 0x43, 0xc0, 0xf6, 0xd9, + 0xb2, 0x6e, 0xc2, 0xaa, 0x4e, 0x22, 0x6b, 0xaf, 0x62, 0xed, 0x2b, 0x1a, 0xa5, 0x68, 0xe4, 0x0e, + 0xb4, 0x25, 0x7d, 0xc4, 0x3b, 0x8b, 0xeb, 0x5c, 0x77, 0x5a, 0x02, 0x2c, 0x87, 0x70, 0x17, 0x3a, + 0x67, 0x5e, 0xe0, 0xfa, 0xfd, 0x81, 0x9f, 0x5c, 0xf4, 0x87, 0xd4, 0x4f, 0x5c, 0x5c, 0xf1, 0xaa, + 0xd3, 0x42, 0xf8, 0x8e, 0x9f, 0x5c, 0xec, 0x32, 0x28, 0xf9, 0x18, 0xea, 0x67, 0x94, 0xf6, 0x71, + 0x26, 0xba, 0x35, 0x43, 0x7a, 0xe4, 0xec, 0x3a, 0xb5, 0x33, 0x39, 0xcf, 0x77, 0xa1, 0x13, 0x4e, + 0x93, 0x51, 0xe8, 0x05, 0xa3, 0x3e, 0xd3, 0x57, 0x7d, 0x6f, 0x88, 0x1c, 0x50, 0x71, 0x5a, 0x12, + 0xce, 0xb4, 0xc6, 0xb3, 0x21, 0x79, 0x1f, 0x00, 0xdb, 0xe6, 0x15, 0xc3, 0x2d, 0xeb, 0xee, 0xb2, + 0x53, 0x67, 0x10, 0x5e, 0xd1, 0x67, 0x50, 0xc3, 0xf9, 0x4c, 0xfc, 0x8b, 0x6e, 0x03, 0x17, 0xfc, + 0x03, 0xd1, 0xaa, 0xb6, 0x12, 0x9b, 0xbb, 0x34, 0x4e, 0x4e, 0xfc, 0x0b, 0xb6, 0x9f, 0xce, 0x9c, + 0xa5, 0x21, 0x2f, 0xf5, 0x3e, 0x83, 0xa6, 0x8e, 0x60, 0x53, 0xff, 0x8a, 0xce, 0x70, 0xb9, 0x2a, + 0x0e, 0xfb, 0x49, 0xd6, 0xa0, 0x7a, 0xe1, 0xfa, 0x53, 0x2a, 0x14, 0x1b, 0x2f, 0x7c, 0x56, 0x7a, + 0x68, 0xd9, 0xff, 0xce, 0x82, 0x26, 0x6f, 0x41, 0x6c, 0xc8, 0xb7, 0x61, 0x59, 0x4e, 0x29, 0x8d, + 0xa2, 0x30, 0x12, 0xf2, 0x6d, 0x02, 0xc9, 0x3d, 0xe8, 0x48, 0xc0, 0x24, 0xa2, 0xde, 0xd8, 0x1d, + 0xc9, 0xba, 0x73, 0x70, 0xf2, 0x20, 0xad, 0x31, 0x0a, 0xa7, 0x09, 0x15, 0xaa, 0xbf, 0x29, 0xc6, + 0xe7, 0x30, 0x98, 0x63, 0x92, 0x30, 0xf9, 0x2e, 0xe0, 0x15, 0x03, 0x66, 0xff, 0xcc, 0x02, 0xc2, + 0xba, 0x7e, 0x12, 0xf2, 0x2a, 0xc4, 0x52, 0x67, 0xd9, 0xcc, 0x7a, 0x6b, 0x36, 0x2b, 0xcd, 0x63, + 0x33, 0x1b, 0xaa, 0xbc, 0xe7, 0x95, 0x82, 0x9e, 0x73, 0xd4, 0xb7, 0x2b, 0xb5, 0x72, 0xa7, 0x62, + 0xff, 0xdc, 0x82, 0xe6, 0x0e, 0xdf, 0xb7, 0x50, 0xd1, 0x92, 0xfb, 0x40, 0xce, 0xa6, 0xc1, 0x90, + 0xf1, 0x47, 0xf2, 0xda, 0x1b, 0xf6, 0x4f, 0x67, 0x09, 0x8d, 0x79, 0x9f, 0xf6, 0x17, 0x9c, 0x02, + 0x1c, 0xf9, 0x18, 0x3a, 0x06, 0x34, 0x4e, 0x22, 0xde, 0xb3, 0xfd, 0x05, 0x27, 0x87, 0x61, 0x13, + 0xc5, 0x54, 0xf9, 0x34, 0xe9, 0x7b, 0xc1, 0x90, 0xbe, 0xc6, 0xb9, 0x5d, 0x76, 0x0c, 0xd8, 0xe3, + 0x16, 0x34, 0xf5, 0xef, 0xec, 0x1f, 0x43, 0x4d, 0x6e, 0x04, 0xa8, 0x04, 0x33, 0xfd, 0x72, 0x34, + 0x08, 0xe9, 0x41, 0xcd, 0xec, 0x85, 0x53, 0x7b, 0x97, 0xb6, 0xed, 0x3f, 0x0f, 0x9d, 0x03, 0xa6, + 0x8d, 0x03, 0x2f, 0x18, 0x89, 0x9d, 0x90, 0x6d, 0x11, 0x93, 0xe9, 0xa9, 0x64, 0xd1, 0xba, 0x23, + 0x4a, 0x4c, 0xcf, 0x9c, 0x87, 0x71, 0x22, 0xda, 0xc1, 0xdf, 0xf6, 0x7f, 0xb4, 0x80, 0xec, 0xc5, + 0x89, 0x37, 0x76, 0x13, 0xfa, 0x84, 0xaa, 0x45, 0x7e, 0x0e, 0x4d, 0x56, 0xdb, 0x49, 0xb8, 0xcd, + 0xf7, 0x1a, 0xae, 0x23, 0xbf, 0x26, 0x16, 0x26, 0xff, 0xc1, 0xa6, 0x4e, 0xcd, 0xc5, 0xc7, 0xa8, + 0x80, 0xe9, 0xb3, 0xc4, 0x8d, 0x46, 0x34, 0xc1, 0x8d, 0x48, 0x98, 0x31, 0xc0, 0x41, 0x3b, 0x61, + 0x70, 0xd6, 0xfb, 0x03, 0x58, 0xc9, 0xd5, 0xa1, 0x4b, 0x5a, 0xbd, 0x40, 0xd2, 0xca, 0xba, 0xa4, + 0x0d, 0x60, 0xd5, 0xe8, 0x97, 0x90, 0xb7, 0x2e, 0x2c, 0x31, 0x7d, 0xc3, 0xf6, 0x79, 0xd4, 0xd5, + 0x8e, 0x2c, 0x92, 0x07, 0xb0, 0x76, 0x46, 0x69, 0xe4, 0x26, 0x58, 0xec, 0x4f, 0x68, 0x84, 0x6b, + 0x22, 0x6a, 0x2e, 0xc4, 0xd9, 0xff, 0xc3, 0x82, 0x36, 0x93, 0x89, 0xcf, 0xdd, 0x60, 0x26, 0xe7, + 0xea, 0xa0, 0x70, 0xae, 0xee, 0x6a, 0xea, 0x45, 0xa3, 0x7e, 0xd7, 0x89, 0x2a, 0x67, 0x27, 0x8a, + 0xdc, 0x82, 0xa6, 0xd1, 0xdd, 0x2a, 0xdf, 0x58, 0x63, 0x37, 0x39, 0xa2, 0xd1, 0xe3, 0x59, 0x42, + 0x7f, 0xf3, 0xa9, 0xfc, 0x08, 0x3a, 0x69, 0xb7, 0xc5, 0x3c, 0x12, 0xa8, 0x30, 0xc6, 0x14, 0x15, + 0xe0, 0x6f, 0xfb, 0x1f, 0x59, 0x9c, 0x70, 0x27, 0xf4, 0xd4, 0xa6, 0xcb, 0x08, 0xd9, 0xde, 0x2d, + 0x09, 0xd9, 0xef, 0xb9, 0x46, 0xcb, 0x6f, 0x3e, 0x58, 0x72, 0x1d, 0x6a, 0x31, 0x0d, 0x86, 0x7d, + 0xd7, 0xf7, 0x71, 0x6f, 0xaa, 0x39, 0x4b, 0xac, 0xbc, 0xed, 0xfb, 0xf6, 0x1d, 0x58, 0xd1, 0x7a, + 0xf7, 0x86, 0x71, 0x1c, 0x02, 0x39, 0xf0, 0xe2, 0xe4, 0x45, 0x10, 0x4f, 0xb4, 0x3d, 0xed, 0x3d, + 0xa8, 0x8f, 0xbd, 0x00, 0x7b, 0xc6, 0x25, 0xb7, 0xea, 0xd4, 0xc6, 0x5e, 0xc0, 0xfa, 0x15, 0x23, + 0xd2, 0x7d, 0x2d, 0x90, 0x25, 0x81, 0x74, 0x5f, 0x23, 0xd2, 0x7e, 0x08, 0xab, 0x46, 0x7d, 0xa2, + 0xe9, 0x0f, 0xa1, 0x3a, 0x4d, 0x5e, 0x87, 0xd2, 0xe2, 0x68, 0x08, 0x0e, 0x61, 0xb6, 0xad, 0xc3, + 0x31, 0xf6, 0x23, 0x58, 0x39, 0xa4, 0x97, 0x42, 0x90, 0x65, 0x47, 0x3e, 0xba, 0xd2, 0xee, 0x45, + 0xbc, 0xbd, 0x09, 0x44, 0xff, 0x38, 0x15, 0x00, 0x69, 0x05, 0x5b, 0x86, 0x15, 0x6c, 0x7f, 0x04, + 0xe4, 0xd8, 0x1b, 0x05, 0x9f, 0xd3, 0x38, 0x76, 0x47, 0x4a, 0xf4, 0x3b, 0x50, 0x1e, 0xc7, 0x23, + 0xa1, 0xaa, 0xd8, 0x4f, 0xfb, 0xeb, 0xb0, 0x6a, 0xd0, 0x89, 0x8a, 0x6f, 0x40, 0x3d, 0xf6, 0x46, + 0x81, 0x9b, 0x4c, 0x23, 0x2a, 0xaa, 0x4e, 0x01, 0xf6, 0x13, 0x58, 0xfb, 0x1e, 0x8d, 0xbc, 0xb3, + 0xd9, 0x55, 0xd5, 0x9b, 0xf5, 0x94, 0xb2, 0xf5, 0xec, 0xc1, 0xb5, 0x4c, 0x3d, 0xa2, 0x79, 0xce, + 0xbe, 0x62, 0x25, 0x6b, 0x0e, 0x2f, 0x68, 0xba, 0xaf, 0xa4, 0xeb, 0x3e, 0xfb, 0x05, 0x90, 0x9d, + 0x30, 0x08, 0xe8, 0x20, 0x39, 0xa2, 0x34, 0x4a, 0x1d, 0xf0, 0x94, 0x57, 0x1b, 0x0f, 0x36, 0xc4, + 0xcc, 0x66, 0x15, 0xaa, 0x60, 0x62, 0x02, 0x95, 0x09, 0x8d, 0xc6, 0x58, 0x71, 0xcd, 0xc1, 0xdf, + 0xf6, 0x35, 0x58, 0x35, 0xaa, 0x15, 0x2e, 0xcb, 0x27, 0x70, 0x6d, 0xd7, 0x8b, 0x07, 0xf9, 0x06, + 0xbb, 0xb0, 0x34, 0x99, 0x9e, 0xf6, 0x53, 0x49, 0x94, 0x45, 0x66, 0xc5, 0x66, 0x3f, 0x11, 0x95, + 0xfd, 0x75, 0x0b, 0x2a, 0xfb, 0x27, 0x07, 0x3b, 0x6c, 0xaf, 0xf0, 0x82, 0x41, 0x38, 0x66, 0x7b, + 0x29, 0x1f, 0xb4, 0x2a, 0xcf, 0x95, 0xb0, 0x1b, 0x50, 0xc7, 0x2d, 0x98, 0x19, 0xee, 0xc2, 0x57, + 0x4e, 0x01, 0xcc, 0x69, 0xa0, 0xaf, 0x27, 0x5e, 0x84, 0x5e, 0x81, 0xb4, 0xf5, 0x2b, 0xb8, 0xcd, + 0xe4, 0x11, 0xf6, 0x2f, 0x17, 0x61, 0x49, 0x6c, 0xbe, 0xd8, 0xde, 0x20, 0xf1, 0x2e, 0xa8, 0xe8, + 0x89, 0x28, 0x31, 0xf3, 0x26, 0xa2, 0xe3, 0x30, 0xa1, 0x7d, 0x63, 0x19, 0x4c, 0x20, 0x3a, 0x45, + 0xc2, 0x5f, 0xe5, 0x6e, 0x54, 0x99, 0x53, 0x19, 0x40, 0x36, 0x59, 0xd2, 0xe6, 0xab, 0xa0, 0xad, + 0x25, 0x8b, 0x6c, 0x26, 0x06, 0xee, 0xc4, 0x1d, 0x78, 0xc9, 0x4c, 0xa8, 0x04, 0x55, 0x66, 0x75, + 0xfb, 0xe1, 0xc0, 0x65, 0x9e, 0xb0, 0xef, 0x06, 0x03, 0x2a, 0x1d, 0x2e, 0x03, 0xc8, 0x9c, 0x0f, + 0xd1, 0x25, 0x49, 0xc6, 0x1d, 0x94, 0x0c, 0x94, 0xed, 0xdf, 0x83, 0x70, 0x3c, 0xf6, 0x12, 0xe6, + 0xb3, 0xa0, 0xbd, 0x5a, 0x76, 0x34, 0x08, 0x77, 0xef, 0xb0, 0x74, 0xc9, 0x67, 0xaf, 0x2e, 0xdd, + 0x3b, 0x0d, 0xc8, 0x6a, 0x61, 0xbb, 0x0e, 0x53, 0x63, 0xaf, 0x2e, 0xd1, 0x38, 0x2d, 0x3b, 0x1a, + 0x84, 0xad, 0xc3, 0x34, 0x88, 0x69, 0x92, 0xf8, 0x74, 0xa8, 0x3a, 0xd4, 0x40, 0xb2, 0x3c, 0x82, + 0xdc, 0x87, 0x55, 0xee, 0x46, 0xc5, 0x6e, 0x12, 0xc6, 0xe7, 0x5e, 0xdc, 0x8f, 0x99, 0xc3, 0xd1, + 0x44, 0xfa, 0x22, 0x14, 0x79, 0x08, 0x1b, 0x19, 0x70, 0x44, 0x07, 0xd4, 0xbb, 0xa0, 0xc3, 0xee, + 0x32, 0x7e, 0x35, 0x0f, 0x4d, 0x6e, 0x41, 0x83, 0x79, 0x8f, 0xd3, 0xc9, 0xd0, 0x65, 0x06, 0x4c, + 0x0b, 0xd7, 0x41, 0x07, 0x91, 0x4f, 0x60, 0x79, 0x42, 0xb9, 0xf5, 0x73, 0x9e, 0xf8, 0x83, 0xb8, + 0xdb, 0x36, 0xb4, 0x1b, 0xe3, 0x5c, 0xc7, 0xa4, 0x60, 0x4c, 0x39, 0x88, 0xd1, 0x4d, 0x70, 0x67, + 0xdd, 0x8e, 0x30, 0xd5, 0x25, 0x00, 0x65, 0x24, 0xf2, 0x2e, 0xdc, 0x84, 0x76, 0x57, 0xb8, 0x42, + 0x17, 0x45, 0xf6, 0x9d, 0x17, 0x78, 0x89, 0xe7, 0x26, 0x61, 0xd4, 0x25, 0x88, 0x4b, 0x01, 0x6c, + 0x12, 0x91, 0x3f, 0xe2, 0xc4, 0x4d, 0xa6, 0x71, 0xff, 0xcc, 0x77, 0x47, 0x71, 0x77, 0x95, 0xdb, + 0x9c, 0x39, 0x04, 0xf9, 0x14, 0xd6, 0x39, 0x47, 0x20, 0x2a, 0xa2, 0x31, 0x8d, 0x2e, 0xb8, 0x99, + 0xb0, 0x86, 0x33, 0x32, 0x07, 0xcb, 0xa6, 0x52, 0xb0, 0x48, 0xee, 0xc3, 0x6b, 0x7c, 0x2a, 0xe7, + 0xa0, 0xed, 0x7f, 0x62, 0xf1, 0x6d, 0x41, 0x88, 0x90, 0x52, 0xef, 0x1f, 0x40, 0x83, 0x0b, 0x4f, + 0x3f, 0x0c, 0xfc, 0x99, 0x90, 0x27, 0xe0, 0xa0, 0xe7, 0x81, 0x3f, 0x23, 0x5f, 0x81, 0x65, 0x2f, + 0xd0, 0x49, 0xb8, 0x06, 0x6a, 0x4a, 0x20, 0x12, 0x7d, 0x00, 0x8d, 0xc9, 0xf4, 0xd4, 0xf7, 0x06, + 0x9c, 0xa4, 0xcc, 0x6b, 0xe1, 0x20, 0x24, 0x60, 0x76, 0x3b, 0x9f, 0x47, 0x4e, 0x51, 0x41, 0x8a, + 0x86, 0x80, 0x31, 0x12, 0xfb, 0x31, 0xac, 0x99, 0x1d, 0x14, 0xaa, 0xf6, 0x1e, 0xd4, 0x84, 0x64, + 0xc6, 0xc2, 0x79, 0x6a, 0x69, 0x71, 0xa5, 0x80, 0xfa, 0x8e, 0xc2, 0xdb, 0xff, 0xb6, 0x02, 0xab, + 0x02, 0xba, 0xe3, 0x87, 0x31, 0x3d, 0x9e, 0x8e, 0xc7, 0x6e, 0x54, 0x20, 0xf2, 0xd6, 0x15, 0x22, + 0x5f, 0x32, 0x45, 0x9e, 0x09, 0xe2, 0xb9, 0xeb, 0x05, 0xdc, 0xe9, 0xe0, 0xfa, 0x42, 0x83, 0x90, + 0xbb, 0xd0, 0x1e, 0xf8, 0x61, 0xcc, 0x8d, 0x70, 0x3d, 0xac, 0x91, 0x05, 0xe7, 0x55, 0x54, 0xb5, + 0x48, 0x45, 0xe9, 0x2a, 0x66, 0x31, 0xa3, 0x62, 0x6c, 0x68, 0xb2, 0x4a, 0xa9, 0xd4, 0x98, 0x4b, + 0xdc, 0x30, 0xd7, 0x61, 0xac, 0x3f, 0x59, 0x81, 0xe6, 0xda, 0xa3, 0x5d, 0x24, 0xce, 0xde, 0x98, + 0xa2, 0x46, 0xd6, 0xa8, 0xeb, 0x42, 0x9c, 0xf3, 0x28, 0xf2, 0x84, 0xf9, 0xba, 0xac, 0x2d, 0x34, + 0x0b, 0x00, 0xcd, 0x82, 0x8f, 0xcc, 0x15, 0xd1, 0xe7, 0x7e, 0x93, 0x15, 0xa6, 0x11, 0x45, 0x53, + 0x41, 0xfb, 0xd2, 0xfe, 0x23, 0x0b, 0x1a, 0x1a, 0x8e, 0x5c, 0x83, 0x95, 0x9d, 0xe7, 0xcf, 0x8f, + 0xf6, 0x9c, 0xed, 0x93, 0x67, 0xdf, 0xdb, 0xeb, 0xef, 0x1c, 0x3c, 0x3f, 0xde, 0xeb, 0x2c, 0x30, + 0xf0, 0xc1, 0xf3, 0x9d, 0xed, 0x83, 0xfe, 0x93, 0xe7, 0xce, 0x8e, 0x04, 0x5b, 0x64, 0x1d, 0x88, + 0xb3, 0xf7, 0xf9, 0xf3, 0x93, 0x3d, 0x03, 0x5e, 0x22, 0x1d, 0x68, 0x3e, 0x76, 0xf6, 0xb6, 0x77, + 0xf6, 0x05, 0xa4, 0x4c, 0xd6, 0xa0, 0xf3, 0xe4, 0xc5, 0xe1, 0xee, 0xb3, 0xc3, 0xa7, 0xfd, 0x9d, + 0xed, 0xc3, 0x9d, 0xbd, 0x83, 0xbd, 0xdd, 0x4e, 0x85, 0x2c, 0x43, 0x7d, 0xfb, 0xf1, 0xf6, 0xe1, + 0xee, 0xf3, 0xc3, 0xbd, 0xdd, 0x4e, 0xd5, 0xfe, 0x6f, 0x16, 0x5c, 0xc3, 0x5e, 0x0f, 0xb3, 0x02, + 0x72, 0x0b, 0x1a, 0x83, 0x30, 0x9c, 0x30, 0x73, 0x3c, 0xdd, 0x70, 0x74, 0x10, 0x63, 0x7e, 0x2e, + 0xae, 0x67, 0x61, 0x34, 0xa0, 0x42, 0x3e, 0x00, 0x41, 0x4f, 0x18, 0x84, 0x31, 0xbf, 0x58, 0x5e, + 0x4e, 0xc1, 0xc5, 0xa3, 0xc1, 0x61, 0x9c, 0x64, 0x1d, 0x16, 0x4f, 0x23, 0xea, 0x0e, 0xce, 0x85, + 0x64, 0x88, 0x12, 0xf9, 0x6a, 0xea, 0x2f, 0x0e, 0xd8, 0xec, 0xfb, 0x74, 0x88, 0x1c, 0x53, 0x73, + 0xda, 0x02, 0xbe, 0x23, 0xc0, 0x4c, 0x3f, 0xb9, 0xa7, 0x6e, 0x30, 0x0c, 0x03, 0x3a, 0x14, 0xc6, + 0x68, 0x0a, 0xb0, 0x8f, 0x60, 0x3d, 0x3b, 0x3e, 0x21, 0x5f, 0x9f, 0x6a, 0xf2, 0xc5, 0x6d, 0xc3, + 0xde, 0xfc, 0xd5, 0xd4, 0x64, 0xed, 0x4f, 0x4b, 0x50, 0x61, 0xa6, 0xc2, 0x7c, 0xb3, 0x42, 0xb7, + 0xfe, 0xca, 0xb9, 0x18, 0x28, 0xba, 0xa0, 0x7c, 0xf3, 0xe0, 0x1b, 0xac, 0x06, 0x49, 0xf1, 0x11, + 0x1d, 0x5c, 0xe0, 0x88, 0x15, 0x9e, 0x41, 0x98, 0x80, 0x30, 0xd3, 0x1c, 0xbf, 0x16, 0x02, 0x22, + 0xcb, 0x12, 0x87, 0x5f, 0x2e, 0xa5, 0x38, 0xfc, 0xae, 0x0b, 0x4b, 0x5e, 0x70, 0x1a, 0x4e, 0x83, + 0x21, 0x0a, 0x44, 0xcd, 0x91, 0x45, 0x8c, 0xba, 0xa2, 0xa0, 0x7a, 0x63, 0xc9, 0xfe, 0x29, 0x80, + 0x3c, 0x80, 0x7a, 0x3c, 0x0b, 0x06, 0x3a, 0xcf, 0xaf, 0x89, 0x59, 0x62, 0x73, 0xb0, 0x79, 0x3c, + 0x0b, 0x06, 0xc8, 0xe1, 0x29, 0x99, 0xfd, 0x07, 0x50, 0x93, 0x60, 0xc6, 0x96, 0x2f, 0x0e, 0xbf, + 0x73, 0xf8, 0xfc, 0xe5, 0x61, 0xff, 0xf8, 0xfb, 0x87, 0x3b, 0x9d, 0x05, 0xd2, 0x86, 0xc6, 0xf6, + 0x0e, 0x72, 0x3a, 0x02, 0x2c, 0x46, 0x72, 0xb4, 0x7d, 0x7c, 0xac, 0x20, 0x25, 0x9b, 0x30, 0xf7, + 0x3a, 0x46, 0x7b, 0x4c, 0x45, 0x15, 0x3f, 0x85, 0x15, 0x0d, 0x96, 0xda, 0xf6, 0x13, 0x06, 0xc8, + 0xd8, 0xf6, 0x68, 0xc8, 0x71, 0x8c, 0xdd, 0x81, 0xd6, 0x53, 0x9a, 0x3c, 0x0b, 0xce, 0x42, 0x59, + 0xd3, 0xff, 0xaa, 0x40, 0x5b, 0x81, 0x44, 0x45, 0x77, 0xa1, 0xed, 0x0d, 0x69, 0x90, 0x78, 0xc9, + 0xac, 0x6f, 0x78, 0xf1, 0x59, 0x30, 0x33, 0x80, 0x5d, 0xdf, 0x73, 0x65, 0x70, 0x9b, 0x17, 0x98, + 0x57, 0xcb, 0x76, 0x67, 0xb9, 0xe1, 0x2a, 0xbe, 0xe2, 0xc1, 0x83, 0x42, 0x1c, 0xd3, 0x40, 0x0c, + 0x2e, 0xb6, 0x18, 0xf5, 0x09, 0x37, 0x04, 0x8b, 0x50, 0x6c, 0xa9, 0x78, 0x4d, 0x6c, 0xc8, 0x55, + 0xbe, 0x83, 0x2b, 0x40, 0x2e, 0x7a, 0xbc, 0xc8, 0xf5, 0x63, 0x36, 0x7a, 0xac, 0x45, 0xa0, 0x6b, + 0xb9, 0x08, 0x34, 0xd3, 0x9f, 0xb3, 0x60, 0x40, 0x87, 0xfd, 0x24, 0xec, 0xa3, 0x9e, 0x47, 0x96, + 0xa8, 0x39, 0x59, 0x30, 0xb9, 0x01, 0x4b, 0x09, 0x8d, 0x93, 0x80, 0xf2, 0xb0, 0x5f, 0xed, 0x71, + 0xa9, 0x6b, 0x39, 0x12, 0xc4, 0xac, 0xf6, 0x69, 0xe4, 0xc5, 0xdd, 0x26, 0xc6, 0x96, 0xf1, 0x37, + 0xf9, 0x06, 0x5c, 0x3b, 0xa5, 0x71, 0xd2, 0x3f, 0xa7, 0xee, 0x90, 0x46, 0xc8, 0x5e, 0x3c, 0x88, + 0xcd, 0x8d, 0xa1, 0x62, 0x24, 0x63, 0xdc, 0x0b, 0x1a, 0xc5, 0x5e, 0x18, 0xa0, 0x19, 0x54, 0x77, + 0x64, 0x91, 0xd5, 0xc7, 0x06, 0xaf, 0x36, 0x69, 0x35, 0x83, 0x6d, 0x1c, 0x78, 0x31, 0x92, 0xdc, + 0x86, 0x45, 0x1c, 0x40, 0xdc, 0xed, 0x20, 0xcf, 0x34, 0x53, 0x99, 0xf7, 0x02, 0x47, 0xe0, 0xd8, + 0x2a, 0x0f, 0x42, 0x3f, 0x8c, 0xd0, 0x16, 0xaa, 0x3b, 0xbc, 0x60, 0xce, 0xce, 0x28, 0x72, 0x27, + 0xe7, 0xc2, 0x1e, 0xca, 0x82, 0xbf, 0x5d, 0xa9, 0x35, 0x3a, 0x4d, 0xfb, 0xcf, 0x41, 0x15, 0xab, + 0xc5, 0xea, 0x70, 0x32, 0x2d, 0x51, 0x1d, 0x42, 0xbb, 0xb0, 0x14, 0xd0, 0xe4, 0x32, 0x8c, 0x5e, + 0xc9, 0x93, 0x12, 0x51, 0xb4, 0x7f, 0x8a, 0x7e, 0x93, 0x3a, 0x39, 0x78, 0x81, 0x46, 0x1f, 0xf3, + 0x7e, 0xf9, 0x52, 0xc5, 0xe7, 0xae, 0x70, 0xe5, 0x6a, 0x08, 0x38, 0x3e, 0x77, 0x99, 0xae, 0x35, + 0x56, 0x9f, 0x7b, 0xc7, 0x0d, 0x84, 0xed, 0xf3, 0xc5, 0xbf, 0x0d, 0x2d, 0x79, 0x26, 0x11, 0xf7, + 0x7d, 0x7a, 0x96, 0xc8, 0xd8, 0x56, 0x30, 0x1d, 0xa3, 0x0b, 0x7d, 0x40, 0xcf, 0x12, 0xfb, 0x10, + 0x56, 0x84, 0xfe, 0x7b, 0x3e, 0xa1, 0xb2, 0xe9, 0xdf, 0x2b, 0xb2, 0x23, 0x1a, 0x0f, 0x56, 0x4d, + 0x85, 0xc9, 0x4f, 0x61, 0x4c, 0x4a, 0xdb, 0x01, 0xa2, 0xeb, 0x53, 0x51, 0xa1, 0xd8, 0xcc, 0x65, + 0xf4, 0x4e, 0x0c, 0xc7, 0x80, 0xb1, 0xf9, 0x89, 0xa7, 0x83, 0x81, 0x3c, 0x49, 0xaa, 0x39, 0xb2, + 0x68, 0xff, 0x73, 0x0b, 0x56, 0xb1, 0x36, 0x69, 0x09, 0x89, 0x3d, 0xeb, 0xe1, 0x3b, 0x74, 0xb3, + 0x39, 0xd0, 0x23, 0x9a, 0x6b, 0x50, 0xd5, 0x77, 0x31, 0x5e, 0x78, 0xf7, 0x48, 0x49, 0x25, 0x1b, + 0x29, 0xb1, 0xff, 0xbe, 0x05, 0x2b, 0x7c, 0x23, 0x41, 0x3b, 0x58, 0x0c, 0xff, 0xf7, 0x61, 0x99, + 0x5b, 0x04, 0x42, 0x2b, 0x88, 0x8e, 0xa6, 0xaa, 0x15, 0xa1, 0x9c, 0x78, 0x7f, 0xc1, 0x31, 0x89, + 0xc9, 0x23, 0xb4, 0xca, 0x82, 0x3e, 0x42, 0x0b, 0xce, 0x1c, 0xcd, 0xb9, 0xde, 0x5f, 0x70, 0x34, + 0xf2, 0xc7, 0x35, 0x58, 0xe4, 0x4e, 0x84, 0xfd, 0x14, 0x96, 0x8d, 0x86, 0x8c, 0x28, 0x4d, 0x93, + 0x47, 0x69, 0x72, 0xe1, 0xd0, 0x52, 0x41, 0x38, 0xf4, 0x5f, 0x97, 0x81, 0x30, 0x66, 0xc9, 0xac, + 0x06, 0xf3, 0x62, 0xc2, 0xa1, 0xe1, 0x93, 0x36, 0x1d, 0x1d, 0x44, 0x36, 0x81, 0x68, 0x45, 0x19, + 0xb1, 0xe6, 0x5b, 0x66, 0x01, 0x86, 0xa9, 0x59, 0x61, 0x71, 0x08, 0xdb, 0x40, 0x78, 0xdf, 0x7c, + 0xda, 0x0b, 0x71, 0x6c, 0x57, 0x9c, 0x4c, 0xe3, 0x73, 0xf4, 0x15, 0x84, 0xd7, 0x2a, 0xcb, 0xd9, + 0xf5, 0x5d, 0xbc, 0x72, 0x7d, 0x97, 0x72, 0x91, 0x30, 0xcd, 0x6f, 0xaa, 0x99, 0x7e, 0xd3, 0x6d, + 0x58, 0x1e, 0x33, 0x3b, 0x39, 0xf1, 0x07, 0xfd, 0x31, 0x6b, 0x5d, 0x38, 0xa9, 0x06, 0x90, 0xdc, + 0x83, 0x8e, 0x74, 0x5d, 0x94, 0x73, 0xc6, 0xcf, 0x51, 0x72, 0x70, 0xa6, 0xff, 0xd3, 0xd8, 0x58, + 0x03, 0x3b, 0x9b, 0x02, 0x98, 0x27, 0x16, 0x33, 0x0e, 0xe9, 0x4f, 0x03, 0x71, 0xec, 0x48, 0x87, + 0xe8, 0x9e, 0xd6, 0x9c, 0x3c, 0xc2, 0xfe, 0xdb, 0x16, 0x74, 0xd8, 0x9a, 0x19, 0x6c, 0xf9, 0x19, + 0xa0, 0x54, 0xbc, 0x25, 0x57, 0x1a, 0xb4, 0xe4, 0x21, 0xd4, 0xb1, 0x1c, 0x4e, 0x68, 0x20, 0x78, + 0xb2, 0x6b, 0xf2, 0x64, 0xaa, 0x4f, 0xf6, 0x17, 0x9c, 0x94, 0x58, 0xe3, 0xc8, 0xff, 0x6c, 0x41, + 0x43, 0xb4, 0xf2, 0x6b, 0xc7, 0x5e, 0x7a, 0xda, 0x39, 0x31, 0xe7, 0xa4, 0xf4, 0x58, 0xf8, 0x2e, + 0xb4, 0xc7, 0x6e, 0x32, 0x8d, 0xd8, 0x7e, 0x6e, 0xc4, 0x5d, 0xb2, 0x60, 0xb6, 0x39, 0xa3, 0xea, + 0x8c, 0xfb, 0x89, 0xe7, 0xf7, 0x25, 0x56, 0x9c, 0xc8, 0x16, 0xa1, 0x98, 0x06, 0x89, 0x13, 0x77, + 0x44, 0xc5, 0xbe, 0xcb, 0x0b, 0x76, 0x17, 0xd6, 0xc5, 0x80, 0x32, 0xf6, 0xb5, 0xfd, 0x2f, 0x97, + 0x61, 0x23, 0x87, 0x52, 0xf9, 0x23, 0x22, 0xa0, 0xe0, 0x7b, 0xe3, 0xd3, 0x50, 0x39, 0x27, 0x96, + 0x1e, 0x6b, 0x30, 0x50, 0x64, 0x04, 0xd7, 0xa4, 0x81, 0xc1, 0xe6, 0x34, 0xdd, 0x0c, 0x4b, 0xb8, + 0xcb, 0x7d, 0x62, 0x2e, 0x61, 0xb6, 0x41, 0x09, 0xd7, 0x85, 0xb8, 0xb8, 0x3e, 0x72, 0x0e, 0x5d, + 0x65, 0xc9, 0x08, 0x65, 0xad, 0x59, 0x3b, 0xac, 0xad, 0x8f, 0xaf, 0x68, 0xcb, 0x30, 0xc7, 0x9d, + 0xb9, 0xb5, 0x91, 0x19, 0xdc, 0x94, 0x38, 0xd4, 0xc6, 0xf9, 0xf6, 0x2a, 0x6f, 0x35, 0x36, 0x74, + 0x34, 0xcc, 0x46, 0xaf, 0xa8, 0x98, 0xfc, 0x18, 0xd6, 0x2f, 0x5d, 0x2f, 0x91, 0xdd, 0xd2, 0x6c, + 0x8b, 0x2a, 0x36, 0xf9, 0xe0, 0x8a, 0x26, 0x5f, 0xf2, 0x8f, 0x8d, 0x2d, 0x6a, 0x4e, 0x8d, 0xbd, + 0x5f, 0x96, 0xa0, 0x65, 0xd6, 0xc3, 0xd8, 0x54, 0xc8, 0xbe, 0xd4, 0x81, 0xd2, 0x1a, 0xcd, 0x80, + 0xf3, 0xfe, 0x7d, 0xa9, 0xc8, 0xbf, 0xd7, 0xbd, 0xea, 0xf2, 0x55, 0x81, 0xbb, 0xca, 0xdb, 0x05, + 0xee, 0xaa, 0x85, 0x81, 0xbb, 0xf9, 0xf1, 0x9d, 0xc5, 0x5f, 0x37, 0xbe, 0xb3, 0xf4, 0xc6, 0xf8, + 0x4e, 0xef, 0xff, 0x5a, 0x40, 0xf2, 0xdc, 0x4b, 0x9e, 0xf2, 0x90, 0x46, 0x40, 0x7d, 0xa1, 0xc4, + 0x7e, 0xf7, 0xed, 0x24, 0x40, 0xae, 0x96, 0xfc, 0x9a, 0x89, 0xa2, 0x9e, 0xc4, 0xa1, 0x9b, 0x57, + 0xcb, 0x4e, 0x11, 0x2a, 0x13, 0xbc, 0xac, 0x5c, 0x1d, 0xbc, 0xac, 0x5e, 0x1d, 0xbc, 0x5c, 0xcc, + 0x06, 0x2f, 0x7b, 0x7f, 0xcd, 0x82, 0xd5, 0x02, 0x36, 0xfb, 0xed, 0x0d, 0x9c, 0x31, 0x86, 0xa1, + 0x7d, 0x4a, 0x82, 0x31, 0x74, 0x60, 0xef, 0x2f, 0xc3, 0xb2, 0x21, 0x5a, 0xbf, 0xbd, 0xf6, 0xb3, + 0x16, 0x22, 0xe7, 0x6c, 0x03, 0xd6, 0xfb, 0xdf, 0x25, 0x20, 0x79, 0xf1, 0xfe, 0x33, 0xed, 0x43, + 0x7e, 0x9e, 0xca, 0x05, 0xf3, 0xf4, 0xff, 0x75, 0xe7, 0xf9, 0x18, 0x56, 0x44, 0x66, 0x9a, 0x16, + 0xc8, 0xe2, 0x1c, 0x93, 0x47, 0x30, 0x1b, 0xd9, 0x8c, 0x1c, 0xd7, 0x8c, 0x4c, 0x1c, 0x6d, 0xfb, + 0xcd, 0x04, 0x90, 0xed, 0x1e, 0x74, 0xc5, 0x0c, 0xed, 0x5d, 0xd0, 0x20, 0x39, 0x9e, 0x9e, 0xf2, + 0xd4, 0x2c, 0x2f, 0x0c, 0xec, 0x7f, 0x53, 0x56, 0x66, 0x3e, 0x22, 0x85, 0x41, 0xf1, 0x0d, 0x68, + 0xea, 0xdb, 0x87, 0x58, 0x8e, 0x4c, 0x1c, 0x93, 0x99, 0x12, 0x3a, 0x15, 0xd9, 0x85, 0x16, 0x2a, + 0xc9, 0xa1, 0xfa, 0xae, 0x84, 0xdf, 0xbd, 0x21, 0x3e, 0xb3, 0xbf, 0xe0, 0x64, 0xbe, 0x21, 0xdf, + 0x82, 0x96, 0xe9, 0xfc, 0x09, 0xab, 0xa4, 0xc8, 0x1b, 0x60, 0x9f, 0x9b, 0xc4, 0x64, 0x1b, 0x3a, + 0x59, 0xef, 0x51, 0x64, 0x4a, 0xcc, 0xa9, 0x20, 0x47, 0x4e, 0x1e, 0x8a, 0x23, 0xc4, 0x2a, 0xc6, + 0x4d, 0x6e, 0x9b, 0x9f, 0x69, 0xd3, 0xb4, 0xc9, 0xff, 0x68, 0x87, 0x8a, 0x7f, 0x08, 0x90, 0xc2, + 0x48, 0x07, 0x9a, 0xcf, 0x8f, 0xf6, 0x0e, 0xfb, 0x3b, 0xfb, 0xdb, 0x87, 0x87, 0x7b, 0x07, 0x9d, + 0x05, 0x42, 0xa0, 0x85, 0x61, 0xbe, 0x5d, 0x05, 0xb3, 0x18, 0x4c, 0x04, 0x56, 0x24, 0xac, 0x44, + 0xd6, 0xa0, 0xf3, 0xec, 0x30, 0x03, 0x2d, 0x3f, 0xae, 0x2b, 0xf9, 0xb0, 0xd7, 0x61, 0x8d, 0x67, + 0x1e, 0x3e, 0xe6, 0xec, 0x21, 0xad, 0x93, 0x7f, 0x6c, 0xc1, 0xb5, 0x0c, 0x22, 0x4d, 0xa5, 0xe1, + 0x06, 0x88, 0x69, 0x95, 0x98, 0x40, 0x3c, 0x16, 0x90, 0xb6, 0x66, 0x46, 0x83, 0xe4, 0x11, 0x8c, + 0xe7, 0x35, 0xdb, 0x34, 0x23, 0x49, 0x45, 0x28, 0x7b, 0x83, 0xe7, 0x47, 0x62, 0x26, 0xa5, 0xd1, + 0xf1, 0x33, 0x9e, 0xd1, 0xa8, 0x23, 0xd2, 0x23, 0x59, 0xb3, 0xcb, 0xb2, 0xc8, 0xdc, 0x0a, 0xc3, + 0xd8, 0x31, 0xfb, 0x5b, 0x88, 0xb3, 0xff, 0xa8, 0x02, 0xe4, 0xbb, 0x53, 0x1a, 0xcd, 0x30, 0x5f, + 0x46, 0x45, 0x4d, 0x37, 0xb2, 0x31, 0xc1, 0xc5, 0xc9, 0xf4, 0xf4, 0x3b, 0x74, 0x26, 0x33, 0xc7, + 0x4a, 0x69, 0xe6, 0x58, 0x51, 0xf6, 0x56, 0xe5, 0xea, 0xec, 0xad, 0xea, 0x55, 0xd9, 0x5b, 0x5f, + 0x81, 0x65, 0x6f, 0x14, 0x84, 0x4c, 0xe6, 0x99, 0x9d, 0x10, 0x77, 0x17, 0x6f, 0x95, 0x99, 0x6f, + 0x2d, 0x80, 0x87, 0x0c, 0x46, 0x1e, 0xa5, 0x44, 0x74, 0x38, 0xc2, 0x4c, 0x41, 0x5d, 0x0b, 0xec, + 0x0d, 0x47, 0xf4, 0x20, 0x1c, 0xb8, 0x49, 0x18, 0x61, 0x60, 0x47, 0x7e, 0xcc, 0xe0, 0x31, 0xb9, + 0x0d, 0xad, 0x38, 0x9c, 0x32, 0xcb, 0x49, 0x8e, 0x95, 0x47, 0x92, 0x9a, 0x1c, 0x7a, 0xc4, 0x47, + 0xbc, 0x09, 0xab, 0xd3, 0x98, 0xf6, 0xc7, 0x5e, 0x1c, 0xb3, 0xdd, 0x71, 0x10, 0x06, 0x49, 0x14, + 0xfa, 0x22, 0x9e, 0xb4, 0x32, 0x8d, 0xe9, 0xe7, 0x1c, 0xb3, 0xc3, 0x11, 0xe4, 0x1b, 0x69, 0x97, + 0x26, 0xae, 0x17, 0xc5, 0x5d, 0xc0, 0x2e, 0xc9, 0x91, 0xb2, 0x7e, 0x1f, 0xb9, 0x5e, 0xa4, 0xfa, + 0xc2, 0x0a, 0x31, 0xd9, 0xce, 0xa5, 0x98, 0xc9, 0x98, 0x7c, 0x7e, 0x75, 0x7e, 0xfb, 0x99, 0x66, + 0x22, 0x41, 0x6a, 0x13, 0x6a, 0xb2, 0x7b, 0xcc, 0x89, 0x3e, 0x8b, 0xc2, 0xb1, 0x74, 0xa2, 0xd9, + 0x6f, 0xd2, 0x82, 0x52, 0x12, 0x8a, 0x8f, 0x4b, 0x49, 0x68, 0x7f, 0x1f, 0x1a, 0xda, 0x0c, 0x63, + 0x16, 0x9d, 0x30, 0xd8, 0x84, 0xf7, 0x5d, 0xe1, 0xfe, 0x51, 0x40, 0xfd, 0x67, 0x43, 0xf2, 0x35, + 0x58, 0x19, 0x7a, 0x11, 0xc5, 0x84, 0xc8, 0x7e, 0x44, 0x2f, 0x68, 0x14, 0xcb, 0x38, 0x45, 0x47, + 0x21, 0x1c, 0x0e, 0xb7, 0x1f, 0xc1, 0xaa, 0x31, 0x70, 0x25, 0xb5, 0x8b, 0x98, 0xd1, 0x25, 0x43, + 0xa5, 0x66, 0xb6, 0x97, 0xc0, 0xd9, 0xff, 0xac, 0x0c, 0xe5, 0xfd, 0x70, 0xa2, 0x1f, 0x08, 0x59, + 0xe6, 0x81, 0x90, 0x30, 0x38, 0xfb, 0xca, 0x9e, 0x14, 0x56, 0x81, 0x01, 0x24, 0xf7, 0xa0, 0xe5, + 0x8e, 0x93, 0x7e, 0x12, 0x32, 0x03, 0xfb, 0xd2, 0x8d, 0x86, 0x5c, 0x94, 0x91, 0x95, 0x32, 0x18, + 0xb2, 0x06, 0x65, 0x65, 0x27, 0x21, 0x01, 0x2b, 0x32, 0xef, 0x0e, 0x8f, 0xc2, 0x67, 0x22, 0xce, + 0x29, 0x4a, 0x4c, 0x53, 0x98, 0xdf, 0x73, 0xd7, 0x9a, 0xef, 0x76, 0x45, 0x28, 0x66, 0xfc, 0x32, + 0xe1, 0x19, 0xa7, 0xb6, 0xa4, 0x2a, 0xeb, 0x11, 0xfc, 0x9a, 0x19, 0xc1, 0xbf, 0x05, 0x8d, 0xc4, + 0xbf, 0xe8, 0x4f, 0xdc, 0x99, 0x1f, 0xba, 0x43, 0xc1, 0xb4, 0x3a, 0x88, 0xfc, 0x3e, 0xa7, 0x60, + 0x1b, 0x6c, 0x34, 0x94, 0xcc, 0x2a, 0x77, 0xa8, 0xfd, 0x70, 0xb2, 0x79, 0xe2, 0x5f, 0x38, 0x1c, + 0xc9, 0xf9, 0x4d, 0x27, 0xef, 0x7d, 0x0b, 0xda, 0x19, 0xfc, 0x3b, 0x25, 0x38, 0xfe, 0xca, 0x82, + 0x2a, 0x2e, 0x1d, 0x33, 0x2c, 0xb8, 0xe6, 0x55, 0x47, 0x56, 0x58, 0xc3, 0xb2, 0x93, 0x05, 0x13, + 0xdb, 0xc8, 0x0c, 0x2e, 0xa9, 0xf9, 0xd6, 0xb3, 0x83, 0x6f, 0x41, 0x9d, 0x97, 0x54, 0x96, 0x2b, + 0x92, 0xa4, 0x40, 0x72, 0x13, 0x2a, 0xe7, 0xe1, 0x44, 0xfa, 0x5e, 0x90, 0x8e, 0xd7, 0x41, 0x78, + 0xda, 0x1f, 0x56, 0x1f, 0x9f, 0x75, 0x6e, 0xdf, 0x66, 0xc1, 0xcc, 0xa7, 0x50, 0xd5, 0xea, 0xab, + 0x98, 0x81, 0xda, 0x2f, 0xa0, 0xcd, 0x84, 0x4b, 0x0b, 0xe1, 0xcf, 0xd7, 0xb2, 0x5f, 0x65, 0x9b, + 0xf6, 0xc0, 0x9f, 0x0e, 0xa9, 0xee, 0x01, 0x63, 0x88, 0x56, 0xc0, 0xa5, 0xed, 0x67, 0xff, 0x2b, + 0x8b, 0x0b, 0x2d, 0xab, 0x97, 0xdc, 0x85, 0x0a, 0xd3, 0x95, 0x99, 0x80, 0x87, 0x4a, 0x49, 0x61, + 0x74, 0x0e, 0x52, 0x30, 0x93, 0x10, 0x83, 0xa8, 0x7a, 0xed, 0x3c, 0x84, 0x9a, 0xba, 0x8f, 0x6a, + 0x64, 0x19, 0xaf, 0x2b, 0x03, 0x25, 0x9b, 0xda, 0x09, 0x54, 0xc5, 0xd0, 0xbf, 0xd2, 0x46, 0x18, + 0x8e, 0xa8, 0x76, 0xf2, 0xf4, 0xc7, 0x16, 0x2c, 0x1b, 0x7d, 0x62, 0x6c, 0xea, 0xbb, 0x71, 0x22, + 0xd2, 0x02, 0xc4, 0xca, 0xeb, 0x20, 0x9d, 0xc5, 0x4b, 0x26, 0x8b, 0xab, 0x93, 0x8c, 0xb2, 0x7e, + 0x92, 0x71, 0x1f, 0xea, 0x69, 0x6a, 0xb8, 0xd9, 0x29, 0xd6, 0xa2, 0x4c, 0xce, 0x49, 0x89, 0xd2, + 0x58, 0x79, 0x55, 0x8b, 0x95, 0xdb, 0x8f, 0xa0, 0xa1, 0xd1, 0xeb, 0xb1, 0x6e, 0xcb, 0x88, 0x75, + 0xab, 0xcc, 0xb5, 0x52, 0x9a, 0xb9, 0x66, 0xff, 0xac, 0x04, 0xcb, 0x8c, 0xbd, 0xbd, 0x60, 0x74, + 0x14, 0xfa, 0xde, 0x60, 0x86, 0x6c, 0x25, 0x39, 0x59, 0xec, 0x95, 0x92, 0xcd, 0x4d, 0x30, 0x93, + 0x77, 0x19, 0x61, 0x13, 0xca, 0x49, 0x95, 0x99, 0xf6, 0x62, 0xb2, 0x7f, 0xea, 0xc6, 0x42, 0x21, + 0x08, 0x5b, 0xdd, 0x00, 0x32, 0x1d, 0xc3, 0x00, 0x98, 0x87, 0x38, 0xf6, 0x7c, 0xdf, 0xe3, 0xb4, + 0xdc, 0x93, 0x2b, 0x42, 0xb1, 0x36, 0x87, 0x5e, 0xec, 0x9e, 0xa6, 0xa7, 0x94, 0xaa, 0x8c, 0x61, + 0x40, 0xf7, 0xb5, 0x16, 0x06, 0x5c, 0x44, 0x01, 0x37, 0x81, 0xd9, 0x85, 0x5c, 0xca, 0x2d, 0xa4, + 0xfd, 0x1f, 0x4a, 0xd0, 0xd0, 0xd8, 0x42, 0x1c, 0xcd, 0x9b, 0x9b, 0x86, 0x06, 0x91, 0x78, 0x23, + 0x2e, 0xa0, 0x41, 0xc8, 0x6d, 0xb3, 0x45, 0x3c, 0x0a, 0x40, 0x61, 0x37, 0xd8, 0xe7, 0x06, 0xd4, + 0x19, 0xdb, 0x7f, 0x82, 0x41, 0x08, 0x71, 0x27, 0x43, 0x01, 0x24, 0xf6, 0x01, 0x62, 0xab, 0x29, + 0x16, 0x01, 0x6f, 0x3c, 0xcc, 0x7f, 0x08, 0x4d, 0x51, 0x0d, 0xae, 0x2f, 0x0e, 0x38, 0x15, 0x3c, + 0x63, 0xed, 0x1d, 0x83, 0x52, 0x7e, 0xf9, 0x40, 0x7e, 0x59, 0xbb, 0xea, 0x4b, 0x49, 0x69, 0x3f, + 0x55, 0x39, 0x12, 0x4f, 0x23, 0x77, 0x72, 0x2e, 0x95, 0xc9, 0x7d, 0x58, 0x95, 0x3a, 0x63, 0x1a, + 0xb8, 0x41, 0x10, 0x4e, 0x83, 0x01, 0x95, 0x09, 0x6e, 0x45, 0x28, 0x7b, 0xa8, 0xd2, 0xa1, 0xb1, + 0x22, 0x72, 0x0f, 0xaa, 0xdc, 0xd2, 0xe2, 0x7b, 0x6b, 0xb1, 0xfa, 0xe0, 0x24, 0xe4, 0x2e, 0x54, + 0xb9, 0xc1, 0x55, 0x9a, 0x2b, 0xf0, 0x9c, 0xc0, 0xbe, 0x07, 0x6d, 0xcc, 0xb2, 0x37, 0xf5, 0x9e, + 0xb9, 0x2f, 0x2f, 0x0e, 0x30, 0x0f, 0xdf, 0x5e, 0x03, 0x72, 0xc8, 0xe5, 0x49, 0x3f, 0xe9, 0xfc, + 0x55, 0x19, 0x1a, 0x1a, 0x98, 0xe9, 0x25, 0x3c, 0x9e, 0xea, 0x0f, 0x3d, 0x77, 0x4c, 0x13, 0x1a, + 0x09, 0x19, 0xca, 0x40, 0x19, 0x9d, 0x7b, 0x31, 0xea, 0x87, 0xd3, 0xa4, 0x3f, 0xa4, 0xa3, 0x88, + 0xf2, 0x0d, 0x88, 0x6d, 0xdd, 0x06, 0x94, 0xd1, 0x31, 0x2e, 0xd6, 0xe8, 0xf8, 0x81, 0x52, 0x06, + 0x2a, 0xcf, 0x2d, 0xf9, 0x1c, 0x55, 0xd2, 0x73, 0x4b, 0x3e, 0x23, 0x59, 0x8d, 0x5a, 0x2d, 0xd0, + 0xa8, 0x9f, 0xc2, 0x3a, 0xd7, 0x9d, 0x42, 0x6b, 0xf4, 0x33, 0x8c, 0x35, 0x07, 0x4b, 0xee, 0x41, + 0x87, 0xf5, 0x59, 0x8a, 0x45, 0xec, 0xfd, 0x94, 0xcb, 0x96, 0xe5, 0xe4, 0xe0, 0x8c, 0x16, 0x83, + 0xe9, 0x3a, 0x2d, 0x4f, 0x1e, 0xc9, 0xc1, 0x91, 0xd6, 0x7d, 0x6d, 0xd2, 0xd6, 0x05, 0x6d, 0x06, + 0x4e, 0x1e, 0xc2, 0xc6, 0x98, 0x0e, 0x3d, 0xd7, 0xac, 0x02, 0x63, 0x5b, 0x3c, 0x27, 0x6d, 0x1e, + 0x9a, 0xb5, 0xc2, 0x66, 0xe1, 0xa7, 0xe1, 0xf8, 0xd4, 0xe3, 0x1b, 0x1a, 0x0f, 0xfb, 0x57, 0x9c, + 0x1c, 0xdc, 0x5e, 0x86, 0xc6, 0x71, 0x12, 0x4e, 0xe4, 0xd2, 0xb7, 0xa0, 0xc9, 0x8b, 0x22, 0x9d, + 0xf1, 0x3d, 0xb8, 0x8e, 0xbc, 0x7a, 0x12, 0x4e, 0x42, 0x3f, 0x1c, 0xcd, 0x0c, 0xe7, 0xfd, 0x3f, + 0x59, 0xb0, 0x6a, 0x60, 0x53, 0xef, 0x1d, 0x23, 0x8d, 0x32, 0x0f, 0x8d, 0xb3, 0xf7, 0x8a, 0xb6, + 0x1d, 0x70, 0x42, 0x7e, 0xa8, 0xf3, 0x42, 0xa4, 0xa6, 0x6d, 0xa7, 0xd7, 0xe2, 0xe4, 0x87, 0x9c, + 0xd7, 0xbb, 0x79, 0x5e, 0x17, 0xdf, 0xcb, 0x5b, 0x71, 0xb2, 0x8a, 0x6f, 0x89, 0x54, 0x9f, 0xa1, + 0x18, 0x74, 0xd9, 0x4c, 0xcf, 0xd0, 0x83, 0x3d, 0xb2, 0x07, 0x03, 0x05, 0x8c, 0xed, 0x9f, 0x5b, + 0x00, 0x69, 0xef, 0x30, 0x41, 0x44, 0x6d, 0x69, 0xfc, 0x0a, 0xa6, 0xb6, 0x7d, 0x7d, 0x08, 0x4d, + 0x75, 0xc6, 0x9f, 0xee, 0x92, 0x0d, 0x09, 0x63, 0x56, 0xc5, 0x1d, 0x68, 0x8f, 0xfc, 0xf0, 0x14, + 0xad, 0x17, 0xcc, 0x8f, 0x8d, 0x45, 0x52, 0x67, 0x8b, 0x83, 0x9f, 0x08, 0x68, 0xba, 0xa5, 0x56, + 0xf4, 0x2d, 0xb5, 0x78, 0x83, 0xfc, 0x9b, 0x25, 0x75, 0xd0, 0x9a, 0xce, 0xc4, 0x5c, 0x09, 0x27, + 0x0f, 0x72, 0xea, 0x7c, 0xce, 0xb9, 0x26, 0x3a, 0x0e, 0x47, 0x57, 0xc6, 0x7d, 0x1f, 0x41, 0x2b, + 0xe2, 0xba, 0x52, 0x2a, 0xd2, 0xca, 0x1b, 0x14, 0xe9, 0x72, 0x64, 0xec, 0xc6, 0x5f, 0x85, 0x8e, + 0x3b, 0xbc, 0xa0, 0x51, 0xe2, 0x61, 0x1c, 0x0c, 0x4d, 0x27, 0x3e, 0xb8, 0xb6, 0x06, 0x47, 0x0b, + 0xe5, 0x0e, 0xb4, 0x45, 0x7a, 0xad, 0xa2, 0x14, 0x97, 0x99, 0x52, 0x30, 0x23, 0xb4, 0x7f, 0x21, + 0xcf, 0x74, 0xcd, 0x95, 0x9d, 0x3f, 0x23, 0xfa, 0xe8, 0x4a, 0x99, 0xd1, 0x7d, 0x45, 0x9c, 0xaf, + 0x0e, 0x65, 0xb0, 0xad, 0xac, 0x25, 0x8b, 0x0d, 0xc5, 0x79, 0xb8, 0x39, 0xa5, 0x95, 0xb7, 0x99, + 0x52, 0xfb, 0x4f, 0x2c, 0x58, 0xda, 0x0f, 0x27, 0xfb, 0x22, 0x6d, 0x0e, 0xc5, 0x43, 0xe5, 0xb5, + 0xcb, 0xe2, 0x1b, 0x12, 0xea, 0x0a, 0x2d, 0x90, 0xe5, 0xac, 0x05, 0xf2, 0x17, 0xe0, 0x3d, 0x0c, + 0xf5, 0x46, 0xe1, 0x24, 0x8c, 0x98, 0x88, 0xba, 0x3e, 0x37, 0x37, 0xc2, 0x20, 0x39, 0x97, 0x2a, + 0xf4, 0x4d, 0x24, 0x18, 0x7f, 0xf1, 0x93, 0x8b, 0x3e, 0x77, 0x9b, 0x84, 0xc5, 0xc4, 0x35, 0x6b, + 0x1e, 0x61, 0xff, 0x1e, 0xd4, 0xd1, 0x9b, 0xc0, 0x61, 0x7d, 0x0c, 0xf5, 0xf3, 0x70, 0xd2, 0x3f, + 0xf7, 0x82, 0x44, 0x8a, 0x7c, 0x2b, 0x35, 0xf3, 0xf7, 0x71, 0x42, 0x14, 0x81, 0xfd, 0x77, 0x17, + 0x61, 0xe9, 0x59, 0x70, 0x11, 0x7a, 0x03, 0x3c, 0x3f, 0x1e, 0xd3, 0x71, 0x28, 0xb3, 0xfc, 0xd9, + 0x6f, 0x72, 0x03, 0x96, 0x30, 0xad, 0x75, 0xc2, 0x99, 0xb6, 0xc9, 0xf3, 0x44, 0x04, 0x08, 0x6f, + 0x19, 0xa6, 0x77, 0xad, 0xb8, 0x50, 0x69, 0x10, 0xe6, 0x06, 0x46, 0xfa, 0x5d, 0x29, 0x51, 0x4a, + 0x3d, 0xa3, 0xaa, 0x76, 0x8b, 0x82, 0xb5, 0x25, 0xd2, 0xfc, 0x78, 0x1e, 0x18, 0x6f, 0x4b, 0x80, + 0xd0, 0x75, 0x8d, 0x28, 0x0f, 0xd5, 0x2b, 0x23, 0x8b, 0xb9, 0xae, 0x3a, 0x90, 0x19, 0x62, 0xfc, + 0x03, 0x4e, 0xc3, 0x37, 0x00, 0x1d, 0xc4, 0x4c, 0xd1, 0xec, 0xf5, 0x3c, 0x7e, 0x3d, 0x32, 0x0b, + 0x66, 0xfa, 0x7b, 0x48, 0x95, 0x9a, 0xe5, 0xe3, 0x00, 0x7e, 0x9f, 0x2c, 0x0b, 0xd7, 0x1c, 0x5e, + 0x9e, 0x81, 0x2c, 0x1d, 0x5e, 0xc6, 0x30, 0xae, 0xef, 0x9f, 0xba, 0x83, 0x57, 0x78, 0x3b, 0x13, + 0x4f, 0x74, 0xeb, 0x8e, 0x09, 0xc4, 0x64, 0xbd, 0x74, 0x55, 0x31, 0xa3, 0xa6, 0xe2, 0xe8, 0x20, + 0xf2, 0x00, 0x1a, 0xe8, 0xe4, 0x8b, 0x75, 0x6d, 0xe1, 0xba, 0x76, 0xf4, 0x28, 0x00, 0xae, 0xac, + 0x4e, 0xa4, 0x9f, 0x6d, 0xb7, 0x73, 0x39, 0xc1, 0xee, 0x70, 0x28, 0x52, 0x02, 0x3a, 0x3c, 0x60, + 0xa1, 0x00, 0x6c, 0x47, 0x17, 0x13, 0xc6, 0x09, 0x56, 0x90, 0xc0, 0x80, 0x91, 0x9b, 0x50, 0x63, + 0x1e, 0xde, 0xc4, 0xf5, 0x86, 0x98, 0x44, 0xc3, 0x1d, 0x4d, 0x05, 0x63, 0x75, 0xc8, 0xdf, 0xb8, + 0x55, 0xae, 0xe2, 0xac, 0x18, 0x30, 0x36, 0x37, 0xaa, 0x3c, 0x4e, 0x93, 0x88, 0x4d, 0x20, 0xf9, + 0x04, 0x0f, 0x66, 0x13, 0x8a, 0x99, 0xc2, 0xad, 0x07, 0xef, 0x89, 0x31, 0x0b, 0xa6, 0x95, 0x7f, + 0x8f, 0x19, 0x89, 0xc3, 0x29, 0xed, 0x6d, 0x68, 0xea, 0x60, 0x52, 0x83, 0xca, 0xf3, 0xa3, 0xbd, + 0xc3, 0xce, 0x02, 0x69, 0xc0, 0xd2, 0xf1, 0xde, 0xc9, 0xc9, 0xc1, 0xde, 0x6e, 0xc7, 0x22, 0x4d, + 0xa8, 0xa9, 0xcc, 0xca, 0x12, 0x2b, 0x6d, 0xef, 0xec, 0xec, 0x1d, 0x9d, 0xec, 0xed, 0x76, 0xca, + 0x76, 0x02, 0x64, 0x7b, 0x38, 0x14, 0xb5, 0xa8, 0x30, 0x4c, 0xca, 0xcf, 0x96, 0xc1, 0xcf, 0x05, + 0x3c, 0x55, 0x2a, 0xe6, 0xa9, 0x37, 0xce, 0xbc, 0xbd, 0x07, 0x8d, 0x23, 0xed, 0x4a, 0x20, 0x8a, + 0x97, 0xbc, 0x0c, 0x28, 0xc4, 0x52, 0x83, 0x68, 0xdd, 0x29, 0xe9, 0xdd, 0xb1, 0xff, 0xa9, 0xc5, + 0xef, 0xe6, 0xa8, 0xee, 0xf3, 0xb6, 0x6d, 0x68, 0xaa, 0x58, 0x68, 0x9a, 0x34, 0x6d, 0xc0, 0x18, + 0x0d, 0x76, 0xa5, 0x1f, 0x9e, 0x9d, 0xc5, 0x54, 0xa6, 0x38, 0x1a, 0x30, 0x69, 0xd7, 0x30, 0x4b, + 0xc9, 0xe3, 0x2d, 0xc4, 0x22, 0xd5, 0x31, 0x07, 0x67, 0x5a, 0x5e, 0x84, 0xbc, 0x64, 0x72, 0xa7, + 0x2a, 0xab, 0xdc, 0xee, 0xec, 0x2c, 0xdf, 0x83, 0x9a, 0xaa, 0xd7, 0x54, 0x60, 0x92, 0x52, 0xe1, + 0x99, 0xa2, 0x44, 0x7f, 0xc7, 0xe8, 0x34, 0x57, 0xda, 0x79, 0x04, 0xd9, 0x04, 0x72, 0xe6, 0x45, + 0x59, 0xf2, 0x32, 0x92, 0x17, 0x60, 0xec, 0x97, 0xb0, 0x2a, 0x19, 0x49, 0x33, 0xb8, 0xcc, 0x45, + 0xb4, 0xae, 0x12, 0x9f, 0x52, 0x5e, 0x7c, 0xec, 0x3f, 0x2d, 0xc3, 0x92, 0x58, 0xe9, 0xdc, 0xb5, + 0x52, 0xbe, 0xce, 0x06, 0x8c, 0x74, 0x8d, 0x6b, 0x67, 0x28, 0x6b, 0x42, 0x69, 0xe6, 0xd4, 0x62, + 0xb9, 0x48, 0x2d, 0x12, 0xa8, 0x4c, 0xdc, 0xe4, 0x1c, 0x23, 0x02, 0x75, 0x07, 0x7f, 0xcb, 0xc8, + 0x5d, 0xd5, 0x8c, 0xdc, 0x15, 0x5d, 0xa2, 0xe5, 0x3b, 0x7e, 0xfe, 0x12, 0xed, 0x0d, 0xa8, 0x63, + 0x27, 0xb4, 0x83, 0xde, 0x14, 0xc0, 0xb8, 0x97, 0x17, 0x50, 0xb6, 0xc5, 0x2d, 0x90, 0x14, 0xf2, + 0x0e, 0x8a, 0xf8, 0x1b, 0xb0, 0xc8, 0xaf, 0x21, 0x88, 0x14, 0xd6, 0x1b, 0xf2, 0xb0, 0x8b, 0xd3, + 0xc9, 0xbf, 0x3c, 0x17, 0xc6, 0x11, 0xb4, 0xfa, 0x25, 0xc6, 0x86, 0x79, 0x89, 0x51, 0x8f, 0x29, + 0x36, 0xcd, 0x98, 0xa2, 0xfd, 0x04, 0x96, 0x8d, 0xea, 0x98, 0xca, 0x10, 0x29, 0xb0, 0x9d, 0x05, + 0xb2, 0x0c, 0xf5, 0x67, 0x87, 0xfd, 0x27, 0x07, 0xcf, 0x9e, 0xee, 0x9f, 0x74, 0x2c, 0x56, 0x3c, + 0x7e, 0xb1, 0xb3, 0xb3, 0xb7, 0xb7, 0x8b, 0x2a, 0x04, 0x60, 0xf1, 0xc9, 0xf6, 0xb3, 0x03, 0x54, + 0x20, 0xbb, 0x9c, 0xb7, 0x45, 0x5d, 0xea, 0x80, 0xe1, 0x77, 0x81, 0x48, 0x97, 0x14, 0x53, 0x61, + 0x26, 0x3e, 0x4d, 0x64, 0x76, 0xf6, 0x8a, 0xc0, 0x3c, 0x53, 0x08, 0x79, 0xb9, 0x20, 0xad, 0x25, + 0x15, 0x11, 0x31, 0x49, 0x59, 0x11, 0x11, 0xa4, 0x8e, 0xc2, 0xdb, 0x3d, 0xe8, 0xee, 0x52, 0x56, + 0xdb, 0xb6, 0xef, 0x67, 0xba, 0xc3, 0xfc, 0x8a, 0x02, 0x9c, 0x70, 0x3a, 0xbe, 0x0b, 0xd7, 0xb6, + 0x79, 0x22, 0xf6, 0x6f, 0x2b, 0x4f, 0xcf, 0xee, 0xc2, 0x7a, 0xb6, 0x4a, 0xd1, 0xd8, 0x13, 0x58, + 0xd9, 0xa5, 0xa7, 0xd3, 0xd1, 0x01, 0xbd, 0x48, 0x1b, 0x22, 0x50, 0x89, 0xcf, 0xc3, 0x4b, 0x31, + 0x3f, 0xf8, 0x9b, 0xbc, 0x0f, 0xe0, 0x33, 0x9a, 0x7e, 0x3c, 0xa1, 0x03, 0x79, 0xf5, 0x0d, 0x21, + 0xc7, 0x13, 0x3a, 0xb0, 0x3f, 0x05, 0xa2, 0xd7, 0x23, 0xe6, 0x8b, 0x99, 0x05, 0xd3, 0xd3, 0x7e, + 0x3c, 0x8b, 0x13, 0x3a, 0x96, 0x77, 0xfa, 0x74, 0x90, 0x7d, 0x07, 0x9a, 0x47, 0xee, 0xcc, 0xa1, + 0x3f, 0x11, 0xd7, 0xab, 0x37, 0x60, 0x69, 0xe2, 0xce, 0x18, 0x0b, 0xaa, 0x18, 0x25, 0xa2, 0xed, + 0xff, 0x53, 0x82, 0x45, 0x4e, 0xc9, 0x6a, 0x1d, 0xd2, 0x38, 0xf1, 0x02, 0x94, 0x34, 0x59, 0xab, + 0x06, 0xca, 0xc9, 0x76, 0xa9, 0x40, 0xb6, 0x85, 0x03, 0x2d, 0xaf, 0x11, 0x09, 0x01, 0x36, 0x60, + 0x4c, 0xd2, 0xd2, 0x84, 0x5b, 0x1e, 0xc9, 0x4a, 0x01, 0x99, 0x68, 0x7b, 0x6a, 0x7c, 0xf0, 0xfe, + 0x49, 0xb5, 0x25, 0xc4, 0x58, 0x07, 0x15, 0x9a, 0x38, 0x4b, 0x5c, 0xda, 0x73, 0x26, 0x4e, 0xce, + 0x94, 0xa9, 0xbd, 0x85, 0x29, 0xc3, 0xbd, 0xea, 0x37, 0x99, 0x32, 0xf0, 0x16, 0xa6, 0x8c, 0x4d, + 0xa0, 0x83, 0xf7, 0x93, 0x99, 0xb1, 0x2c, 0x79, 0xf7, 0x1f, 0x58, 0xd0, 0x11, 0x5c, 0xa4, 0x70, + 0xe4, 0x43, 0xc3, 0x29, 0x28, 0xbc, 0x2e, 0x73, 0x1b, 0x96, 0xd1, 0x54, 0x57, 0x2a, 0x40, 0x9c, + 0x81, 0x18, 0x40, 0x36, 0x0e, 0x99, 0xae, 0x31, 0xf6, 0x7c, 0xb1, 0x28, 0x3a, 0x48, 0x6a, 0x91, + 0xc8, 0x15, 0x89, 0xa3, 0x96, 0xa3, 0xca, 0xf6, 0x2f, 0x2d, 0x58, 0xd1, 0x3a, 0x2c, 0xb8, 0xf0, + 0x11, 0x48, 0x69, 0xe0, 0x41, 0x7c, 0x2e, 0xb9, 0x1b, 0xa6, 0xd8, 0xa4, 0x9f, 0x19, 0xc4, 0xb8, + 0x98, 0xee, 0x0c, 0x3b, 0x18, 0x4f, 0xc7, 0x62, 0x57, 0xd1, 0x41, 0x8c, 0x91, 0x2e, 0x29, 0x7d, + 0xa5, 0x48, 0xf8, 0xbe, 0x66, 0xc0, 0x30, 0x9c, 0xc9, 0x5c, 0x0c, 0x45, 0x54, 0x11, 0xe1, 0x4c, + 0x1d, 0x68, 0xff, 0x57, 0x0b, 0x56, 0xb9, 0xaf, 0x28, 0xfc, 0x73, 0x75, 0x13, 0x73, 0x91, 0xbb, + 0xcc, 0x5c, 0x22, 0xf7, 0x17, 0x1c, 0x51, 0x26, 0xdf, 0x7c, 0x4b, 0xff, 0x56, 0x65, 0xb3, 0xce, + 0x59, 0x8b, 0x72, 0xd1, 0x5a, 0xbc, 0x61, 0xa6, 0x8b, 0x22, 0xcb, 0xd5, 0xc2, 0xc8, 0xf2, 0xe3, + 0x25, 0xa8, 0xc6, 0x83, 0x70, 0x42, 0xed, 0x75, 0x58, 0x33, 0x07, 0x27, 0x54, 0xd0, 0xcf, 0x2d, + 0xe8, 0x3e, 0xe1, 0x67, 0x4f, 0x5e, 0x30, 0xda, 0xf7, 0xe2, 0x24, 0x8c, 0xd4, 0x85, 0xf5, 0x9b, + 0x00, 0x71, 0xe2, 0x46, 0x09, 0xbf, 0x68, 0x21, 0xe2, 0xb5, 0x29, 0x84, 0xf5, 0x91, 0x06, 0x43, + 0x8e, 0xe5, 0x6b, 0xa3, 0xca, 0x39, 0xa3, 0x4a, 0x78, 0xb3, 0x86, 0x69, 0xf2, 0x11, 0xcf, 0xee, + 0x66, 0xc6, 0x13, 0xbd, 0x40, 0xbd, 0xce, 0xdd, 0xc4, 0x0c, 0xd4, 0xfe, 0x2f, 0x16, 0xb4, 0xd3, + 0x4e, 0x62, 0x16, 0x82, 0xa9, 0x1d, 0x84, 0x3d, 0x92, 0x6a, 0x07, 0x19, 0x49, 0xf6, 0x98, 0x81, + 0x22, 0xfa, 0xa6, 0x41, 0x50, 0x62, 0x45, 0x29, 0x9c, 0x4a, 0x8b, 0x4f, 0x07, 0xf1, 0x5c, 0x4d, + 0x66, 0x1a, 0x09, 0x33, 0x4f, 0x94, 0xf0, 0x9e, 0xcc, 0x38, 0xc1, 0xaf, 0x78, 0x54, 0x5c, 0x16, + 0x49, 0x87, 0xdb, 0x16, 0x4b, 0xfc, 0x30, 0x8c, 0xd9, 0x15, 0xfa, 0x9e, 0x5b, 0xe3, 0xf3, 0xa3, + 0xf6, 0xdc, 0xbf, 0x65, 0xc1, 0xf5, 0x82, 0x89, 0x17, 0x52, 0xb3, 0x0b, 0x2b, 0x67, 0x0a, 0x29, + 0x27, 0x87, 0x8b, 0xce, 0xba, 0x3c, 0x46, 0x37, 0x27, 0xc4, 0xc9, 0x7f, 0xa0, 0x0c, 0x45, 0x3e, + 0xdd, 0x46, 0x36, 0x74, 0x1e, 0x61, 0x1f, 0x41, 0x6f, 0xef, 0x35, 0x13, 0xc2, 0x1d, 0xfd, 0xbd, + 0x27, 0xc9, 0x0b, 0x0f, 0x72, 0x4a, 0xe6, 0xea, 0xc8, 0xc3, 0x19, 0x2c, 0x1b, 0x75, 0x91, 0xaf, + 0xbf, 0x6d, 0x25, 0xba, 0xbc, 0xc8, 0xb5, 0xe2, 0x0f, 0x56, 0xc9, 0x9c, 0x6c, 0x0d, 0x64, 0x5f, + 0x40, 0xfb, 0xf3, 0xa9, 0x9f, 0x78, 0xe9, 0xe3, 0x55, 0xe4, 0x9b, 0xe2, 0x23, 0xac, 0x42, 0x4e, + 0x5d, 0x61, 0x53, 0x3a, 0x1d, 0x9b, 0xb1, 0x31, 0xab, 0xa9, 0x9f, 0x6f, 0x31, 0x8f, 0xb0, 0xaf, + 0xc3, 0x46, 0xda, 0x24, 0x9f, 0x3b, 0xa9, 0xa8, 0x7f, 0x61, 0xf1, 0xe4, 0x22, 0xf3, 0x2d, 0x2d, + 0xf2, 0x14, 0x56, 0x63, 0x2f, 0x18, 0xf9, 0x54, 0xaf, 0x27, 0x16, 0x33, 0x71, 0xcd, 0xec, 0x9e, + 0x78, 0x6f, 0xcb, 0x29, 0xfa, 0x82, 0x31, 0x48, 0x71, 0x47, 0x53, 0x06, 0xc9, 0x4c, 0x49, 0xd1, + 0x00, 0xbe, 0x0d, 0x2d, 0xb3, 0x31, 0xf2, 0x50, 0xa4, 0x53, 0xa7, 0x3d, 0xd3, 0x8f, 0x07, 0x4c, + 0xce, 0x30, 0x28, 0xed, 0x9f, 0x59, 0xd0, 0x75, 0x28, 0x63, 0x63, 0xaa, 0x35, 0x2a, 0xb8, 0xe7, + 0x51, 0xae, 0xda, 0xf9, 0x03, 0x56, 0x69, 0xda, 0x72, 0xac, 0x9b, 0x73, 0x17, 0x65, 0x7f, 0xa1, + 0x60, 0x54, 0x8f, 0x6b, 0xb0, 0x28, 0xc6, 0xb7, 0x01, 0xd7, 0x44, 0x97, 0x64, 0x77, 0xd2, 0xd8, + 0xb2, 0xd1, 0xa8, 0x11, 0x5b, 0xee, 0x41, 0x97, 0xbf, 0x24, 0xa0, 0x8f, 0x83, 0x7f, 0x78, 0xef, + 0x4b, 0x68, 0x68, 0xef, 0x29, 0x90, 0x0d, 0x58, 0x7d, 0xf9, 0xec, 0xe4, 0x70, 0xef, 0xf8, 0xb8, + 0x7f, 0xf4, 0xe2, 0xf1, 0x77, 0xf6, 0xbe, 0xdf, 0xdf, 0xdf, 0x3e, 0xde, 0xef, 0x2c, 0x90, 0x75, + 0x20, 0x87, 0x7b, 0xc7, 0x27, 0x7b, 0xbb, 0x06, 0xdc, 0x22, 0x37, 0xa1, 0xf7, 0xe2, 0xf0, 0xc5, + 0xf1, 0xde, 0x6e, 0xbf, 0xe8, 0xbb, 0x12, 0x79, 0x1f, 0xae, 0x0b, 0x7c, 0xc1, 0xe7, 0xe5, 0x07, + 0x3f, 0x2b, 0x43, 0x8b, 0xe7, 0x38, 0xf1, 0x27, 0xd8, 0x68, 0x44, 0x3e, 0x87, 0x25, 0xf1, 0x96, + 0x1f, 0x91, 0xf3, 0x69, 0xbe, 0x1e, 0xd8, 0x5b, 0xcf, 0x82, 0xc5, 0x24, 0xac, 0xfe, 0xd5, 0x3f, + 0xf9, 0x9f, 0x7f, 0xa7, 0xb4, 0x4c, 0x1a, 0x5b, 0x17, 0x9f, 0x6c, 0x8d, 0x68, 0x10, 0xb3, 0x3a, + 0xfe, 0x10, 0x20, 0x7d, 0xa1, 0x8e, 0x74, 0x95, 0x13, 0x9a, 0x79, 0xbe, 0xaf, 0x77, 0xbd, 0x00, + 0x23, 0xea, 0xbd, 0x8e, 0xf5, 0xae, 0xda, 0x2d, 0x56, 0xaf, 0x17, 0x78, 0x09, 0x7f, 0xad, 0xee, + 0x33, 0xeb, 0x1e, 0x19, 0x42, 0x53, 0x7f, 0x3b, 0x8e, 0xc8, 0xf8, 0x78, 0xc1, 0xeb, 0x77, 0xbd, + 0xf7, 0x0a, 0x71, 0x72, 0x01, 0xb1, 0x8d, 0x6b, 0x76, 0x87, 0xb5, 0x31, 0x45, 0x8a, 0xb4, 0x15, + 0x9f, 0xb3, 0x75, 0xfa, 0x44, 0x1c, 0xb9, 0xa1, 0x71, 0x5a, 0xee, 0x81, 0xba, 0xde, 0xfb, 0x73, + 0xb0, 0xa2, 0xad, 0xf7, 0xb1, 0xad, 0x0d, 0x9b, 0xb0, 0xb6, 0x06, 0x48, 0x23, 0x1f, 0xa8, 0xfb, + 0xcc, 0xba, 0xf7, 0xe0, 0xef, 0x7d, 0x04, 0x75, 0x75, 0x6e, 0x46, 0x7e, 0x0c, 0xcb, 0x46, 0x12, + 0x1a, 0x91, 0xc3, 0x28, 0xca, 0x59, 0xeb, 0xdd, 0x28, 0x46, 0x8a, 0x86, 0x6f, 0x62, 0xc3, 0x5d, + 0xb2, 0xce, 0x1a, 0x16, 0x59, 0x5c, 0x5b, 0x98, 0x4e, 0xc9, 0x6f, 0x63, 0xbd, 0xd2, 0xc4, 0x97, + 0x37, 0x76, 0x23, 0x2b, 0x51, 0x46, 0x6b, 0xef, 0xcf, 0xc1, 0x8a, 0xe6, 0x6e, 0x60, 0x73, 0xeb, + 0x64, 0x4d, 0x6f, 0x4e, 0x9d, 0x67, 0x51, 0xbc, 0x82, 0xa8, 0xbf, 0x9e, 0x46, 0xde, 0x57, 0x8c, + 0x55, 0xf4, 0xaa, 0x9a, 0x62, 0x91, 0xfc, 0xd3, 0x6a, 0x76, 0x17, 0x9b, 0x22, 0x04, 0x97, 0x4f, + 0x7f, 0x3c, 0x8d, 0x9c, 0x42, 0x43, 0x7b, 0x9d, 0x87, 0x5c, 0x9f, 0xfb, 0x92, 0x50, 0xaf, 0x57, + 0x84, 0x2a, 0x1a, 0x8a, 0x5e, 0xff, 0x16, 0xdb, 0x97, 0x7f, 0x08, 0x75, 0xf5, 0xde, 0x0b, 0xd9, + 0xd0, 0xde, 0xdf, 0xd1, 0xdf, 0xa7, 0xe9, 0x75, 0xf3, 0x88, 0x22, 0xe6, 0xd3, 0x6b, 0x67, 0xcc, + 0xf7, 0x12, 0x1a, 0xda, 0x9b, 0x2e, 0x6a, 0x00, 0xf9, 0x77, 0x63, 0xd4, 0x00, 0x0a, 0x9e, 0x80, + 0xb1, 0x57, 0xb0, 0x89, 0x06, 0xa9, 0x23, 0x7f, 0x27, 0xaf, 0xc3, 0x98, 0x1c, 0xc0, 0x35, 0xa1, + 0xa6, 0x4e, 0xe9, 0xbb, 0x2c, 0x43, 0xc1, 0x83, 0x75, 0xf7, 0x2d, 0xf2, 0x08, 0x6a, 0xf2, 0xe9, + 0x1e, 0xb2, 0x5e, 0xfc, 0x04, 0x51, 0x6f, 0x23, 0x07, 0x17, 0xe6, 0xc9, 0xf7, 0x01, 0xd2, 0x07, + 0x64, 0x94, 0x92, 0xc8, 0x3d, 0x48, 0xa3, 0x38, 0x20, 0xff, 0xda, 0x8c, 0xbd, 0x8e, 0x03, 0xec, + 0x10, 0x54, 0x12, 0x01, 0xbd, 0x94, 0xb7, 0x8d, 0x7f, 0x04, 0x0d, 0xed, 0x0d, 0x19, 0x35, 0x7d, + 0xf9, 0xf7, 0x67, 0xd4, 0xf4, 0x15, 0x3c, 0x39, 0x63, 0xf7, 0xb0, 0xf6, 0x35, 0xbb, 0xcd, 0x6a, + 0x8f, 0xbd, 0x51, 0x30, 0xe6, 0x04, 0x6c, 0x81, 0xce, 0x61, 0xd9, 0x78, 0x28, 0x46, 0x49, 0x68, + 0xd1, 0x33, 0x34, 0x4a, 0x42, 0x0b, 0xdf, 0x96, 0x91, 0x7c, 0x66, 0xaf, 0xb0, 0x76, 0x2e, 0x90, + 0x44, 0x6b, 0xe9, 0x07, 0xd0, 0xd0, 0x1e, 0x7d, 0x51, 0x63, 0xc9, 0xbf, 0x2f, 0xa3, 0xc6, 0x52, + 0xf4, 0x46, 0xcc, 0x1a, 0xb6, 0xd1, 0xb2, 0x91, 0x15, 0xf0, 0xde, 0x2c, 0xab, 0xfb, 0xc7, 0xd0, + 0x32, 0x9f, 0x81, 0x51, 0xb2, 0x5f, 0xf8, 0xa0, 0x8c, 0x92, 0xfd, 0x39, 0x6f, 0xc7, 0x08, 0x96, + 0xbe, 0xb7, 0xaa, 0x1a, 0xd9, 0xfa, 0x42, 0x64, 0xdd, 0x7c, 0x49, 0xbe, 0xcb, 0x14, 0x9c, 0xb8, + 0xc8, 0x4c, 0x36, 0x34, 0xae, 0xd5, 0xaf, 0x3b, 0x2b, 0x79, 0xc9, 0xdd, 0x79, 0x36, 0x99, 0x99, + 0xdf, 0xfc, 0xc5, 0x5d, 0x0b, 0x2f, 0x34, 0x6b, 0xbb, 0x96, 0x7e, 0xe7, 0x59, 0xdb, 0xb5, 0x8c, + 0x7b, 0xcf, 0xd9, 0x5d, 0x2b, 0xf1, 0x58, 0x1d, 0x01, 0xb4, 0x33, 0x89, 0xf2, 0x4a, 0x2a, 0x8a, + 0xef, 0x32, 0xf5, 0x6e, 0xbe, 0x39, 0xbf, 0xde, 0xd4, 0x20, 0x52, 0x09, 0x6e, 0xc9, 0x9b, 0x63, + 0x7f, 0x09, 0x9a, 0xfa, 0x03, 0x18, 0x44, 0x17, 0xe5, 0x6c, 0x4b, 0xef, 0x15, 0xe2, 0xcc, 0xc5, + 0x25, 0x4d, 0xbd, 0x19, 0xf2, 0x3d, 0x58, 0x57, 0xa2, 0xae, 0xe7, 0x5e, 0xc7, 0xe4, 0x83, 0x82, + 0x8c, 0x6c, 0xdd, 0x78, 0xe9, 0x5d, 0x9f, 0x9b, 0xb2, 0x7d, 0xdf, 0x62, 0x4c, 0x63, 0xbe, 0x2c, + 0x90, 0x6e, 0x18, 0x45, 0x0f, 0x2a, 0xa4, 0x1b, 0x46, 0xe1, 0x73, 0x04, 0x92, 0x69, 0xc8, 0xaa, + 0x31, 0x47, 0xfc, 0xc0, 0x92, 0xfc, 0x00, 0xda, 0xda, 0xed, 0x96, 0xe3, 0x59, 0x30, 0x50, 0x02, + 0x90, 0xbf, 0x78, 0xd9, 0x2b, 0x32, 0xcd, 0xed, 0x0d, 0xac, 0x7f, 0xc5, 0x36, 0x26, 0x87, 0x31, + 0xff, 0x0e, 0x34, 0xf4, 0x9b, 0x33, 0x6f, 0xa8, 0x77, 0x43, 0x43, 0xe9, 0xf7, 0x06, 0xef, 0x5b, + 0xe4, 0x1f, 0x5a, 0xd0, 0x34, 0xee, 0xa1, 0x18, 0x87, 0xf5, 0x99, 0x7a, 0xba, 0x3a, 0x4e, 0xaf, + 0xc8, 0x76, 0xb0, 0x93, 0x07, 0xf7, 0xbe, 0x6d, 0x4c, 0xc2, 0x17, 0x46, 0xfc, 0x65, 0x33, 0xfb, + 0x5c, 0xe0, 0x97, 0x59, 0x02, 0xfd, 0x72, 0xea, 0x97, 0xf7, 0x2d, 0xf2, 0xc7, 0x16, 0xb4, 0xcc, + 0xa8, 0xa1, 0x5a, 0xaa, 0xc2, 0xf8, 0xa4, 0x5a, 0xaa, 0x39, 0xa1, 0xc6, 0x1f, 0x60, 0x2f, 0x4f, + 0xee, 0x39, 0x46, 0x2f, 0xc5, 0x9b, 0x13, 0xbf, 0x59, 0x6f, 0xc9, 0x67, 0xfc, 0x95, 0x52, 0x19, + 0xdb, 0x27, 0xf9, 0xf7, 0x32, 0xd5, 0xf2, 0xea, 0x2f, 0x5c, 0xde, 0xb5, 0xee, 0x5b, 0xe4, 0x47, + 0xfc, 0x99, 0x3c, 0x19, 0x7e, 0x66, 0x5c, 0xf2, 0xb6, 0xdf, 0xdb, 0xb7, 0x71, 0x4c, 0x37, 0xed, + 0xeb, 0xc6, 0x98, 0xb2, 0xfb, 0xf1, 0x36, 0xef, 0x9d, 0x78, 0x9c, 0x32, 0xdd, 0x50, 0x72, 0x0f, + 0x56, 0xce, 0xef, 0xe4, 0x98, 0x77, 0x52, 0x90, 0x1b, 0xac, 0xfc, 0x96, 0xd5, 0xd8, 0xf7, 0xb0, + 0xaf, 0xb7, 0xed, 0x0f, 0xe6, 0xf6, 0x75, 0x0b, 0x63, 0x7f, 0xac, 0xc7, 0x47, 0x00, 0xe9, 0x39, + 0x1c, 0xc9, 0x9c, 0x03, 0x29, 0x01, 0xcf, 0x1f, 0xd5, 0x99, 0xf2, 0x22, 0x8f, 0x8b, 0x58, 0x8d, + 0x3f, 0xe4, 0xea, 0xea, 0x99, 0x3c, 0x41, 0xd2, 0x8d, 0x12, 0xf3, 0xc0, 0xcc, 0x30, 0x4a, 0xb2, + 0xf5, 0x1b, 0xca, 0x4a, 0x1d, 0x47, 0xbd, 0x80, 0xe5, 0x83, 0x30, 0x7c, 0x35, 0x9d, 0xa8, 0x33, + 0x75, 0x33, 0x2c, 0xbf, 0xef, 0xc6, 0xe7, 0xbd, 0xcc, 0x28, 0xec, 0x5b, 0x58, 0x55, 0x8f, 0x74, + 0xb5, 0xaa, 0xb6, 0xbe, 0x48, 0xcf, 0xf9, 0xbe, 0x24, 0x2e, 0xac, 0x28, 0x1d, 0xa8, 0x3a, 0xde, + 0x33, 0xab, 0x31, 0x34, 0x5f, 0xb6, 0x09, 0xc3, 0x7a, 0x96, 0xbd, 0xdd, 0x8a, 0x65, 0x9d, 0xf7, + 0x2d, 0x72, 0x04, 0xcd, 0x5d, 0x3a, 0xc0, 0x2c, 0x78, 0x8c, 0x6d, 0xaf, 0xa6, 0x1d, 0x57, 0x41, + 0xf1, 0xde, 0xb2, 0x01, 0x34, 0xf7, 0x85, 0x89, 0x3b, 0x8b, 0xe8, 0x4f, 0xb6, 0xbe, 0x10, 0x51, + 0xf3, 0x2f, 0xe5, 0xbe, 0x20, 0x8f, 0x15, 0x8c, 0x7d, 0x21, 0x73, 0x0e, 0x61, 0xec, 0x0b, 0xb9, + 0x73, 0x08, 0x63, 0xaa, 0xe5, 0xb1, 0x06, 0xf1, 0x61, 0x25, 0x77, 0x74, 0xa1, 0xb6, 0x84, 0x79, + 0x07, 0x1e, 0xbd, 0x5b, 0xf3, 0x09, 0xcc, 0xd6, 0xee, 0x99, 0xad, 0x1d, 0xc3, 0xf2, 0x2e, 0xe5, + 0x93, 0xc5, 0x93, 0x06, 0x33, 0x97, 0x99, 0xf4, 0x94, 0xc4, 0xac, 0x02, 0x47, 0x9c, 0xb9, 0xf1, + 0x63, 0xc6, 0x1e, 0xf9, 0x21, 0x34, 0x9e, 0xd2, 0x44, 0x66, 0x09, 0x2a, 0xd3, 0x33, 0x93, 0x36, + 0xd8, 0x2b, 0x48, 0x32, 0x34, 0x79, 0x06, 0x6b, 0xdb, 0xa2, 0xc3, 0x11, 0xe5, 0xca, 0xa9, 0xef, + 0x0d, 0xbf, 0x24, 0x7f, 0x11, 0x2b, 0x57, 0x29, 0xd2, 0xeb, 0x5a, 0xda, 0x97, 0x5e, 0x79, 0x3b, + 0x03, 0x2f, 0xaa, 0x39, 0x08, 0x87, 0x54, 0x33, 0x81, 0x02, 0x68, 0x68, 0x77, 0x14, 0x94, 0x00, + 0xe5, 0x2f, 0x6c, 0x28, 0x01, 0x2a, 0xb8, 0xd2, 0x60, 0xdf, 0xc5, 0x76, 0x6c, 0x72, 0x2b, 0x6d, + 0x87, 0x5f, 0x63, 0x48, 0x5b, 0xda, 0xfa, 0xc2, 0x1d, 0x27, 0x5f, 0x92, 0x97, 0xf8, 0x06, 0x8c, + 0x9e, 0x09, 0x99, 0xda, 0xd2, 0xd9, 0xa4, 0x49, 0x35, 0x59, 0x1a, 0xca, 0xb4, 0xaf, 0x79, 0x53, + 0x68, 0x29, 0x7d, 0x13, 0xe0, 0x38, 0x09, 0x27, 0xbb, 0x2e, 0x1d, 0x87, 0x41, 0xaa, 0x6b, 0xd3, + 0x3c, 0xbc, 0x54, 0x7f, 0x69, 0xc9, 0x78, 0xe4, 0xa5, 0xe6, 0x7c, 0x18, 0x89, 0xa4, 0x92, 0xb9, + 0xe6, 0xa6, 0xea, 0xa9, 0x09, 0x29, 0x48, 0xd7, 0xbb, 0x6f, 0x91, 0x6d, 0x80, 0xf4, 0xec, 0x4a, + 0xb9, 0x12, 0xb9, 0x63, 0x31, 0xa5, 0xf6, 0x0a, 0x0e, 0xba, 0x8e, 0xa0, 0x9e, 0x1e, 0x86, 0x6c, + 0xa4, 0xb7, 0x8c, 0x8c, 0xa3, 0x13, 0xb5, 0x83, 0xe7, 0x8e, 0x28, 0xec, 0x0e, 0x4e, 0x15, 0x90, + 0x1a, 0x9b, 0x2a, 0x3c, 0x77, 0xf0, 0x60, 0x95, 0x77, 0x50, 0x99, 0x23, 0x98, 0x43, 0x26, 0x47, + 0x52, 0x70, 0x4c, 0xa0, 0xa4, 0xb9, 0x30, 0xca, 0x6e, 0x44, 0x44, 0x18, 0xb7, 0xf2, 0xfc, 0x35, + 0xa6, 0x9a, 0xc7, 0xb0, 0x92, 0x0b, 0x03, 0x2b, 0x91, 0x9e, 0x17, 0x99, 0x57, 0x22, 0x3d, 0x37, + 0x82, 0x6c, 0x5f, 0xc3, 0x26, 0xdb, 0x36, 0xa0, 0x07, 0x74, 0xe9, 0x25, 0x83, 0x73, 0xd6, 0xdc, + 0x2f, 0x2c, 0x58, 0x2d, 0x88, 0xf2, 0x92, 0x0f, 0xa5, 0x33, 0x3d, 0x37, 0x02, 0xdc, 0x2b, 0x0c, + 0x02, 0xda, 0xc7, 0xd8, 0xce, 0xe7, 0xe4, 0x3b, 0xc6, 0xc6, 0xc6, 0xe3, 0x6f, 0x42, 0x32, 0xdf, + 0x68, 0x54, 0x14, 0x5a, 0x14, 0x3f, 0x81, 0x0d, 0xde, 0x91, 0x6d, 0xdf, 0xcf, 0x04, 0x28, 0x6f, + 0xe6, 0xfe, 0x51, 0x81, 0x11, 0x78, 0xed, 0xcd, 0xff, 0x47, 0x06, 0x73, 0xcc, 0x55, 0xde, 0x55, + 0x32, 0x85, 0x4e, 0x36, 0xe8, 0x47, 0xe6, 0xd7, 0xd5, 0xfb, 0xc0, 0x70, 0x0b, 0xf3, 0x81, 0x42, + 0xfb, 0x77, 0xb0, 0xb1, 0x0f, 0xec, 0x5e, 0xd1, 0xbc, 0x70, 0x4f, 0x91, 0xad, 0xc7, 0x5f, 0x51, + 0x11, 0xca, 0xcc, 0x38, 0x65, 0x03, 0xf3, 0x42, 0xaa, 0xca, 0x31, 0x2d, 0x0e, 0x70, 0x7e, 0x84, + 0xcd, 0xdf, 0xb2, 0xdf, 0x2b, 0x6a, 0x3e, 0xe2, 0x9f, 0x70, 0x17, 0x75, 0x23, 0x2b, 0xd7, 0xb2, + 0x07, 0xb7, 0x8a, 0xd6, 0x7b, 0xae, 0xaf, 0x91, 0x99, 0xeb, 0x85, 0xfb, 0xd6, 0xe3, 0x3b, 0x3f, + 0xf8, 0x9d, 0x91, 0x97, 0x9c, 0x4f, 0x4f, 0x37, 0x07, 0xe1, 0x78, 0xcb, 0x97, 0x21, 0x32, 0x91, + 0xf1, 0xbc, 0xe5, 0x07, 0xc3, 0x2d, 0xfc, 0xfe, 0x74, 0x11, 0xff, 0xef, 0xc9, 0xd7, 0xff, 0x5f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0xa0, 0xa0, 0x17, 0x29, 0x65, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 695100a8..3c361627 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -883,6 +883,13 @@ message SendRequest { maximum enforced. */ uint32 cltv_limit = 10; + + /** + An optional field that can be used to pass an arbitrary set of TLV records + to a peer which understands the new records. This can be used to pass + application specific data during the payment attempt. + */ + map dest_tlv = 11; } message SendResponse { @@ -1665,6 +1672,14 @@ message QueryRoutesRequest { A list of directed node pairs that will be ignored during path finding. */ repeated NodePair ignored_pairs = 10; + + /** + An optional field that can be used to pass an arbitrary set of TLV records + to a peer which understands the new records. This can be used to pass + application specific data during the payment attempt. If the destination + does not support the specified recrods, and error will be returned. + */ + map dest_tlv = 11; } message NodePair { @@ -1711,6 +1726,20 @@ message Hop { can be executed without relying on a copy of the channel graph. */ string pub_key = 8 [json_name = "pub_key"]; + + /** + If set to true, then this hop will be encoded using the new variable length + TLV format. Note that if any custom tlv_records below are specified, then + this field MUST be set to true for them to be encoded properly. + */ + bool tlv_payload = 9 [json_name = "tlv_payload"]; + + /** + An optional set of key-value TLV records. This is useful within the context + of the SendToRoute call as it allows callers to specify arbitrary K-V pairs + to drop off at each hop within the onion. + */ + map tlv_records = 10 [json_name = "tlv_records"]; } /** diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index 383c7776..bb726235 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -2305,6 +2305,19 @@ "pub_key": { "type": "string", "description": "*\nAn optional public key of the hop. If the public key is given, the payment\ncan be executed without relying on a copy of the channel graph." + }, + "tlv_payload": { + "type": "boolean", + "format": "boolean", + "description": "* \nIf set to true, then this hop will be encoded using the new variable length\nTLV format. Note that if any custom tlv_records below are specified, then\nthis field MUST be set to true for them to be encoded properly." + }, + "tlv_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "*\nAn optional set of key-value TLV records. This is useful within the context\nof the SendToRoute call as it allows callers to specify arbitrary K-V pairs\nto drop off at each hop within the onion." } } }, @@ -3286,6 +3299,14 @@ "type": "integer", "format": "int64", "description": "* \nAn optional maximum total time lock for the route. If zero, there is no\nmaximum enforced." + }, + "dest_tlv": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "* \nAn optional field that can be used to pass an arbitrary set of TLV records\nto a peer which understands the new records. This can be used to pass\napplication specific data during the payment attempt." } } }, diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 9905c56b..d6a16a9a 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -4013,13 +4013,15 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) { } // As preliminary setup, we'll create two new nodes: Carol and Dave, - // such that we now have a 4 ndoe, 3 channel topology. Dave will make - // a channel with Alice, and Carol with Dave. After this setup, the + // such that we now have a 4 node, 3 channel topology. Dave will make a + // channel with Alice, and Carol with Dave. After this setup, the // network topology should now look like: // Carol -> Dave -> Alice -> Bob // - // First, we'll create Dave and establish a channel to Alice. - dave, err := net.NewNode("Dave", nil) + // First, we'll create Dave and establish a channel to Alice. Dave will + // be running an older node that requires the legacy onion payload. + daveArgs := []string{"--legacyprotocol.onion"} + dave, err := net.NewNode("Dave", daveArgs) if err != nil { t.Fatalf("unable to create new nodes: %v", err) } diff --git a/lnwire/features.go b/lnwire/features.go index 1dd536ba..4cdbd054 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -46,6 +46,15 @@ const ( // efficient network view reconciliation. GossipQueriesOptional FeatureBit = 7 + // TLVOnionPayloadRequired is a feature bit that indicates a node is + // able to decode the new TLV information included in the onion packet. + TLVOnionPayloadRequired FeatureBit = 8 + + // TLVOnionPayloadRequired is an optional feature bit that indicates a + // node is able to decode the new TLV information included in the onion + // packet. + TLVOnionPayloadOptional FeatureBit = 9 + // maxAllowedSize is a maximum allowed size of feature vector. // // NOTE: Within the protocol, the maximum allowed message size is 65535 @@ -76,7 +85,10 @@ var LocalFeatures = map[FeatureBit]string{ // name. All known global feature bits must be assigned a name in this mapping. // Global features are those which are advertised to the entire network. A full // description of these feature bits is provided in the BOLT-09 specification. -var GlobalFeatures map[FeatureBit]string +var GlobalFeatures = map[FeatureBit]string{ + TLVOnionPayloadRequired: "tlv-onion", + TLVOnionPayloadOptional: "tlv-onion", +} // RawFeatureVector represents a set of feature bits as defined in BOLT-09. A // RawFeatureVector itself just stores a set of bit flags but can be used to diff --git a/routing/control_tower_test.go b/routing/control_tower_test.go index 0a765fdb..49cc6d43 100644 --- a/routing/control_tower_test.go +++ b/routing/control_tower_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec" + "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/routing/route" @@ -26,6 +27,7 @@ var ( ChannelID: 12345, OutgoingTimeLock: 111, AmtToForward: 555, + LegacyPayload: true, } testRoute = route.Route{ @@ -142,8 +144,11 @@ func TestControlTowerSubscribeSuccess(t *testing.T) { if result.Preimage != preimg { t.Fatal("unexpected preimage") } + if !reflect.DeepEqual(result.Route, &attempt.Route) { - t.Fatal("unexpected route") + t.Fatalf("unexpected route: %v vs %v", + spew.Sdump(result.Route), + spew.Sdump(attempt.Route)) } // After the final event, we expect the channel to be closed. diff --git a/routing/missioncontrol_store_test.go b/routing/missioncontrol_store_test.go index 6afbe4f9..86111d0b 100644 --- a/routing/missioncontrol_store_test.go +++ b/routing/missioncontrol_store_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/lnwire" "github.com/coreos/bbolt" @@ -50,7 +51,8 @@ func TestMissionControlStore(t *testing.T) { SourcePubKey: route.Vertex{1}, Hops: []*route.Hop{ { - PubKeyBytes: route.Vertex{2}, + PubKeyBytes: route.Vertex{2}, + LegacyPayload: true, }, }, } @@ -99,10 +101,12 @@ func TestMissionControlStore(t *testing.T) { // Check that results are stored in chronological order. if !reflect.DeepEqual(&result1, results[0]) { - t.Fatal() + t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result1), + spew.Sdump(results[0])) } if !reflect.DeepEqual(&result2, results[1]) { - t.Fatal() + t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result2), + spew.Sdump(results[1])) } // Recreate store to test pruning. @@ -132,9 +136,11 @@ func TestMissionControlStore(t *testing.T) { } if !reflect.DeepEqual(&result2, results[0]) { - t.Fatal() + t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result2), + spew.Sdump(results[0])) } if !reflect.DeepEqual(&result3, results[1]) { - t.Fatal() + t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result3), + spew.Sdump(results[1])) } } diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go index 2d8247ac..cbc7194d 100644 --- a/routing/missioncontrol_test.go +++ b/routing/missioncontrol_test.go @@ -16,13 +16,15 @@ var ( SourcePubKey: route.Vertex{10}, Hops: []*route.Hop{ { - ChannelID: 1, - PubKeyBytes: route.Vertex{11}, - AmtToForward: 1000, + ChannelID: 1, + PubKeyBytes: route.Vertex{11}, + AmtToForward: 1000, + LegacyPayload: true, }, { - ChannelID: 2, - PubKeyBytes: route.Vertex{12}, + ChannelID: 2, + PubKeyBytes: route.Vertex{12}, + LegacyPayload: true, }, }, } @@ -167,7 +169,8 @@ func TestMissionControl(t *testing.T) { // Check whether history snapshot looks sane. history := ctx.mc.GetHistorySnapshot() if len(history.Nodes) != 1 { - t.Fatal("unexpected number of nodes") + t.Fatalf("unexpected number of nodes: expected 1 got %v", + len(history.Nodes)) } if len(history.Pairs) != 1 { diff --git a/routing/pathfind.go b/routing/pathfind.go index b82ef465..93efba66 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -2,6 +2,7 @@ package routing import ( "container/heap" + "fmt" "math" "github.com/coreos/bbolt" @@ -9,6 +10,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" ) const ( @@ -98,7 +100,8 @@ func isSamePath(path1, path2 []*channeldb.ChannelEdgePolicy) bool { // the source to the target node of the path finding attempt. func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32, - finalCLTVDelta uint16) (*route.Route, error) { + finalCLTVDelta uint16, + finalDestRecords []tlv.Record) (*route.Route, error) { var ( hops []*route.Hop @@ -179,7 +182,27 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, ChannelID: edge.ChannelID, AmtToForward: amtToForward, OutgoingTimeLock: outgoingTimeLock, + LegacyPayload: true, } + + // We start out above by assuming that this node needs the + // legacy payload, as if we don't have the full + // NodeAnnouncement information for this node, then we can't + // assume it knows the latest features. If we do have a feature + // vector for this node, then we'll update the info now. + if edge.Node.Features != nil { + features := edge.Node.Features + currentHop.LegacyPayload = !features.HasFeature( + lnwire.TLVOnionPayloadOptional, + ) + } + + // If this is the last hop, then we'll populate any TLV records + // destined for it. + if i == len(pathEdges)-1 && len(finalDestRecords) != 0 { + currentHop.TLVRecords = finalDestRecords + } + hops = append([]*route.Hop{currentHop}, hops...) // Finally, we update the amount that needs to flow into the @@ -190,7 +213,8 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex, // With the base routing data expressed as hops, build the full route newRoute, err := route.NewRouteFromHops( - nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex), hops, + nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex), + hops, ) if err != nil { return nil, err @@ -261,6 +285,11 @@ type RestrictParams struct { // ctlv. After path finding is complete, the caller needs to increase // all cltv expiry heights with the required final cltv delta. CltvLimit *uint32 + + // DestPayloadTLV should be set to true if we need to drop off a TLV + // payload at the final hop in order to properly complete this payment + // attempt. + DestPayloadTLV bool } // PathFindingConfig defines global parameters that control the trade-off in @@ -316,10 +345,34 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, node *channeldb.LightningNode) error { // TODO(roasbeef): with larger graph can just use disk seeks // with a visited map - distance[route.Vertex(node.PubKeyBytes)] = nodeWithDist{ + vertex := route.Vertex(node.PubKeyBytes) + distance[vertex] = nodeWithDist{ dist: infinity, node: route.Vertex(node.PubKeyBytes), } + + // If we don't have any features for this node, then we can + // stop here. + if node.Features == nil || !r.DestPayloadTLV { + return nil + } + + // We only need to perform this check for the final node, so we + // can exit here if this isn't them. + if vertex != target { + return nil + } + + // If we have any records for the final hop, then we'll check + // not to ensure that they are actually able to interpret them. + supportsTLV := node.Features.HasFeature( + lnwire.TLVOnionPayloadOptional, + ) + if !supportsTLV { + return fmt.Errorf("destination hop doesn't " + + "understand new TLV paylods") + } + return nil }); err != nil { return nil, err diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 64676c83..3151220a 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -670,7 +670,8 @@ func TestFindLowestFeePath(t *testing.T) { } route, err := newRoute( paymentAmt, sourceVertex, path, startingHeight, - finalHopCLTV) + finalHopCLTV, nil, + ) if err != nil { t.Fatalf("unable to create path: %v", err) } @@ -819,7 +820,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc route, err := newRoute( paymentAmt, sourceVertex, path, startingHeight, - finalHopCLTV, + finalHopCLTV, nil, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -857,9 +858,15 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc for i := 0; i < len(expectedHops)-1; i++ { var expectedHop [8]byte binary.BigEndian.PutUint64(expectedHop[:], route.Hops[i+1].ChannelID) - if !bytes.Equal(sphinxPath[i].HopData.NextAddress[:], expectedHop[:]) { + + hopData, err := sphinxPath[i].HopPayload.HopData() + if err != nil { + t.Fatalf("unable to make hop data: %v", err) + } + + if !bytes.Equal(hopData.NextAddress[:], expectedHop[:]) { t.Fatalf("first hop has incorrect next hop: expected %x, got %x", - expectedHop[:], sphinxPath[i].HopData.NextAddress) + expectedHop[:], hopData.NextAddress[:]) } } @@ -867,9 +874,15 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc // to indicate it's the exit hop. var exitHop [8]byte lastHopIndex := len(expectedHops) - 1 - if !bytes.Equal(sphinxPath[lastHopIndex].HopData.NextAddress[:], exitHop[:]) { + + hopData, err := sphinxPath[lastHopIndex].HopPayload.HopData() + if err != nil { + t.Fatalf("unable to create hop data: %v", err) + } + + if !bytes.Equal(hopData.NextAddress[:], exitHop[:]) { t.Fatalf("first hop has incorrect next hop: expected %x, got %x", - exitHop[:], sphinxPath[lastHopIndex].HopData.NextAddress) + exitHop[:], hopData.NextAddress) } var expectedTotalFee lnwire.MilliSatoshi @@ -1001,7 +1014,11 @@ func TestNewRoute(t *testing.T) { timeLockDelta uint16) *channeldb.ChannelEdgePolicy { return &channeldb.ChannelEdgePolicy{ - Node: &channeldb.LightningNode{}, + Node: &channeldb.LightningNode{ + Features: lnwire.NewFeatureVector( + nil, nil, + ), + }, FeeProportionalMillionths: feeRate, FeeBaseMSat: baseFee, TimeLockDelta: timeLockDelta, @@ -1176,9 +1193,11 @@ func TestNewRoute(t *testing.T) { } t.Run(testCase.name, func(t *testing.T) { - route, err := newRoute(testCase.paymentAmount, - sourceVertex, testCase.hops, startingHeight, - finalHopCLTV) + route, err := newRoute( + testCase.paymentAmount, sourceVertex, + testCase.hops, startingHeight, finalHopCLTV, + nil, + ) if testCase.expectError { expectedCode := testCase.expectedErrorCode @@ -1683,7 +1702,7 @@ func TestPathFindSpecExample(t *testing.T) { carol := ctx.aliases["C"] const amt lnwire.MilliSatoshi = 4999999 route, err := ctx.router.FindRoute( - bobNode.PubKeyBytes, carol, amt, noRestrictions, + bobNode.PubKeyBytes, carol, amt, noRestrictions, nil, ) if err != nil { t.Fatalf("unable to find route: %v", err) @@ -1742,7 +1761,7 @@ func TestPathFindSpecExample(t *testing.T) { // We'll now request a route from A -> B -> C. route, err = ctx.router.FindRoute( - source.PubKeyBytes, carol, amt, noRestrictions, + source.PubKeyBytes, carol, amt, noRestrictions, nil, ) if err != nil { t.Fatalf("unable to find routes: %v", err) @@ -1925,7 +1944,7 @@ func TestRestrictOutgoingChannel(t *testing.T) { } route, err := newRoute( paymentAmt, sourceVertex, path, startingHeight, - finalHopCLTV, + finalHopCLTV, nil, ) if err != nil { t.Fatalf("unable to create path: %v", err) @@ -2033,6 +2052,7 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) { ) route, err := newRoute( paymentAmt, sourceVertex, path, startingHeight, finalHopCLTV, + nil, ) if err != nil { t.Fatalf("unable to create path: %v", err) diff --git a/routing/payment_session.go b/routing/payment_session.go index 3894bc60..16baa0f4 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -127,6 +127,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes) route, err := newRoute( payment.Amount, sourceVertex, path, height, finalCltvDelta, + payment.FinalDestRecords, ) if err != nil { // TODO(roasbeef): return which edge/vertex didn't work diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 5d31e902..67b6c04b 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -26,7 +26,11 @@ func TestRequestRoute(t *testing.T) { path := []*channeldb.ChannelEdgePolicy{ { - Node: &channeldb.LightningNode{}, + Node: &channeldb.LightningNode{ + Features: lnwire.NewFeatureVector( + nil, nil, + ), + }, }, } diff --git a/routing/route/route.go b/routing/route/route.go index 50688c57..70712d04 100644 --- a/routing/route/route.go +++ b/routing/route/route.go @@ -1,14 +1,17 @@ package route import ( + "bytes" "encoding/binary" "fmt" + "io" "strconv" "strings" "github.com/btcsuite/btcd/btcec" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" ) // VertexSize is the size of the array to store a vertex. @@ -72,6 +75,61 @@ type Hop struct { // hop. This value is less than the value that the incoming HTLC // carries as a fee will be subtracted by the hop. AmtToForward lnwire.MilliSatoshi + + // TLVRecords if non-nil are a set of additional TLV records that + // should be included in the forwarding instructions for this node. + TLVRecords []tlv.Record + + // LegacyPayload if true, then this signals that this node doesn't + // understand the new TLV payload, so we must instead use the legacy + // payload. + LegacyPayload bool +} + +// PackHopPayload writes to the passed io.Writer, the series of byes that can +// be placed directly into the per-hop payload (EOB) for this hop. This will +// include the required routing fields, as well as serializing any of the +// passed optional TLVRecords. nextChanID is the unique channel ID that +// references the _outgoing_ channel ID that follows this hop. This field +// follows the same semantics as the NextAddress field in the onion: it should +// be set to zero to indicate the terminal hop. +func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { + // If this is a legacy payload, then we'll exit here as this method + // shouldn't be called. + if h.LegacyPayload == true { + return fmt.Errorf("cannot pack hop payloads for legacy " + + "payloads") + } + + // Otherwise, we'll need to make a new stream that includes our + // required routing fields, as well as these optional values. + amt := uint64(h.AmtToForward) + combinedRecords := append(h.TLVRecords, + tlv.MakeDynamicRecord( + tlv.AmtOnionType, &amt, func() uint64 { + return tlv.SizeTUint64(amt) + }, + tlv.ETUint64, tlv.DTUint64, + ), + tlv.MakeDynamicRecord( + tlv.LockTimeOnionType, &h.OutgoingTimeLock, func() uint64 { + return tlv.SizeTUint32(h.OutgoingTimeLock) + }, + tlv.ETUint32, tlv.DTUint32, + ), + tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &nextChanID), + ) + + // To ensure we produce a canonical stream, we'll sort the records + // before encoding them as a stream in the hop payload. + tlv.SortRecords(combinedRecords) + + tlvStream, err := tlv.NewStream(combinedRecords...) + if err != nil { + return err + } + + return tlvStream.Encode(w) } // Route represents a path through the channel graph which runs over one or @@ -156,7 +214,8 @@ func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32, // ToSphinxPath converts a complete route into a sphinx PaymentPath that // contains the per-hop paylods used to encoding the HTLC routing data for each -// hop in the route. +// hop in the route. This method also accepts an optional EOB payload for the +// final hop. func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) { var path sphinx.PaymentPath @@ -171,17 +230,6 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) { return nil, err } - path[i] = sphinx.OnionHop{ - NodePub: *pub, - HopData: sphinx.HopData{ - // TODO(roasbeef): properly set realm, make - // sphinx type an enum actually? - Realm: [1]byte{0}, - ForwardAmount: uint64(hop.AmtToForward), - OutgoingCltv: hop.OutgoingTimeLock, - }, - } - // As a base case, the next hop is set to all zeroes in order // to indicate that the "last hop" as no further hops after it. nextHop := uint64(0) @@ -192,9 +240,50 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) { nextHop = r.Hops[i+1].ChannelID } - binary.BigEndian.PutUint64( - path[i].HopData.NextAddress[:], nextHop, - ) + var payload sphinx.HopPayload + + // If this is the legacy payload, then we can just include the + // hop data as normal. + if hop.LegacyPayload { + // Before we encode this value, we'll pack the next hop + // into the NextAddress field of the hop info to ensure + // we point to the right now. + hopData := sphinx.HopData{ + ForwardAmount: uint64(hop.AmtToForward), + OutgoingCltv: hop.OutgoingTimeLock, + } + binary.BigEndian.PutUint64( + hopData.NextAddress[:], nextHop, + ) + + payload, err = sphinx.NewHopPayload(&hopData, nil) + if err != nil { + return nil, err + } + } else { + // For non-legacy payloads, we'll need to pack the + // routing information, along with any extra TLV + // information into the new per-hop payload format. + // We'll also pass in the chan ID of the hop this + // channel should be forwarded to so we can construct a + // valid payload. + var b bytes.Buffer + err := hop.PackHopPayload(&b, nextHop) + if err != nil { + return nil, err + } + + // TODO(roasbeef): make better API for NewHopPayload? + payload, err = sphinx.NewHopPayload(nil, b.Bytes()) + if err != nil { + return nil, err + } + } + + path[i] = sphinx.OnionHop{ + NodePub: *pub, + HopPayload: payload, + } } return &path, nil diff --git a/routing/router.go b/routing/router.go index 65b28f51..ae62c17b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -26,6 +26,7 @@ import ( "github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/ticker" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/zpay32" ) @@ -1429,6 +1430,7 @@ type routingMsg struct { // factoring in channel capacities and cumulative fees along the route. func (r *ChannelRouter) FindRoute(source, target route.Vertex, amt lnwire.MilliSatoshi, restrictions *RestrictParams, + destTlvRecords []tlv.Record, finalExpiry ...uint16) (*route.Route, error) { var finalCLTVDelta uint16 @@ -1482,6 +1484,7 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex, // Create the route with absolute time lock values. route, err := newRoute( amt, source, path, uint32(currentHeight), finalCLTVDelta, + destTlvRecords, ) if err != nil { return nil, err @@ -1630,7 +1633,11 @@ type LightningPayment struct { // attempting to complete. PaymentRequest []byte - // TODO(roasbeef): add e2e message? + // FinalDestRecords are TLV records that are to be sent to the final + // hop in the new onion payload format. If the destination does not + // understand this new onion payload format, then the payment will + // fail. + FinalDestRecords []tlv.Record } // SendPayment attempts to send a payment as described within the passed @@ -1694,6 +1701,8 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( // Record this payment hash with the ControlTower, ensuring it is not // already in-flight. + // + // TODO(roasbeef): store records as part of creation info? info := &channeldb.PaymentCreationInfo{ PaymentHash: payment.PaymentHash, Value: payment.Amount, diff --git a/routing/router_test.go b/routing/router_test.go index a1fa141f..9ade7cf0 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -231,7 +231,7 @@ func TestFindRoutesWithFeeLimit(t *testing.T) { route, err := ctx.router.FindRoute( ctx.router.selfNode.PubKeyBytes, - target, paymentAmt, restrictions, + target, paymentAmt, restrictions, nil, zpay32.DefaultFinalCLTVDelta, ) if err != nil { @@ -390,12 +390,14 @@ func TestChannelUpdateValidation(t *testing.T) { hops := []*route.Hop{ { - ChannelID: 1, - PubKeyBytes: hop1, + ChannelID: 1, + PubKeyBytes: hop1, + LegacyPayload: true, }, { - ChannelID: 2, - PubKeyBytes: hop2, + ChannelID: 2, + PubKeyBytes: hop2, + LegacyPayload: true, }, } @@ -1074,8 +1076,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { t.Parallel() const startingBlockHeight = 101 - ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight, - basicGraphFilePath) + ctx, cleanUp, err := createTestCtxFromFile( + startingBlockHeight, basicGraphFilePath, + ) if err != nil { t.Fatalf("unable to create router: %v", err) } @@ -1108,7 +1111,8 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { fundingTx, _, chanID, err := createChannelEdge(ctx, bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(), - 10000, 500) + 10000, 500, + ) if err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -1266,7 +1270,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { copy(targetPubKeyBytes[:], targetNode.SerializeCompressed()) _, err = ctx.router.FindRoute( ctx.router.selfNode.PubKeyBytes, - targetPubKeyBytes, paymentAmt, noRestrictions, + targetPubKeyBytes, paymentAmt, noRestrictions, nil, zpay32.DefaultFinalCLTVDelta, ) if err != nil { @@ -1309,7 +1313,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { // updated. _, err = ctx.router.FindRoute( ctx.router.selfNode.PubKeyBytes, - targetPubKeyBytes, paymentAmt, noRestrictions, + targetPubKeyBytes, paymentAmt, noRestrictions, nil, zpay32.DefaultFinalCLTVDelta, ) if err != nil { @@ -2632,12 +2636,14 @@ func TestRouterPaymentStateMachine(t *testing.T) { hop2 := testGraph.aliasMap["c"] hops := []*route.Hop{ { - ChannelID: 1, - PubKeyBytes: hop1, + ChannelID: 1, + PubKeyBytes: hop1, + LegacyPayload: true, }, { - ChannelID: 2, - PubKeyBytes: hop2, + ChannelID: 2, + PubKeyBytes: hop2, + LegacyPayload: true, }, } @@ -3270,14 +3276,16 @@ func TestSendToRouteStructuredError(t *testing.T) { hop2 := ctx.aliases["c"] hops := []*route.Hop{ { - ChannelID: 1, - PubKeyBytes: hop1, - AmtToForward: payAmt, + ChannelID: 1, + PubKeyBytes: hop1, + AmtToForward: payAmt, + LegacyPayload: true, }, { - ChannelID: 2, - PubKeyBytes: hop2, - AmtToForward: payAmt, + ChannelID: 2, + PubKeyBytes: hop2, + AmtToForward: payAmt, + LegacyPayload: true, }, } diff --git a/rpcserver.go b/rpcserver.go index 19f713c3..5cb959c5 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/watchtower" "github.com/btcsuite/btcd/blockchain" @@ -2926,6 +2927,8 @@ type rpcPaymentIntent struct { outgoingChannelID *uint64 payReq []byte + destTLV []tlv.Record + route *route.Route } @@ -2968,6 +2971,16 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error payIntent.cltvLimit = &rpcPayReq.CltvLimit } + if len(rpcPayReq.DestTlv) != 0 { + var err error + payIntent.destTLV, err = tlv.MapToRecords( + rpcPayReq.DestTlv, + ) + if err != nil { + return payIntent, err + } + } + // If the payment request field isn't blank, then the details of the // invoice are encoded entirely within the encoded payReq. So we'll // attempt to decode it, populating the payment accordingly. @@ -3123,6 +3136,7 @@ func (r *rpcServer) dispatchPaymentIntent( OutgoingChannelID: payIntent.outgoingChannelID, PaymentRequest: payIntent.payReq, PayAttemptTimeout: routing.DefaultPayAttemptTimeout, + FinalDestRecords: payIntent.destTLV, } preImage, route, routerErr = r.server.chanRouter.SendPayment( @@ -3293,10 +3307,16 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error { return } - marshalledRouted := r.routerBackend. - MarshallRoute(resp.Route) + backend := r.routerBackend + marshalledRouted, err := backend.MarshallRoute( + resp.Route, + ) + if err != nil { + errChan <- err + return + } - err := stream.send(&lnrpc.SendResponse{ + err = stream.send(&lnrpc.SendResponse{ PaymentHash: payIntent.rHash[:], PaymentPreimage: resp.Preimage[:], PaymentRoute: marshalledRouted, @@ -3375,10 +3395,15 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context, }, nil } + rpcRoute, err := r.routerBackend.MarshallRoute(resp.Route) + if err != nil { + return nil, err + } + return &lnrpc.SendResponse{ PaymentHash: payIntent.rHash[:], PaymentPreimage: resp.Preimage[:], - PaymentRoute: r.routerBackend.MarshallRoute(resp.Route), + PaymentRoute: rpcRoute, }, nil } diff --git a/server.go b/server.go index 7687d095..7a37e769 100644 --- a/server.go +++ b/server.go @@ -313,6 +313,12 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, globalFeatures := lnwire.NewRawFeatureVector() + // Only if we're not being forced to use the legacy onion format, will + // we signal our knowledge of the new TLV onion format. + if !cfg.LegacyProtocol.LegacyOnion() { + globalFeatures.Set(lnwire.TLVOnionPayloadOptional) + } + var serializedPubKey [33]byte copy(serializedPubKey[:], privKey.PubKey().SerializeCompressed()) diff --git a/tlv/onion_types.go b/tlv/onion_types.go new file mode 100644 index 00000000..65d5b42c --- /dev/null +++ b/tlv/onion_types.go @@ -0,0 +1,15 @@ +package tlv + +const ( + // AmtOnionType is the type used in the onion to refrence the amount to + // send to the next hop. + AmtOnionType Type = 2 + + // LockTimeTLV is the type used in the onion to refenernce the CLTV + // value that should be used for the next hop's HTLC. + LockTimeOnionType Type = 4 + + // NextHopOnionType is the type used in the onion to reference the ID + // of the next hop. + NextHopOnionType Type = 6 +) diff --git a/tlv/record.go b/tlv/record.go index ae21c050..610ab6c1 100644 --- a/tlv/record.go +++ b/tlv/record.go @@ -1,7 +1,10 @@ package tlv import ( + "bytes" + "fmt" "io" + "sort" "github.com/btcsuite/btcd/btcec" ) @@ -57,6 +60,20 @@ func (f *Record) Size() uint64 { return f.sizeFunc() } +// Type returns the type of the underlying TLV record. +func (f *Record) Type() Type { + return f.typ +} + +// Encode writes out the TLV record to the passed writer. This is useful when a +// caller wants to obtain the raw encoding of a *single* TLV record, outside +// the context of the Stream struct. +func (f *Record) Encode(w io.Writer) error { + var b [8]byte + + return f.encoder(w, f.value, &b) +} + // MakePrimitiveRecord creates a record for common types. func MakePrimitiveRecord(typ Type, val interface{}) Record { var ( @@ -112,7 +129,7 @@ func MakePrimitiveRecord(typ Type, val interface{}) Record { decoder = DVarBytes default: - panic("unknown primitive type") + panic(fmt.Sprintf("unknown primitive type: %T", val)) } return Record{ @@ -151,3 +168,63 @@ func MakeDynamicRecord(typ Type, val interface{}, sizeFunc SizeFunc, decoder: decoder, } } + +// RecordsToMap encodes a series of TLV records as raw key-value pairs in the +// form of a map. +func RecordsToMap(records []Record) (map[uint64][]byte, error) { + tlvMap := make(map[uint64][]byte, len(records)) + + for _, record := range records { + var b bytes.Buffer + if err := record.Encode(&b); err != nil { + return nil, err + } + + tlvMap[uint64(record.Type())] = b.Bytes() + } + + return tlvMap, nil +} + +// StubEncoder is a factory function that makes a stub tlv.Encoder out of a raw +// value. We can use this to make a record that can be encoded when we don't +// actually know it's true underlying value, and only it serialization. +func StubEncoder(v []byte) Encoder { + return func(w io.Writer, val interface{}, buf *[8]byte) error { + _, err := w.Write(v) + return err + } +} + +// MapToRecords encodes the passed TLV map as a series of regular tlv.Record +// instances. The resulting set of records will be returned in sorted order by +// their type. +func MapToRecords(tlvMap map[uint64][]byte) ([]Record, error) { + records := make([]Record, 0, len(tlvMap)) + for k, v := range tlvMap { + // We don't pass in a decoder here since we don't actually know + // the type, and only expect this Record to be used for display + // and encoding purposes. + record := MakeStaticRecord( + Type(k), nil, uint64(len(v)), StubEncoder(v), nil, + ) + + records = append(records, record) + } + + SortRecords(records) + + return records, nil +} + +// SortRecords is a helper function that will sort a slice of records in place +// according to their type. +func SortRecords(records []Record) { + if len(records) == 0 { + return + } + + sort.Slice(records, func(i, j int) bool { + return records[i].Type() < records[j].Type() + }) +} diff --git a/tlv/record_test.go b/tlv/record_test.go new file mode 100644 index 00000000..02d2e893 --- /dev/null +++ b/tlv/record_test.go @@ -0,0 +1,149 @@ +package tlv + +import ( + "bytes" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// TestSortRecords tests that SortRecords is able to properly sort records in +// place. +func TestSortRecords(t *testing.T) { + t.Parallel() + + testCases := []struct { + preSort []Record + postSort []Record + }{ + // An empty slice requires no sorting. + { + preSort: []Record{}, + postSort: []Record{}, + }, + + // An already sorted slice should be passed through. + { + preSort: []Record{ + MakeStaticRecord(1, nil, 0, nil, nil), + MakeStaticRecord(2, nil, 0, nil, nil), + MakeStaticRecord(3, nil, 0, nil, nil), + }, + postSort: []Record{ + MakeStaticRecord(1, nil, 0, nil, nil), + MakeStaticRecord(2, nil, 0, nil, nil), + MakeStaticRecord(3, nil, 0, nil, nil), + }, + }, + + // We should be able to sort a randomized set of records . + { + preSort: []Record{ + MakeStaticRecord(9, nil, 0, nil, nil), + MakeStaticRecord(43, nil, 0, nil, nil), + MakeStaticRecord(1, nil, 0, nil, nil), + MakeStaticRecord(0, nil, 0, nil, nil), + }, + postSort: []Record{ + MakeStaticRecord(0, nil, 0, nil, nil), + MakeStaticRecord(1, nil, 0, nil, nil), + MakeStaticRecord(9, nil, 0, nil, nil), + MakeStaticRecord(43, nil, 0, nil, nil), + }, + }, + } + + for i, testCase := range testCases { + SortRecords(testCase.preSort) + + if !reflect.DeepEqual(testCase.preSort, testCase.postSort) { + t.Fatalf("#%v: wrong order: expected %v, got %v", i, + spew.Sdump(testCase.preSort), + spew.Sdump(testCase.postSort)) + } + } +} + +// TestRecordMapTransformation tests that we're able to properly morph a set of +// records into a map using TlvRecordsToMap, then the other way around using +// the MapToTlvRecords method. +func TestRecordMapTransformation(t *testing.T) { + t.Parallel() + + tlvBytes := []byte{1, 2, 3, 4} + encoder := StubEncoder(tlvBytes) + + testCases := []struct { + records []Record + + tlvMap map[uint64][]byte + }{ + // An empty set of records should yield an empty map, and the other + // way around. + { + records: []Record{}, + tlvMap: map[uint64][]byte{}, + }, + + // We should be able to transform this set of records, then obtain + // the records back in the same order. + { + records: []Record{ + MakeStaticRecord(1, nil, 4, encoder, nil), + MakeStaticRecord(2, nil, 4, encoder, nil), + MakeStaticRecord(3, nil, 4, encoder, nil), + }, + tlvMap: map[uint64][]byte{ + 1: tlvBytes, + 2: tlvBytes, + 3: tlvBytes, + }, + }, + } + + for i, testCase := range testCases { + mappedRecords, err := RecordsToMap(testCase.records) + if err != nil { + t.Fatalf("#%v: unable to map records: %v", i, err) + } + + if !reflect.DeepEqual(mappedRecords, testCase.tlvMap) { + t.Fatalf("#%v: incorrect record map: expected %v, got %v", + i, spew.Sdump(testCase.tlvMap), + spew.Sdump(mappedRecords)) + } + + unmappedRecords, err := MapToRecords(mappedRecords) + if err != nil { + t.Fatalf("#%v: unable to unmap records: %v", i, err) + } + + for i := 0; i < len(testCase.records); i++ { + if unmappedRecords[i].Type() != testCase.records[i].Type() { + t.Fatalf("#%v: wrong type: expected %v, got %v", + i, unmappedRecords[i].Type(), + testCase.records[i].Type()) + } + + var b bytes.Buffer + if err := unmappedRecords[i].Encode(&b); err != nil { + t.Fatalf("#%v: unable to encode record: %v", + i, err) + } + + if !bytes.Equal(b.Bytes(), tlvBytes) { + t.Fatalf("#%v: wrong raw record: "+ + "expected %x, got %x", + i, tlvBytes, b.Bytes()) + } + + if unmappedRecords[i].Size() != testCase.records[0].Size() { + t.Fatalf("#%v: wrong size: expected %v, "+ + "got %v", i, + unmappedRecords[i].Size(), + testCase.records[i].Size()) + } + } + } +}