Merge pull request #4752 from Roasbeef/require-payment-addr

features+invoices: force MPP payload inclusion for non-keysend payments
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2020-11-25 16:43:18 -08:00
commit 8921f81d97
20 files changed
+1434 -1051

No files matched your search

+1 -1
View File
@@ -33,7 +33,7 @@ var defaultSetDesc = setDesc{
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.PaymentAddrOptional: {
lnwire.PaymentAddrRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
SetInvoice: {}, // 9
+175
View File
@@ -1190,3 +1190,178 @@ func TestOldInvoiceRemovalOnStart(t *testing.T) {
// registry start.
require.Equal(t, expected, response.Invoices)
}
// TestSettleInvoicePaymentAddrRequired tests that if an incoming payment has
// an invoice that requires the payment addr bit to be set, and the incoming
// payment doesn't include an mpp payload, then the payment is rejected.
func TestSettleInvoicePaymentAddrRequired(t *testing.T) {
t.Parallel()
ctx := newTestContext(t)
defer ctx.cleanup()
allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0)
assert.Nil(t, err)
defer allSubscriptions.Cancel()
// Subscribe to the not yet existing invoice.
subscription, err := ctx.registry.SubscribeSingleInvoice(
testInvoicePaymentHash,
)
require.NoError(t, err)
defer subscription.Cancel()
require.Equal(
t, subscription.invoiceRef.PayHash(), testInvoicePaymentHash,
)
// Add the invoice, which requires the MPP payload to always be
// included due to its set of feature bits.
addIdx, err := ctx.registry.AddInvoice(
testPayAddrReqInvoice, testInvoicePaymentHash,
)
require.NoError(t, err)
require.Equal(t, int(addIdx), 1)
// We expect the open state to be sent to the single invoice subscriber.
select {
case update := <-subscription.Updates:
if update.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
update.State)
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
// We expect a new invoice notification to be sent out.
select {
case newInvoice := <-allSubscriptions.NewInvoices:
if newInvoice.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
newInvoice.State)
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
hodlChan := make(chan interface{}, 1)
// Now try to settle the invoice, the testPayload doesn't have any mpp
// information, so it should be forced to the updateLegacy path then
// fail as a required feature bit exists.
resolution, err := ctx.registry.NotifyExitHopHtlc(
testInvoicePaymentHash, testInvoice.Terms.Value,
uint32(testCurrentHeight)+testInvoiceCltvDelta-1,
testCurrentHeight, getCircuitKey(10), hodlChan, testPayload,
)
require.NoError(t, err)
failResolution, ok := resolution.(*HtlcFailResolution)
if !ok {
t.Fatalf("expected fail resolution, got: %T",
resolution)
}
require.Equal(t, failResolution.AcceptHeight, testCurrentHeight)
require.Equal(t, failResolution.Outcome, ResultAddressMismatch)
}
// TestSettleInvoicePaymentAddrRequiredOptionalGrace tests that if an invoice
// in the database has an optional payment addr required bit set, then we'll
// still allow it to be paid by an incoming HTLC that doesn't include the MPP
// payload. This ensures we don't break payment for any invoices in the wild.
func TestSettleInvoicePaymentAddrRequiredOptionalGrace(t *testing.T) {
t.Parallel()
ctx := newTestContext(t)
defer ctx.cleanup()
allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0)
assert.Nil(t, err)
defer allSubscriptions.Cancel()
// Subscribe to the not yet existing invoice.
subscription, err := ctx.registry.SubscribeSingleInvoice(
testInvoicePaymentHash,
)
require.NoError(t, err)
defer subscription.Cancel()
require.Equal(
t, subscription.invoiceRef.PayHash(), testInvoicePaymentHash,
)
// Add the invoice, which requires the MPP payload to always be
// included due to its set of feature bits.
addIdx, err := ctx.registry.AddInvoice(
testPayAddrOptionalInvoice, testInvoicePaymentHash,
)
require.NoError(t, err)
require.Equal(t, int(addIdx), 1)
// We expect the open state to be sent to the single invoice
// subscriber.
select {
case update := <-subscription.Updates:
if update.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
update.State)
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
// We expect a new invoice notification to be sent out.
select {
case newInvoice := <-allSubscriptions.NewInvoices:
if newInvoice.State != channeldb.ContractOpen {
t.Fatalf("expected state ContractOpen, but got %v",
newInvoice.State)
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
// We'll now attempt to settle the invoice as normal, this should work
// no problem as we should allow these existing invoices to be settled.
hodlChan := make(chan interface{}, 1)
resolution, err := ctx.registry.NotifyExitHopHtlc(
testInvoicePaymentHash, testInvoiceAmt,
testHtlcExpiry, testCurrentHeight,
getCircuitKey(10), hodlChan, testPayload,
)
require.NoError(t, err)
settleResolution, ok := resolution.(*HtlcSettleResolution)
if !ok {
t.Fatalf("expected settle resolution, got: %T",
resolution)
}
require.Equal(t, settleResolution.Outcome, ResultSettled)
// We expect the settled state to be sent to the single invoice
// subscriber.
select {
case update := <-subscription.Updates:
if update.State != channeldb.ContractSettled {
t.Fatalf("expected state ContractOpen, but got %v",
update.State)
}
if update.AmtPaid != testInvoice.Terms.Value {
t.Fatal("invoice AmtPaid incorrect")
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
// We expect a settled notification to be sent out.
select {
case settledInvoice := <-allSubscriptions.SettledInvoices:
if settledInvoice.State != channeldb.ContractSettled {
t.Fatalf("expected state ContractOpen, but got %v",
settledInvoice.State)
}
case <-time.After(testTimeout):
t.Fatal("no update received")
}
}
+26
View File
@@ -100,6 +100,32 @@ var (
CreationDate: testInvoiceCreationDate,
}
testPayAddrReqInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: &testInvoicePreimage,
Value: testInvoiceAmt,
Expiry: time.Hour,
Features: lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(lnwire.PaymentAddrRequired),
lnwire.Features,
),
},
CreationDate: testInvoiceCreationDate,
}
testPayAddrOptionalInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: &testInvoicePreimage,
Value: testInvoiceAmt,
Expiry: time.Hour,
Features: lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(lnwire.PaymentAddrOptional),
lnwire.Features,
),
},
CreationDate: testInvoiceCreationDate,
}
testHodlInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
Value: testInvoiceAmt,
+21 -2
View File
@@ -90,6 +90,9 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
}
}
// If no MPP payload was provided, then we expect this to be a keysend,
// or a payment to an invoice created before we started to require the
// MPP payload.
if ctx.mpp == nil {
return updateLegacy(ctx, inv)
}
@@ -206,6 +209,10 @@ func updateMpp(ctx *invoiceUpdateCtx,
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
// settlement logic for legacy payments.
//
// NOTE: This function is only kept in place in order to be able to handle key
// send payments and any invoices we created in the past that are valid and
// still had the optional mpp bit set.
func updateLegacy(ctx *invoiceUpdateCtx,
inv *channeldb.Invoice) (*channeldb.InvoiceUpdateDesc, HtlcResolution, error) {
@@ -223,8 +230,20 @@ func updateLegacy(ctx *invoiceUpdateCtx,
return nil, ctx.failRes(ResultAmountTooLow), nil
}
// TODO(joostjager): Check invoice mpp required feature
// bit when feature becomes mandatory.
// If the invoice had the required feature bit set at this point, then
// if we're in this method it means that the remote party didn't supply
// the expected payload. However if this is a keysend payment, then
// we'll permit it to pass.
_, isKeySend := ctx.customRecords[record.KeySendType]
invoiceFeatures := inv.Terms.Features
paymentAddrRequired := invoiceFeatures.RequiresFeature(
lnwire.PaymentAddrRequired,
)
if !isKeySend && paymentAddrRequired {
log.Warnf("Payment to pay_hash=%v doesn't include MPP "+
"payload, rejecting", ctx.hash)
return nil, ctx.failRes(ResultAddressMismatch), nil
}
// Don't allow settling the invoice with an old style
// htlc if we are already in the process of gathering an
+5
View File
@@ -427,6 +427,11 @@
"type": "boolean",
"format": "boolean",
"description": "Indicates if this invoice was a spontaneous payment that arrived via keysend\n[EXPERIMENTAL]."
},
"payment_addr": {
"type": "string",
"format": "byte",
"description": "The payment address of this invoice. This value will be used in MPP\npayments, and also for newer invoies that always require the MPP paylaod\nfor added end-to-end security."
}
}
},
+1
View File
@@ -148,6 +148,7 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
Htlcs: rpcHtlcs,
Features: CreateRPCFeatures(invoice.Terms.Features),
IsKeysend: len(invoice.PaymentRequest) == 0,
PaymentAddr: invoice.Terms.PaymentAddr[:],
}
if preimage != nil {
+174 -164
View File
@@ -1143,7 +1143,9 @@ type BuildRouteRequest struct {
//
//A list of hops that defines the route. This does not include the source hop
//pubkey.
HopPubkeys [][]byte `protobuf:"bytes,4,rep,name=hop_pubkeys,json=hopPubkeys,proto3" json:"hop_pubkeys,omitempty"`
HopPubkeys [][]byte `protobuf:"bytes,4,rep,name=hop_pubkeys,json=hopPubkeys,proto3" json:"hop_pubkeys,omitempty"`
// An optional payment addr to be included within the last hop of the route.
PaymentAddr []byte `protobuf:"bytes,5,opt,name=payment_addr,json=paymentAddr,proto3" json:"payment_addr,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -1202,6 +1204,13 @@ func (m *BuildRouteRequest) GetHopPubkeys() [][]byte {
return nil
}
func (m *BuildRouteRequest) GetPaymentAddr() []byte {
if m != nil {
return m.PaymentAddr
}
return nil
}
type BuildRouteResponse struct {
//
//Fully specified route that can be used to execute the payment.
@@ -2028,170 +2037,171 @@ func init() {
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
var fileDescriptor_7a0613f69d37b0a5 = []byte{
// 2601 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4d, 0x73, 0xdb, 0xc6,
0xf9, 0x0f, 0x48, 0x88, 0x22, 0x1f, 0xbe, 0x08, 0x5a, 0x29, 0x16, 0xff, 0x94, 0x9d, 0x30, 0x4c,
0x62, 0x73, 0xfc, 0x4f, 0x64, 0x45, 0xed, 0xb4, 0x69, 0xf3, 0xd2, 0x50, 0x24, 0x64, 0xc1, 0xa6,
0x48, 0x66, 0x49, 0x39, 0x49, 0x73, 0xd8, 0x42, 0xe4, 0x52, 0x44, 0x05, 0x02, 0x2c, 0xb0, 0xb4,
0xa3, 0x63, 0x6f, 0x9d, 0x7e, 0x91, 0xde, 0xfa, 0x09, 0x3a, 0xd3, 0x1e, 0xfa, 0x3d, 0x7a, 0xed,
0xbd, 0x33, 0x3d, 0x77, 0xf6, 0x05, 0x20, 0x20, 0x51, 0x76, 0x3b, 0xed, 0xc5, 0x26, 0x7e, 0xcf,
0x6f, 0x9f, 0x7d, 0x76, 0x9f, 0xb7, 0xdd, 0x15, 0xdc, 0x0b, 0xfc, 0x25, 0xa3, 0x41, 0xb0, 0x18,
0x3f, 0x91, 0xbf, 0x0e, 0x16, 0x81, 0xcf, 0x7c, 0x54, 0x88, 0xf1, 0x5a, 0x21, 0x58, 0x8c, 0x25,
0xda, 0xf8, 0x47, 0x0e, 0xd0, 0x90, 0x7a, 0x93, 0x81, 0x7d, 0x3d, 0xa7, 0x1e, 0xc3, 0xf4, 0x37,
0x4b, 0x1a, 0x32, 0x84, 0x40, 0x9f, 0xd0, 0x90, 0x55, 0xb5, 0xba, 0xd6, 0x2c, 0x61, 0xf1, 0x1b,
0x19, 0x90, 0xb5, 0xe7, 0xac, 0x9a, 0xa9, 0x6b, 0xcd, 0x2c, 0xe6, 0x3f, 0xd1, 0xff, 0x41, 0xde,
0x9e, 0x33, 0x32, 0x0f, 0x6d, 0x56, 0x2d, 0x09, 0x78, 0xd3, 0x9e, 0xb3, 0xb3, 0xd0, 0x66, 0xe8,
0x3d, 0x28, 0x2d, 0xa4, 0x4a, 0x32, 0xb3, 0xc3, 0x59, 0x35, 0x2b, 0x14, 0x15, 0x15, 0x76, 0x6a,
0x87, 0x33, 0xd4, 0x04, 0x63, 0xea, 0x78, 0xb6, 0x4b, 0xc6, 0x2e, 0x7b, 0x49, 0x26, 0xd4, 0x65,
0x76, 0x55, 0xaf, 0x6b, 0xcd, 0x0d, 0x5c, 0x11, 0x78, 0xdb, 0x65, 0x2f, 0x3b, 0x1c, 0x45, 0x8f,
0x60, 0x2b, 0x52, 0x16, 0x48, 0x03, 0xab, 0x1b, 0x75, 0xad, 0x59, 0xc0, 0x95, 0x45, 0xda, 0xec,
0x47, 0xb0, 0xc5, 0x9c, 0x39, 0xf5, 0x97, 0x8c, 0x84, 0x74, 0xec, 0x7b, 0x93, 0xb0, 0x9a, 0x93,
0x1a, 0x15, 0x3c, 0x94, 0x28, 0x6a, 0x40, 0x79, 0x4a, 0x29, 0x71, 0x9d, 0xb9, 0xc3, 0x08, 0x37,
0x7f, 0x53, 0x98, 0x5f, 0x9c, 0x52, 0xda, 0xe5, 0xd8, 0xd0, 0x66, 0xe8, 0x03, 0xa8, 0xac, 0x38,
0x62, 0x8d, 0x65, 0x41, 0x2a, 0x45, 0x24, 0xb1, 0xd0, 0x03, 0x30, 0xfc, 0x25, 0xbb, 0xf4, 0x1d,
0xef, 0x92, 0x8c, 0x67, 0xb6, 0x47, 0x9c, 0x49, 0x35, 0x5f, 0xd7, 0x9a, 0xfa, 0xb1, 0x5e, 0xd5,
0x0e, 0x35, 0x5c, 0x89, 0xa4, 0xed, 0x99, 0xed, 0x59, 0x13, 0xf4, 0x18, 0xb6, 0x6f, 0xf2, 0xc3,
0xea, 0x4e, 0x3d, 0xdb, 0xd4, 0xf1, 0x56, 0x9a, 0x1a, 0xa2, 0x87, 0xb0, 0xe5, 0xda, 0x21, 0x23,
0x33, 0x7f, 0x41, 0x16, 0xcb, 0x8b, 0x2b, 0x7a, 0x5d, 0xad, 0x88, 0x7d, 0x2c, 0x73, 0xf8, 0xd4,
0x5f, 0x0c, 0x04, 0x88, 0x1e, 0x00, 0x88, 0x3d, 0x14, 0xa6, 0x56, 0x0b, 0x62, 0xc5, 0x05, 0x8e,
0x08, 0x33, 0xd1, 0x27, 0x50, 0x14, 0xbe, 0x27, 0x33, 0xc7, 0x63, 0x61, 0x15, 0xea, 0xd9, 0x66,
0xf1, 0xc8, 0x38, 0x70, 0x3d, 0x1e, 0x06, 0x98, 0x4b, 0x4e, 0x1d, 0x8f, 0x61, 0x08, 0xa2, 0x9f,
0x21, 0x9a, 0xc0, 0x0e, 0xf7, 0x39, 0x19, 0x2f, 0x43, 0xe6, 0xcf, 0x49, 0x40, 0xc7, 0x7e, 0x30,
0x09, 0xab, 0x45, 0x31, 0xf4, 0xc7, 0x07, 0x71, 0x28, 0x1d, 0xdc, 0x8e, 0x9d, 0x83, 0x0e, 0x0d,
0x59, 0x5b, 0x8c, 0xc3, 0x72, 0x98, 0xe9, 0xb1, 0xe0, 0x1a, 0x6f, 0x4f, 0x6e, 0xe2, 0xe8, 0x23,
0x40, 0xb6, 0xeb, 0xfa, 0xaf, 0x48, 0x48, 0xdd, 0x29, 0x51, 0xbe, 0xac, 0x6e, 0xd5, 0xb5, 0x66,
0x1e, 0x1b, 0x42, 0x32, 0xa4, 0xee, 0x54, 0xa9, 0x47, 0x3f, 0x81, 0xb2, 0xb0, 0x69, 0x4a, 0x6d,
0xb6, 0x0c, 0x68, 0x58, 0x35, 0xea, 0xd9, 0x66, 0xe5, 0x68, 0x5b, 0x2d, 0xe4, 0x44, 0xc2, 0xc7,
0x0e, 0xc3, 0x25, 0xce, 0x53, 0xdf, 0x21, 0xda, 0x87, 0xc2, 0xdc, 0xfe, 0x81, 0x2c, 0xec, 0x80,
0x85, 0xd5, 0xed, 0xba, 0xd6, 0x2c, 0xe3, 0xfc, 0xdc, 0xfe, 0x61, 0xc0, 0xbf, 0xd1, 0x01, 0xec,
0x78, 0x3e, 0x71, 0xbc, 0xa9, 0xeb, 0x5c, 0xce, 0x18, 0x59, 0x2e, 0x26, 0x36, 0xa3, 0x61, 0x15,
0x09, 0x1b, 0xb6, 0x3d, 0xdf, 0x52, 0x92, 0x73, 0x29, 0xa8, 0x75, 0xe0, 0xde, 0xfa, 0xf5, 0xf1,
0xf4, 0xe0, 0x0e, 0xe2, 0x19, 0xa3, 0x63, 0xfe, 0x13, 0xed, 0xc2, 0xc6, 0x4b, 0xdb, 0x5d, 0x52,
0x91, 0x32, 0x25, 0x2c, 0x3f, 0x7e, 0x9e, 0xf9, 0x54, 0x6b, 0xcc, 0x60, 0x67, 0x14, 0xd8, 0xe3,
0xab, 0x1b, 0x59, 0x77, 0x33, 0x69, 0xb4, 0xdb, 0x49, 0x73, 0x87, 0xbd, 0x99, 0x3b, 0xec, 0x6d,
0x7c, 0x09, 0x5b, 0xc2, 0xc3, 0x27, 0x94, 0xbe, 0x2e, 0xb7, 0xf7, 0x80, 0x67, 0xae, 0xc8, 0x04,
0x99, 0xdf, 0x39, 0x7b, 0xce, 0x93, 0xa0, 0x31, 0x01, 0x63, 0x35, 0x3e, 0x5c, 0xf8, 0x5e, 0x48,
0x79, 0xe2, 0xf2, 0x00, 0xe0, 0x11, 0xcc, 0x13, 0x44, 0xa4, 0x86, 0x26, 0x46, 0x55, 0x14, 0x7e,
0x42, 0xa9, 0x48, 0x8e, 0x87, 0x32, 0x1f, 0x89, 0xeb, 0x8f, 0xaf, 0x78, 0x86, 0xdb, 0xd7, 0x4a,
0x7d, 0x99, 0xc3, 0x5d, 0x7f, 0x7c, 0xd5, 0xe1, 0x60, 0xe3, 0x7b, 0x59, 0x84, 0x46, 0xbe, 0x98,
0xeb, 0x3f, 0xd8, 0x8e, 0x06, 0x6c, 0x88, 0x58, 0x14, 0x6a, 0x8b, 0x47, 0xa5, 0x64, 0x50, 0x63,
0x29, 0x6a, 0x7c, 0x0f, 0x3b, 0x29, 0xe5, 0x6a, 0x15, 0x35, 0xc8, 0x2f, 0x02, 0xea, 0xcc, 0xed,
0x4b, 0xaa, 0x34, 0xc7, 0xdf, 0xa8, 0x09, 0x9b, 0x53, 0xdb, 0x71, 0x97, 0x41, 0xa4, 0xb8, 0x12,
0x05, 0x99, 0x44, 0x71, 0x24, 0x6e, 0xdc, 0x87, 0x1a, 0xa6, 0x21, 0x65, 0x67, 0x4e, 0x18, 0x3a,
0xbe, 0xd7, 0xf6, 0x3d, 0x16, 0xf8, 0xae, 0x5a, 0x41, 0xe3, 0x01, 0xec, 0xaf, 0x95, 0x4a, 0x13,
0xf8, 0xe0, 0xaf, 0x97, 0x34, 0xb8, 0x5e, 0x3f, 0xf8, 0x6b, 0xd8, 0x5f, 0x2b, 0x55, 0xf6, 0x7f,
0x04, 0x1b, 0x0b, 0xdb, 0x09, 0xb8, 0xef, 0x79, 0x52, 0xde, 0x4b, 0x24, 0xe5, 0xc0, 0x76, 0x82,
0x53, 0x27, 0x64, 0x7e, 0x70, 0x8d, 0x25, 0xe9, 0x99, 0x9e, 0xd7, 0x8c, 0x4c, 0xe3, 0xf7, 0x1a,
0x14, 0x13, 0x42, 0x9e, 0x1a, 0x9e, 0x3f, 0xa1, 0x64, 0x1a, 0xf8, 0xf3, 0x68, 0x13, 0x38, 0x70,
0x12, 0xf8, 0x73, 0x1e, 0x13, 0x42, 0xc8, 0x7c, 0x15, 0xc0, 0x39, 0xfe, 0x39, 0xf2, 0xd1, 0xc7,
0xb0, 0x39, 0x93, 0x0a, 0x44, 0xd9, 0x2c, 0x1e, 0xed, 0xdc, 0x98, 0xbb, 0x63, 0x33, 0x1b, 0x47,
0x9c, 0x67, 0x7a, 0x3e, 0x6b, 0xe8, 0xcf, 0xf4, 0xbc, 0x6e, 0x6c, 0x3c, 0xd3, 0xf3, 0x1b, 0x46,
0xee, 0x99, 0x9e, 0xcf, 0x19, 0x9b, 0x8d, 0xbf, 0x6b, 0x90, 0x8f, 0xd8, 0xdc, 0x12, 0xbe, 0xa5,
0x84, 0xc7, 0x85, 0x0a, 0xa6, 0x3c, 0x07, 0x46, 0xce, 0x9c, 0xa2, 0x3a, 0x94, 0x84, 0x30, 0x1d,
0xa2, 0xc0, 0xb1, 0x96, 0x08, 0x53, 0x51, 0xcf, 0x23, 0x86, 0x88, 0x47, 0x5d, 0xd5, 0x73, 0x49,
0x89, 0x5a, 0x52, 0xb8, 0x1c, 0x8f, 0x69, 0x18, 0xca, 0x59, 0x36, 0x24, 0x45, 0x61, 0x62, 0xa2,
0x87, 0xb0, 0x15, 0x51, 0xa2, 0xb9, 0x72, 0x32, 0x5e, 0x15, 0xac, 0xa6, 0x6b, 0x82, 0x91, 0xe4,
0xcd, 0x57, 0x1d, 0xa4, 0xb2, 0x22, 0xf2, 0x49, 0xe5, 0xe2, 0x1b, 0xbf, 0x86, 0x3d, 0xe1, 0xca,
0x41, 0xe0, 0x5f, 0xd8, 0x17, 0x8e, 0xeb, 0xb0, 0xeb, 0x28, 0xc8, 0xf9, 0xc2, 0x03, 0x7f, 0x4e,
0xf8, 0xde, 0x46, 0x2e, 0xe0, 0x40, 0xcf, 0x9f, 0x50, 0xee, 0x02, 0xe6, 0x4b, 0x91, 0x72, 0x01,
0xf3, 0x85, 0x20, 0xd9, 0x79, 0xb3, 0xa9, 0xce, 0xdb, 0xb8, 0x82, 0xea, 0xed, 0xb9, 0x54, 0xcc,
0xd4, 0xa1, 0xb8, 0x58, 0xc1, 0x62, 0x3a, 0x0d, 0x27, 0xa1, 0xa4, 0x6f, 0x33, 0x6f, 0xf6, 0x6d,
0xe3, 0x0f, 0x1a, 0x6c, 0x1f, 0x2f, 0x1d, 0x77, 0x92, 0x4a, 0xdc, 0xa4, 0x75, 0x5a, 0xfa, 0x5c,
// 2615 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4b, 0x77, 0xdb, 0xc6,
0xf5, 0x0f, 0x48, 0x8a, 0x22, 0x2f, 0x1f, 0x82, 0x46, 0x8a, 0xc5, 0x3f, 0x65, 0x27, 0x0c, 0x93,
0xd8, 0x3c, 0xfe, 0x27, 0xb2, 0xa2, 0xf6, 0xb4, 0x69, 0xf3, 0x68, 0x28, 0x12, 0xb2, 0x60, 0x53,
0x24, 0x33, 0xa4, 0x9c, 0xa4, 0x59, 0x4c, 0x21, 0x62, 0x28, 0xa2, 0x02, 0x01, 0x16, 0x18, 0xda,
0xd1, 0xb2, 0xbb, 0x9e, 0x7e, 0x98, 0x7e, 0x82, 0x9e, 0xd3, 0x2e, 0xba, 0xea, 0x97, 0xe8, 0xb6,
0xfb, 0x9e, 0xd3, 0x75, 0xcf, 0x3c, 0x00, 0x02, 0x12, 0x65, 0xb7, 0xa7, 0xdd, 0xd8, 0xc4, 0xef,
0xde, 0xb9, 0x73, 0xef, 0xdc, 0xe7, 0x8c, 0xe0, 0x5e, 0xe0, 0x2f, 0x19, 0x0d, 0x82, 0xc5, 0xe4,
0x89, 0xfc, 0x75, 0xb0, 0x08, 0x7c, 0xe6, 0xa3, 0x62, 0x8c, 0xd7, 0x8b, 0xc1, 0x62, 0x22, 0xd1,
0xe6, 0x3f, 0xf2, 0x80, 0x46, 0xd4, 0xb3, 0x87, 0xd6, 0xf5, 0x9c, 0x7a, 0x0c, 0xd3, 0xdf, 0x2c,
0x69, 0xc8, 0x10, 0x82, 0x9c, 0x4d, 0x43, 0x56, 0xd3, 0x1a, 0x5a, 0xab, 0x8c, 0xc5, 0x6f, 0xa4,
0x43, 0xd6, 0x9a, 0xb3, 0x5a, 0xa6, 0xa1, 0xb5, 0xb2, 0x98, 0xff, 0x44, 0xff, 0x07, 0x05, 0x6b,
0xce, 0xc8, 0x3c, 0xb4, 0x58, 0xad, 0x2c, 0xe0, 0x4d, 0x6b, 0xce, 0xce, 0x42, 0x8b, 0xa1, 0xf7,
0xa0, 0xbc, 0x90, 0x22, 0xc9, 0xcc, 0x0a, 0x67, 0xb5, 0xac, 0x10, 0x54, 0x52, 0xd8, 0xa9, 0x15,
0xce, 0x50, 0x0b, 0xf4, 0xa9, 0xe3, 0x59, 0x2e, 0x99, 0xb8, 0xec, 0x25, 0xb1, 0xa9, 0xcb, 0xac,
0x5a, 0xae, 0xa1, 0xb5, 0x36, 0x70, 0x55, 0xe0, 0x1d, 0x97, 0xbd, 0xec, 0x72, 0x14, 0x3d, 0x82,
0xad, 0x48, 0x58, 0x20, 0x15, 0xac, 0x6d, 0x34, 0xb4, 0x56, 0x11, 0x57, 0x17, 0x69, 0xb5, 0x1f,
0xc1, 0x16, 0x73, 0xe6, 0xd4, 0x5f, 0x32, 0x12, 0xd2, 0x89, 0xef, 0xd9, 0x61, 0x2d, 0x2f, 0x25,
0x2a, 0x78, 0x24, 0x51, 0xd4, 0x84, 0xca, 0x94, 0x52, 0xe2, 0x3a, 0x73, 0x87, 0x11, 0xae, 0xfe,
0xa6, 0x50, 0xbf, 0x34, 0xa5, 0xb4, 0xc7, 0xb1, 0x91, 0xc5, 0xd0, 0x07, 0x50, 0x5d, 0xf1, 0x08,
0x1b, 0x2b, 0x82, 0xa9, 0x1c, 0x31, 0x09, 0x43, 0x0f, 0x40, 0xf7, 0x97, 0xec, 0xd2, 0x77, 0xbc,
0x4b, 0x32, 0x99, 0x59, 0x1e, 0x71, 0xec, 0x5a, 0xa1, 0xa1, 0xb5, 0x72, 0xc7, 0xb9, 0x9a, 0x76,
0xa8, 0xe1, 0x6a, 0x44, 0xed, 0xcc, 0x2c, 0xcf, 0xb4, 0xd1, 0x63, 0xd8, 0xbe, 0xc9, 0x1f, 0xd6,
0x76, 0x1a, 0xd9, 0x56, 0x0e, 0x6f, 0xa5, 0x59, 0x43, 0xf4, 0x10, 0xb6, 0x5c, 0x2b, 0x64, 0x64,
0xe6, 0x2f, 0xc8, 0x62, 0x79, 0x71, 0x45, 0xaf, 0x6b, 0x55, 0x71, 0x8e, 0x15, 0x0e, 0x9f, 0xfa,
0x8b, 0xa1, 0x00, 0xd1, 0x03, 0x00, 0x71, 0x86, 0x42, 0xd5, 0x5a, 0x51, 0x58, 0x5c, 0xe4, 0x88,
0x50, 0x13, 0x7d, 0x02, 0x25, 0xe1, 0x7b, 0x32, 0x73, 0x3c, 0x16, 0xd6, 0xa0, 0x91, 0x6d, 0x95,
0x8e, 0xf4, 0x03, 0xd7, 0xe3, 0x61, 0x80, 0x39, 0xe5, 0xd4, 0xf1, 0x18, 0x86, 0x20, 0xfa, 0x19,
0x22, 0x1b, 0x76, 0xb8, 0xcf, 0xc9, 0x64, 0x19, 0x32, 0x7f, 0x4e, 0x02, 0x3a, 0xf1, 0x03, 0x3b,
0xac, 0x95, 0xc4, 0xd2, 0x1f, 0x1f, 0xc4, 0xa1, 0x74, 0x70, 0x3b, 0x76, 0x0e, 0xba, 0x34, 0x64,
0x1d, 0xb1, 0x0e, 0xcb, 0x65, 0x86, 0xc7, 0x82, 0x6b, 0xbc, 0x6d, 0xdf, 0xc4, 0xd1, 0x47, 0x80,
0x2c, 0xd7, 0xf5, 0x5f, 0x91, 0x90, 0xba, 0x53, 0xa2, 0x7c, 0x59, 0xdb, 0x6a, 0x68, 0xad, 0x02,
0xd6, 0x05, 0x65, 0x44, 0xdd, 0xa9, 0x12, 0x8f, 0x7e, 0x02, 0x15, 0xa1, 0xd3, 0x94, 0x5a, 0x6c,
0x19, 0xd0, 0xb0, 0xa6, 0x37, 0xb2, 0xad, 0xea, 0xd1, 0xb6, 0x32, 0xe4, 0x44, 0xc2, 0xc7, 0x0e,
0xc3, 0x65, 0xce, 0xa7, 0xbe, 0x43, 0xb4, 0x0f, 0xc5, 0xb9, 0xf5, 0x03, 0x59, 0x58, 0x01, 0x0b,
0x6b, 0xdb, 0x0d, 0xad, 0x55, 0xc1, 0x85, 0xb9, 0xf5, 0xc3, 0x90, 0x7f, 0xa3, 0x03, 0xd8, 0xf1,
0x7c, 0xe2, 0x78, 0x53, 0xd7, 0xb9, 0x9c, 0x31, 0xb2, 0x5c, 0xd8, 0x16, 0xa3, 0x61, 0x0d, 0x09,
0x1d, 0xb6, 0x3d, 0xdf, 0x54, 0x94, 0x73, 0x49, 0xa8, 0x77, 0xe1, 0xde, 0x7a, 0xfb, 0x78, 0x7a,
0x70, 0x07, 0xf1, 0x8c, 0xc9, 0x61, 0xfe, 0x13, 0xed, 0xc2, 0xc6, 0x4b, 0xcb, 0x5d, 0x52, 0x91,
0x32, 0x65, 0x2c, 0x3f, 0x7e, 0x9e, 0xf9, 0x54, 0x6b, 0xce, 0x60, 0x67, 0x1c, 0x58, 0x93, 0xab,
0x1b, 0x59, 0x77, 0x33, 0x69, 0xb4, 0xdb, 0x49, 0x73, 0x87, 0xbe, 0x99, 0x3b, 0xf4, 0x6d, 0x7e,
0x09, 0x5b, 0xc2, 0xc3, 0x27, 0x94, 0xbe, 0x2e, 0xb7, 0xf7, 0x80, 0x67, 0xae, 0xc8, 0x04, 0x99,
0xdf, 0x79, 0x6b, 0xce, 0x93, 0xa0, 0x69, 0x83, 0xbe, 0x5a, 0x1f, 0x2e, 0x7c, 0x2f, 0xa4, 0x3c,
0x71, 0x79, 0x00, 0xf0, 0x08, 0xe6, 0x09, 0x22, 0x52, 0x43, 0x13, 0xab, 0xaa, 0x0a, 0x3f, 0xa1,
0x54, 0x24, 0xc7, 0x43, 0x99, 0x8f, 0xc4, 0xf5, 0x27, 0x57, 0x3c, 0xc3, 0xad, 0x6b, 0x25, 0xbe,
0xc2, 0xe1, 0x9e, 0x3f, 0xb9, 0xea, 0x72, 0xb0, 0xf9, 0xbd, 0x2c, 0x42, 0x63, 0x5f, 0xec, 0xf5,
0x1f, 0x1c, 0x47, 0x13, 0x36, 0x44, 0x2c, 0x0a, 0xb1, 0xa5, 0xa3, 0x72, 0x32, 0xa8, 0xb1, 0x24,
0x35, 0xbf, 0x87, 0x9d, 0x94, 0x70, 0x65, 0x45, 0x1d, 0x0a, 0x8b, 0x80, 0x3a, 0x73, 0xeb, 0x92,
0x2a, 0xc9, 0xf1, 0x37, 0x6a, 0xc1, 0xe6, 0xd4, 0x72, 0xdc, 0x65, 0x10, 0x09, 0xae, 0x46, 0x41,
0x26, 0x51, 0x1c, 0x91, 0x9b, 0xf7, 0xa1, 0x8e, 0x69, 0x48, 0xd9, 0x99, 0x13, 0x86, 0x8e, 0xef,
0x75, 0x7c, 0x8f, 0x05, 0xbe, 0xab, 0x2c, 0x68, 0x3e, 0x80, 0xfd, 0xb5, 0x54, 0xa9, 0x02, 0x5f,
0xfc, 0xf5, 0x92, 0x06, 0xd7, 0xeb, 0x17, 0x7f, 0x0d, 0xfb, 0x6b, 0xa9, 0x4a, 0xff, 0x8f, 0x60,
0x63, 0x61, 0x39, 0x01, 0xf7, 0x3d, 0x4f, 0xca, 0x7b, 0x89, 0xa4, 0x1c, 0x5a, 0x4e, 0x70, 0xea,
0x84, 0xcc, 0x0f, 0xae, 0xb1, 0x64, 0x7a, 0x96, 0x2b, 0x68, 0x7a, 0xa6, 0xf9, 0x7b, 0x0d, 0x4a,
0x09, 0x22, 0x4f, 0x0d, 0xcf, 0xb7, 0x29, 0x99, 0x06, 0xfe, 0x3c, 0x3a, 0x04, 0x0e, 0x9c, 0x04,
0xfe, 0x9c, 0xc7, 0x84, 0x20, 0x32, 0x5f, 0x05, 0x70, 0x9e, 0x7f, 0x8e, 0x7d, 0xf4, 0x31, 0x6c,
0xce, 0xa4, 0x00, 0x51, 0x36, 0x4b, 0x47, 0x3b, 0x37, 0xf6, 0xee, 0x5a, 0xcc, 0xc2, 0x11, 0xcf,
0xb3, 0x5c, 0x21, 0xab, 0xe7, 0x9e, 0xe5, 0x0a, 0x39, 0x7d, 0xe3, 0x59, 0xae, 0xb0, 0xa1, 0xe7,
0x9f, 0xe5, 0x0a, 0x79, 0x7d, 0xb3, 0xf9, 0x77, 0x0d, 0x0a, 0x11, 0x37, 0xd7, 0x84, 0x1f, 0x29,
0xe1, 0x71, 0xa1, 0x82, 0xa9, 0xc0, 0x81, 0xb1, 0x33, 0xa7, 0xa8, 0x01, 0x65, 0x41, 0x4c, 0x87,
0x28, 0x70, 0xac, 0x2d, 0xc2, 0x54, 0xd4, 0xf3, 0x88, 0x43, 0xc4, 0x63, 0x4e, 0xd5, 0x73, 0xc9,
0x12, 0xb5, 0xa4, 0x70, 0x39, 0x99, 0xd0, 0x30, 0x94, 0xbb, 0x6c, 0x48, 0x16, 0x85, 0x89, 0x8d,
0x1e, 0xc2, 0x56, 0xc4, 0x12, 0xed, 0x95, 0x97, 0xf1, 0xaa, 0x60, 0xb5, 0x5d, 0x0b, 0xf4, 0x24,
0xdf, 0x7c, 0xd5, 0x41, 0xaa, 0x2b, 0x46, 0xbe, 0xa9, 0x34, 0xbe, 0xf9, 0x6b, 0xd8, 0x13, 0xae,
0x1c, 0x06, 0xfe, 0x85, 0x75, 0xe1, 0xb8, 0x0e, 0xbb, 0x8e, 0x82, 0x9c, 0x1b, 0x1e, 0xf8, 0x73,
0xc2, 0xcf, 0x36, 0x72, 0x01, 0x07, 0xfa, 0xbe, 0x4d, 0xb9, 0x0b, 0x98, 0x2f, 0x49, 0xca, 0x05,
0xcc, 0x17, 0x84, 0x64, 0xe7, 0xcd, 0xa6, 0x3a, 0x6f, 0xf3, 0x0a, 0x6a, 0xb7, 0xf7, 0x52, 0x31,
0xd3, 0x80, 0xd2, 0x62, 0x05, 0x8b, 0xed, 0x34, 0x9c, 0x84, 0x92, 0xbe, 0xcd, 0xbc, 0xd9, 0xb7,
0xcd, 0xbf, 0x6a, 0xb0, 0x7d, 0xbc, 0x74, 0x5c, 0x3b, 0x95, 0xb8, 0x49, 0xed, 0xb4, 0xf4, 0x5c,
0xb0, 0xae, 0xe9, 0x67, 0xd6, 0x36, 0xfd, 0x8f, 0xd6, 0x34, 0xd6, 0xac, 0x68, 0xac, 0x99, 0x35,
0x6d, 0xf5, 0x5d, 0x28, 0xae, 0xba, 0x64, 0x58, 0xd5, 0xeb, 0xd9, 0x66, 0x09, 0xc3, 0x2c, 0x6a,
0x91, 0x61, 0xe3, 0x53, 0x40, 0x49, 0x43, 0xd5, 0x86, 0xc4, 0xf5, 0x43, 0xbb, 0xbb, 0x7e, 0xdc,
0x87, 0xda, 0x70, 0x79, 0x11, 0x8e, 0x03, 0xe7, 0x82, 0x9e, 0x32, 0x77, 0x6c, 0xbe, 0xa4, 0x1e,
0x0b, 0xa3, 0x2c, 0xfd, 0xa7, 0x0e, 0x85, 0x18, 0xe5, 0xe5, 0xd9, 0xf1, 0xc6, 0xfe, 0x3c, 0x32,
0xda, 0xa3, 0x2e, 0xb7, 0x5b, 0x36, 0x85, 0xed, 0x48, 0xd4, 0x96, 0x12, 0x6b, 0xc2, 0xf9, 0xa9,
0x45, 0x2a, 0x7e, 0x46, 0xf2, 0x93, 0x6b, 0x94, 0xfc, 0x26, 0x18, 0xb1, 0xfe, 0x19, 0x73, 0xc7,
0xf1, 0xa6, 0xe0, 0x4a, 0x84, 0x73, 0x63, 0x24, 0x33, 0xd6, 0x1c, 0x31, 0x75, 0xc9, 0x8c, 0x70,
0xc5, 0x7c, 0x0f, 0x4a, 0x3c, 0x1f, 0x42, 0x66, 0xcf, 0x17, 0xc4, 0x0b, 0x45, 0x5e, 0xe8, 0xb8,
0x18, 0x63, 0xbd, 0x10, 0x7d, 0x01, 0x40, 0xf9, 0xfa, 0x08, 0xbb, 0x5e, 0x50, 0x91, 0x12, 0x95,
0xa3, 0x77, 0x12, 0x81, 0x11, 0x6f, 0xc0, 0x81, 0xf8, 0x77, 0x74, 0xbd, 0xa0, 0xb8, 0x40, 0xa3,
0x9f, 0xe8, 0x4b, 0x28, 0x4f, 0xfd, 0xe0, 0x95, 0x1d, 0x4c, 0x88, 0x00, 0x55, 0xd9, 0xd8, 0x4b,
0x68, 0x38, 0x91, 0x72, 0x31, 0xfc, 0xf4, 0x2d, 0x5c, 0x9a, 0x26, 0xbe, 0xd1, 0x73, 0x40, 0xd1,
0x78, 0x91, 0xe5, 0x52, 0x49, 0x5e, 0x28, 0xd9, 0xbf, 0xad, 0x84, 0x17, 0xe9, 0x48, 0x91, 0x31,
0xbd, 0x81, 0xa1, 0xcf, 0xa0, 0x14, 0x52, 0xc6, 0x5c, 0xaa, 0xd4, 0x14, 0x84, 0x9a, 0x7b, 0xa9,
0x33, 0x0d, 0x17, 0x47, 0x1a, 0x8a, 0xe1, 0xea, 0x13, 0x1d, 0xc3, 0x96, 0xeb, 0x78, 0x57, 0x49,
0x33, 0x40, 0x8c, 0xaf, 0x26, 0xc6, 0x77, 0x1d, 0xef, 0x2a, 0x69, 0x43, 0xd9, 0x4d, 0x02, 0x8d,
0xcf, 0xa1, 0x10, 0xef, 0x12, 0x2a, 0xc2, 0xe6, 0x79, 0xef, 0x79, 0xaf, 0xff, 0x4d, 0xcf, 0x78,
0x0b, 0xe5, 0x41, 0x1f, 0x9a, 0xbd, 0x8e, 0xa1, 0x71, 0x18, 0x9b, 0x6d, 0xd3, 0x7a, 0x61, 0x1a,
0x19, 0xfe, 0x71, 0xd2, 0xc7, 0xdf, 0xb4, 0x70, 0xc7, 0xc8, 0x1e, 0x6f, 0xc2, 0x86, 0x98, 0xb7,
0xf1, 0x27, 0x0d, 0xf2, 0xc2, 0x83, 0xde, 0xd4, 0x47, 0xff, 0x0f, 0x71, 0x70, 0x89, 0xe2, 0xc6,
0x1b, 0xae, 0x88, 0xba, 0x32, 0x8e, 0x03, 0x66, 0xa4, 0x70, 0x4e, 0x8e, 0x43, 0x23, 0x26, 0x67,
0x24, 0x39, 0x12, 0xc4, 0xe4, 0xc7, 0x09, 0xcd, 0xa9, 0x92, 0xa3, 0xe3, 0xad, 0x48, 0x10, 0x55,
0xd8, 0xe4, 0xd9, 0x36, 0x55, 0x89, 0x13, 0x67, 0x5b, 0xc5, 0x6d, 0xfc, 0x14, 0x4a, 0x49, 0x9f,
0xa3, 0x47, 0xa0, 0x3b, 0xde, 0xd4, 0x57, 0x89, 0xb8, 0x73, 0x23, 0xb8, 0xf8, 0x22, 0xb1, 0x20,
0x34, 0x10, 0x18, 0x37, 0xfd, 0xdc, 0x28, 0x43, 0x31, 0xe1, 0xb4, 0xc6, 0xdf, 0x34, 0x28, 0xa7,
0x9c, 0xf0, 0x6f, 0x6b, 0x47, 0x5f, 0x40, 0xe9, 0x95, 0x13, 0x50, 0x92, 0x6c, 0xff, 0x95, 0xa3,
0x5a, 0xba, 0xfd, 0x47, 0xff, 0xb7, 0xfd, 0x09, 0xc5, 0x45, 0xce, 0x57, 0x00, 0xfa, 0x05, 0x54,
0xd4, 0x48, 0x32, 0xa1, 0xcc, 0x76, 0x5c, 0xb1, 0x55, 0x95, 0x54, 0x78, 0x28, 0x6e, 0x47, 0xc8,
0x71, 0x79, 0x9a, 0xfc, 0x44, 0x1f, 0xae, 0x14, 0x84, 0x2c, 0x70, 0xbc, 0x4b, 0xb1, 0x7f, 0x85,
0x98, 0x36, 0x14, 0x20, 0x6f, 0xe4, 0x65, 0x75, 0x78, 0x1c, 0x32, 0x9b, 0x2d, 0x43, 0xf4, 0x31,
0x6c, 0x84, 0xcc, 0x56, 0x95, 0xac, 0x92, 0xca, 0xad, 0x04, 0x91, 0x62, 0xc9, 0x4a, 0x9d, 0x7e,
0x32, 0xb7, 0x4e, 0x3f, 0x1b, 0xbc, 0x62, 0xc8, 0x2a, 0x5a, 0x3c, 0x42, 0x6a, 0xf1, 0xa7, 0xa3,
0x6e, 0xbb, 0xc5, 0x18, 0x9d, 0x2f, 0x18, 0x96, 0x04, 0xd5, 0xdd, 0xbe, 0x04, 0x68, 0x3b, 0xc1,
0x78, 0xe9, 0xb0, 0xe7, 0xf4, 0x9a, 0xf7, 0xac, 0xa8, 0x5c, 0xcb, 0xb2, 0x97, 0x1b, 0xcb, 0x12,
0xbd, 0x07, 0x9b, 0x51, 0x21, 0x92, 0xf5, 0x2d, 0x37, 0x13, 0x05, 0xa8, 0xf1, 0x67, 0x1d, 0xf6,
0x95, 0x4b, 0xa5, 0x37, 0x18, 0x0d, 0xc6, 0x74, 0x11, 0x1f, 0x8b, 0x9f, 0xc2, 0xee, 0xaa, 0xa8,
0xca, 0x89, 0x48, 0x74, 0xd4, 0x2e, 0x1e, 0xbd, 0x9d, 0x58, 0xe9, 0xca, 0x0c, 0x8c, 0xe2, 0x62,
0xbb, 0x32, 0xed, 0x30, 0xa1, 0xc8, 0x9e, 0xfb, 0x4b, 0x4f, 0x85, 0xa8, 0xac, 0x78, 0x68, 0x15,
0xce, 0x5c, 0x24, 0x22, 0xfa, 0x11, 0xc4, 0x41, 0x4e, 0xe8, 0x0f, 0x0b, 0x27, 0xb8, 0x16, 0xd5,
0xaf, 0xbc, 0x2a, 0xb7, 0xa6, 0x40, 0x6f, 0x9d, 0x55, 0x33, 0xb7, 0xcf, 0xaa, 0x9f, 0x41, 0x2d,
0xce, 0x0e, 0x75, 0x8d, 0xa5, 0x93, 0xb8, 0xb5, 0x6d, 0x0a, 0x1b, 0xf6, 0x22, 0x06, 0x8e, 0x08,
0xaa, 0xbf, 0x1d, 0xc2, 0x6e, 0x22, 0xb5, 0x56, 0xa6, 0xcb, 0x4c, 0x44, 0xab, 0xec, 0x4a, 0x9a,
0x1e, 0x8f, 0x50, 0xa6, 0xeb, 0xd2, 0xf4, 0x08, 0x56, 0xa6, 0xff, 0x0a, 0x2a, 0x37, 0xae, 0x79,
0x79, 0xe1, 0xf7, 0x9f, 0xdd, 0xae, 0xac, 0xeb, 0xdc, 0x73, 0xb0, 0xe6, 0xae, 0x57, 0x1e, 0xa7,
0xee, 0x79, 0x0f, 0x00, 0x7c, 0xcf, 0xf1, 0x3d, 0x72, 0xe1, 0xfa, 0x17, 0xa2, 0xe0, 0x96, 0x70,
0x41, 0x20, 0xc7, 0xae, 0x7f, 0x51, 0xfb, 0x0a, 0xd0, 0x7f, 0x79, 0x9f, 0xfa, 0x8b, 0x06, 0xf7,
0xd7, 0x9b, 0xa8, 0xfa, 0xfc, 0xff, 0x2c, 0x84, 0x3e, 0x83, 0x9c, 0x3d, 0x66, 0x8e, 0xef, 0xa9,
0xca, 0xf0, 0x7e, 0x62, 0x28, 0xa6, 0xa1, 0xef, 0xbe, 0xa4, 0xa7, 0xbe, 0x3b, 0x51, 0xc6, 0xb4,
0x04, 0x15, 0xab, 0x21, 0xa9, 0xa4, 0xcb, 0xa6, 0x93, 0xee, 0xf1, 0x6f, 0x75, 0x28, 0xa7, 0x2a,
0x43, 0xba, 0x35, 0x94, 0xa1, 0xd0, 0xeb, 0x93, 0x8e, 0x39, 0x6a, 0x59, 0x5d, 0x43, 0x43, 0x06,
0x94, 0xfa, 0x3d, 0xab, 0xdf, 0x23, 0x1d, 0xb3, 0xdd, 0xef, 0xf0, 0x26, 0xf1, 0x36, 0x6c, 0x77,
0xad, 0xde, 0x73, 0xd2, 0xeb, 0x8f, 0x88, 0xd9, 0xb5, 0x9e, 0x5a, 0xc7, 0x5d, 0xd3, 0xc8, 0xa2,
0x5d, 0x30, 0xfa, 0x3d, 0xd2, 0x3e, 0x6d, 0x59, 0x3d, 0x32, 0xb2, 0xce, 0xcc, 0xfe, 0xf9, 0xc8,
0xd0, 0x39, 0xca, 0xb3, 0x99, 0x98, 0xdf, 0xb6, 0x4d, 0xb3, 0x33, 0x24, 0x67, 0xad, 0x6f, 0x8d,
0x0d, 0x54, 0x85, 0x5d, 0xab, 0x37, 0x3c, 0x3f, 0x39, 0xb1, 0xda, 0x96, 0xd9, 0x1b, 0x91, 0xe3,
0x56, 0xb7, 0xd5, 0x6b, 0x9b, 0x46, 0x0e, 0xdd, 0x03, 0x64, 0xf5, 0xda, 0xfd, 0xb3, 0x41, 0xd7,
0x1c, 0x99, 0x24, 0x6a, 0x46, 0x9b, 0x68, 0x07, 0xb6, 0x84, 0x9e, 0x56, 0xa7, 0x43, 0x4e, 0x5a,
0x56, 0xd7, 0xec, 0x18, 0x79, 0x6e, 0x89, 0x62, 0x0c, 0x49, 0xc7, 0x1a, 0xb6, 0x8e, 0x39, 0x5c,
0xe0, 0x73, 0x5a, 0xbd, 0x17, 0x7d, 0xab, 0x6d, 0x92, 0x36, 0x57, 0xcb, 0x51, 0xe0, 0xe4, 0x08,
0x3d, 0xef, 0x75, 0x4c, 0x3c, 0x68, 0x59, 0x1d, 0xa3, 0x88, 0xf6, 0x61, 0x2f, 0x82, 0xcd, 0x6f,
0x07, 0x16, 0xfe, 0x8e, 0x8c, 0xfa, 0x7d, 0x32, 0xec, 0xf7, 0x7b, 0x46, 0x29, 0xa9, 0x89, 0xaf,
0xb6, 0x3f, 0x30, 0x7b, 0x46, 0x19, 0xed, 0xc1, 0xce, 0xd9, 0x60, 0x40, 0x22, 0x49, 0xb4, 0xd8,
0x0a, 0xa7, 0xb7, 0x3a, 0x1d, 0x6c, 0x0e, 0x87, 0xe4, 0xcc, 0x1a, 0x9e, 0xb5, 0x46, 0xed, 0x53,
0x63, 0x8b, 0x2f, 0x69, 0x68, 0x8e, 0xc8, 0xa8, 0x3f, 0x6a, 0x75, 0x57, 0xb8, 0xc1, 0x0d, 0x5a,
0xe1, 0x7c, 0xd2, 0x6e, 0xff, 0x1b, 0x63, 0x9b, 0x6f, 0x38, 0x87, 0xfb, 0x2f, 0x94, 0x89, 0x88,
0xaf, 0x5d, 0xb9, 0x27, 0x9a, 0xd3, 0xd8, 0xe1, 0xa0, 0xd5, 0x7b, 0xd1, 0xea, 0x5a, 0x1d, 0xf2,
0xdc, 0xfc, 0x4e, 0x34, 0xf3, 0x5d, 0x0e, 0x4a, 0xcb, 0xc8, 0x00, 0xf7, 0x9f, 0x72, 0x43, 0x8c,
0xb7, 0x11, 0x82, 0x4a, 0xdb, 0xc2, 0xed, 0xf3, 0x6e, 0x0b, 0x13, 0xdc, 0x3f, 0x1f, 0x99, 0xc6,
0xbd, 0xc7, 0x7f, 0xd4, 0xa0, 0x94, 0x2c, 0xd6, 0xdc, 0xeb, 0x56, 0x8f, 0x9c, 0x74, 0xad, 0xa7,
0xa7, 0x23, 0x19, 0x04, 0xc3, 0xf3, 0x36, 0x77, 0x99, 0xc9, 0x0f, 0x09, 0x08, 0x2a, 0x72, 0xd3,
0xe3, 0xc5, 0x66, 0xf8, 0x5c, 0x0a, 0xeb, 0xf5, 0x95, 0xde, 0x2c, 0x37, 0x5e, 0x81, 0x26, 0xc6,
0x7d, 0x6c, 0xe8, 0xe8, 0x03, 0xa8, 0x2b, 0x84, 0xfb, 0x15, 0x63, 0xb3, 0x3d, 0x22, 0x83, 0xd6,
0x77, 0x67, 0xdc, 0xed, 0x32, 0xc8, 0x86, 0xc6, 0x06, 0x7a, 0x17, 0xf6, 0x63, 0xd6, 0xba, 0xb8,
0x78, 0xfc, 0x39, 0x54, 0xef, 0x0a, 0x7a, 0x04, 0x90, 0x1b, 0x9a, 0xa3, 0x51, 0xd7, 0x94, 0x07,
0x9b, 0x13, 0x19, 0xb8, 0x00, 0x39, 0x6c, 0x0e, 0xcf, 0xcf, 0x4c, 0x23, 0x73, 0xf4, 0xd7, 0x3c,
0xe4, 0xc4, 0x49, 0x3b, 0x40, 0x5f, 0x41, 0x39, 0xf1, 0x92, 0xf4, 0xe2, 0x08, 0x3d, 0x78, 0xed,
0x1b, 0x53, 0x2d, 0xba, 0x8f, 0x2b, 0xf8, 0x50, 0x43, 0xc7, 0x50, 0x49, 0x3e, 0xa9, 0xbc, 0x38,
0x42, 0xc9, 0x03, 0xea, 0x9a, 0xd7, 0x96, 0x35, 0x3a, 0x9e, 0x83, 0x61, 0x86, 0xcc, 0x99, 0xf3,
0x3e, 0xa9, 0x1e, 0x3d, 0x50, 0x2d, 0x99, 0xe0, 0xe9, 0x97, 0x94, 0xda, 0xfe, 0x5a, 0x99, 0x2a,
0x39, 0x5f, 0xf3, 0x33, 0x49, 0xfc, 0xec, 0x70, 0x6b, 0x41, 0xe9, 0xb7, 0x8e, 0xda, 0x3b, 0x77,
0x89, 0xd5, 0x53, 0x41, 0xf6, 0x77, 0x19, 0xbe, 0xc6, 0x72, 0x42, 0xb6, 0x66, 0x97, 0x6e, 0x28,
0x5d, 0xd3, 0xb9, 0xd1, 0x04, 0x76, 0xd6, 0x3c, 0x49, 0xa0, 0x0f, 0xd3, 0x75, 0xec, 0x8e, 0x07,
0x8d, 0xda, 0xc3, 0x37, 0xd1, 0xd4, 0xe2, 0x27, 0xb0, 0xb3, 0xe6, 0xed, 0x22, 0x35, 0xcb, 0xdd,
0x2f, 0x1f, 0xa9, 0x59, 0x5e, 0xf7, 0x04, 0xf2, 0x3d, 0x18, 0x37, 0xaf, 0xba, 0xa8, 0x71, 0x73,
0xec, 0xed, 0x3b, 0x77, 0xed, 0xfd, 0xd7, 0x72, 0x94, 0x72, 0x0b, 0x60, 0x75, 0x61, 0x44, 0xf7,
0x13, 0x43, 0x6e, 0x5d, 0x78, 0x6b, 0x0f, 0xee, 0x90, 0x2a, 0x55, 0x23, 0xd8, 0x59, 0x73, 0x83,
0x4c, 0xed, 0xc6, 0xdd, 0x37, 0xcc, 0xda, 0xee, 0xba, 0x8b, 0xd6, 0xa1, 0x86, 0xce, 0x64, 0x80,
0x45, 0xcf, 0xa3, 0x6f, 0xc8, 0x98, 0xea, 0xfa, 0x03, 0xe1, 0x32, 0x14, 0xa1, 0x75, 0xa8, 0xa1,
0x3e, 0x94, 0x92, 0x59, 0xf2, 0xc6, 0xf4, 0x79, 0xa3, 0xc2, 0x29, 0x6c, 0xa5, 0x9a, 0xb1, 0x1f,
0xa0, 0x47, 0x6f, 0x3c, 0x52, 0xc8, 0x1d, 0x4b, 0x45, 0xc0, 0x6b, 0xce, 0x1e, 0x4d, 0xed, 0x50,
0x3b, 0xfe, 0xe4, 0x97, 0x4f, 0x2e, 0x1d, 0x36, 0x5b, 0x5e, 0x1c, 0x8c, 0xfd, 0xf9, 0x13, 0xf1,
0xfa, 0xe9, 0x39, 0xde, 0xa5, 0x47, 0xd9, 0x2b, 0x3f, 0xb8, 0x7a, 0xe2, 0x7a, 0x93, 0x27, 0x22,
0x0d, 0x9e, 0xc4, 0x2a, 0x2f, 0x72, 0xe2, 0x8f, 0x1f, 0x3f, 0xfa, 0x57, 0x00, 0x00, 0x00, 0xff,
0xff, 0x03, 0xef, 0xc7, 0x4a, 0x2c, 0x19, 0x00, 0x00,
0x6d, 0xf5, 0x5d, 0x28, 0xad, 0xba, 0x64, 0x58, 0xcb, 0x35, 0xb2, 0xad, 0x32, 0x86, 0x59, 0xd4,
0x22, 0xc3, 0x64, 0x31, 0xb1, 0x6c, 0x3b, 0x10, 0xde, 0x5f, 0x15, 0x93, 0xb6, 0x6d, 0x07, 0xcd,
0x4f, 0x01, 0x25, 0x6d, 0x51, 0x67, 0x16, 0x97, 0x18, 0xed, 0xee, 0x12, 0x73, 0x1f, 0xea, 0xa3,
0xe5, 0x45, 0x38, 0x09, 0x9c, 0x0b, 0x7a, 0xca, 0xdc, 0x89, 0xf1, 0x92, 0x7a, 0x2c, 0x8c, 0x12,
0xf9, 0x9f, 0x39, 0x28, 0xc6, 0x28, 0xaf, 0xe0, 0x8e, 0x37, 0xf1, 0xe7, 0x91, 0x5d, 0x1e, 0x75,
0xb9, 0x69, 0xb2, 0x6f, 0x6c, 0x47, 0xa4, 0x8e, 0xa4, 0x98, 0x36, 0xe7, 0x4f, 0x9d, 0x83, 0xe2,
0xcf, 0x48, 0xfe, 0xe4, 0x31, 0x48, 0xfe, 0x16, 0xe8, 0xb1, 0xfc, 0x19, 0x73, 0x27, 0xf1, 0xb9,
0xe1, 0x6a, 0x84, 0x73, 0x65, 0x24, 0x67, 0x2c, 0x39, 0xe2, 0xcc, 0x49, 0xce, 0x08, 0x57, 0x9c,
0xef, 0x41, 0x99, 0xa7, 0x4c, 0xc8, 0xac, 0xf9, 0x82, 0x78, 0xa1, 0x38, 0xbc, 0x1c, 0x2e, 0xc5,
0x58, 0x3f, 0x44, 0x5f, 0x00, 0x50, 0x6e, 0x1f, 0x61, 0xd7, 0x0b, 0x2a, 0xb2, 0xa6, 0x7a, 0xf4,
0x4e, 0x22, 0x76, 0xe2, 0x03, 0x38, 0x10, 0xff, 0x8e, 0xaf, 0x17, 0x14, 0x17, 0x69, 0xf4, 0x13,
0x7d, 0x09, 0x95, 0xa9, 0x1f, 0xbc, 0xb2, 0x02, 0x9b, 0x08, 0x50, 0x55, 0x96, 0xbd, 0x84, 0x84,
0x13, 0x49, 0x17, 0xcb, 0x4f, 0xdf, 0xc2, 0xe5, 0x69, 0xe2, 0x1b, 0x3d, 0x07, 0x14, 0xad, 0x17,
0x85, 0x40, 0x0a, 0x29, 0x08, 0x21, 0xfb, 0xb7, 0x85, 0xf0, 0x3a, 0x1e, 0x09, 0xd2, 0xa7, 0x37,
0x30, 0xf4, 0x19, 0x94, 0x43, 0xca, 0x98, 0x4b, 0x95, 0x98, 0xa2, 0x10, 0x73, 0x2f, 0x35, 0xf6,
0x70, 0x72, 0x24, 0xa1, 0x14, 0xae, 0x3e, 0xd1, 0x31, 0x6c, 0xb9, 0x8e, 0x77, 0x95, 0x54, 0x03,
0xc4, 0xfa, 0x5a, 0x62, 0x7d, 0xcf, 0xf1, 0xae, 0x92, 0x3a, 0x54, 0xdc, 0x24, 0xd0, 0xfc, 0x1c,
0x8a, 0xf1, 0x29, 0xa1, 0x12, 0x6c, 0x9e, 0xf7, 0x9f, 0xf7, 0x07, 0xdf, 0xf4, 0xf5, 0xb7, 0x50,
0x01, 0x72, 0x23, 0xa3, 0xdf, 0xd5, 0x35, 0x0e, 0x63, 0xa3, 0x63, 0x98, 0x2f, 0x0c, 0x3d, 0xc3,
0x3f, 0x4e, 0x06, 0xf8, 0x9b, 0x36, 0xee, 0xea, 0xd9, 0xe3, 0x4d, 0xd8, 0x10, 0xfb, 0x36, 0xff,
0xa8, 0x41, 0x41, 0x78, 0xd0, 0x9b, 0xfa, 0xe8, 0xff, 0x21, 0x0e, 0x2e, 0x51, 0xff, 0x78, 0x4f,
0x16, 0x51, 0x57, 0xc1, 0x71, 0xc0, 0x8c, 0x15, 0xce, 0x99, 0xe3, 0xd0, 0x88, 0x99, 0x33, 0x92,
0x39, 0x22, 0xc4, 0xcc, 0x8f, 0x13, 0x92, 0x53, 0x55, 0x29, 0x87, 0xb7, 0x22, 0x42, 0x54, 0x84,
0x93, 0xe3, 0x6f, 0xaa, 0x58, 0x27, 0xc6, 0x5f, 0xc5, 0xdb, 0xfc, 0x29, 0x94, 0x93, 0x3e, 0x47,
0x8f, 0x20, 0xe7, 0x78, 0x53, 0x5f, 0x25, 0xe2, 0xce, 0x8d, 0xe0, 0xe2, 0x46, 0x62, 0xc1, 0xd0,
0x44, 0xa0, 0xdf, 0xf4, 0x73, 0xb3, 0x02, 0xa5, 0x84, 0xd3, 0x9a, 0x7f, 0xd3, 0xa0, 0x92, 0x72,
0xc2, 0xbf, 0x2d, 0x1d, 0x7d, 0x01, 0xe5, 0x57, 0x4e, 0x40, 0x49, 0x72, 0x42, 0xa8, 0x1e, 0xd5,
0xd3, 0x13, 0x42, 0xf4, 0x7f, 0xc7, 0xb7, 0x29, 0x2e, 0x71, 0x7e, 0x05, 0xa0, 0x5f, 0x40, 0x55,
0xad, 0x24, 0x36, 0x65, 0x96, 0xe3, 0x8a, 0xa3, 0xaa, 0xa6, 0xc2, 0x43, 0xf1, 0x76, 0x05, 0x1d,
0x57, 0xa6, 0xc9, 0x4f, 0xf4, 0xe1, 0x4a, 0x40, 0xc8, 0x02, 0xc7, 0xbb, 0x14, 0xe7, 0x57, 0x8c,
0xd9, 0x46, 0x02, 0xe4, 0xbd, 0xbe, 0xa2, 0xe6, 0xcb, 0x11, 0xb3, 0xd8, 0x32, 0x44, 0x1f, 0xc3,
0x46, 0xc8, 0x2c, 0x55, 0xc9, 0xaa, 0xa9, 0xdc, 0x4a, 0x30, 0x52, 0x2c, 0xb9, 0x52, 0x03, 0x52,
0xe6, 0xd6, 0x80, 0xb4, 0xc1, 0x2b, 0x86, 0x2c, 0xb4, 0xa5, 0x23, 0xa4, 0x8c, 0x3f, 0x1d, 0xf7,
0x3a, 0x6d, 0xc6, 0xe8, 0x7c, 0xc1, 0xb0, 0x64, 0x50, 0x0d, 0xf0, 0x4b, 0x80, 0x8e, 0x13, 0x4c,
0x96, 0x0e, 0x7b, 0x4e, 0xaf, 0x79, 0x5b, 0x8b, 0x2a, 0xba, 0x2c, 0x7b, 0xf9, 0x89, 0xac, 0xe2,
0x7b, 0xb0, 0x19, 0x15, 0x22, 0x59, 0xdf, 0xf2, 0x33, 0x51, 0x80, 0x9a, 0x7f, 0xca, 0xc1, 0xbe,
0x72, 0xa9, 0xf4, 0x06, 0xa3, 0xc1, 0x84, 0x2e, 0xe2, 0xc9, 0xf9, 0x29, 0xec, 0xae, 0x8a, 0xaa,
0xdc, 0x88, 0x44, 0xd3, 0x78, 0xe9, 0xe8, 0xed, 0x84, 0xa5, 0x2b, 0x35, 0x30, 0x8a, 0x8b, 0xed,
0x4a, 0xb5, 0xc3, 0x84, 0x20, 0x6b, 0xee, 0x2f, 0x3d, 0x15, 0xa2, 0xb2, 0xe2, 0xa1, 0x55, 0x38,
0x73, 0x92, 0x88, 0xe8, 0x47, 0x10, 0x07, 0x39, 0xa1, 0x3f, 0x2c, 0x9c, 0xe0, 0x5a, 0x54, 0xbf,
0xca, 0xaa, 0xdc, 0x1a, 0x02, 0xbd, 0x35, 0xce, 0x66, 0x6e, 0x8f, 0xb3, 0x9f, 0x41, 0x3d, 0xce,
0x0e, 0x75, 0xd3, 0xa5, 0x76, 0xdc, 0xfd, 0x36, 0x85, 0x0e, 0x7b, 0x11, 0x07, 0x8e, 0x18, 0x54,
0x0b, 0x3c, 0x84, 0xdd, 0x44, 0x6a, 0xad, 0x54, 0x97, 0x99, 0x88, 0x56, 0xd9, 0x95, 0x54, 0x3d,
0x5e, 0xa1, 0x54, 0xcf, 0x49, 0xd5, 0x23, 0x58, 0xa9, 0xfe, 0x2b, 0xa8, 0xde, 0xb8, 0x09, 0x16,
0x84, 0xdf, 0x7f, 0x76, 0xbb, 0xb2, 0xae, 0x73, 0xcf, 0xc1, 0x9a, 0xeb, 0x60, 0x65, 0x92, 0xba,
0x0a, 0x3e, 0x00, 0xf0, 0x3d, 0xc7, 0xf7, 0xc8, 0x85, 0xeb, 0x5f, 0x88, 0x82, 0x5b, 0xc6, 0x45,
0x81, 0x1c, 0xbb, 0xfe, 0x45, 0xfd, 0x2b, 0x40, 0xff, 0xe5, 0x95, 0xeb, 0xcf, 0x1a, 0xdc, 0x5f,
0xaf, 0xa2, 0xea, 0xf3, 0xff, 0xb3, 0x10, 0xfa, 0x0c, 0xf2, 0xd6, 0x84, 0x39, 0xbe, 0xa7, 0x2a,
0xc3, 0xfb, 0x89, 0xa5, 0x98, 0x86, 0xbe, 0xfb, 0x92, 0x9e, 0xfa, 0xae, 0xad, 0x94, 0x69, 0x0b,
0x56, 0xac, 0x96, 0xa4, 0x92, 0x2e, 0x9b, 0x4e, 0xba, 0xc7, 0xbf, 0xcd, 0x41, 0x25, 0x55, 0x19,
0xd2, 0xad, 0xa1, 0x02, 0xc5, 0xfe, 0x80, 0x74, 0x8d, 0x71, 0xdb, 0xec, 0xe9, 0x1a, 0xd2, 0xa1,
0x3c, 0xe8, 0x9b, 0x83, 0x3e, 0xe9, 0x1a, 0x9d, 0x41, 0x97, 0x37, 0x89, 0xb7, 0x61, 0xbb, 0x67,
0xf6, 0x9f, 0x93, 0xfe, 0x60, 0x4c, 0x8c, 0x9e, 0xf9, 0xd4, 0x3c, 0xee, 0x19, 0x7a, 0x16, 0xed,
0x82, 0x3e, 0xe8, 0x93, 0xce, 0x69, 0xdb, 0xec, 0x93, 0xb1, 0x79, 0x66, 0x0c, 0xce, 0xc7, 0x7a,
0x8e, 0xa3, 0x3c, 0x9b, 0x89, 0xf1, 0x6d, 0xc7, 0x30, 0xba, 0x23, 0x72, 0xd6, 0xfe, 0x56, 0xdf,
0x40, 0x35, 0xd8, 0x35, 0xfb, 0xa3, 0xf3, 0x93, 0x13, 0xb3, 0x63, 0x1a, 0xfd, 0x31, 0x39, 0x6e,
0xf7, 0xda, 0xfd, 0x8e, 0xa1, 0xe7, 0xd1, 0x3d, 0x40, 0x66, 0xbf, 0x33, 0x38, 0x1b, 0xf6, 0x8c,
0xb1, 0x41, 0xa2, 0x66, 0xb4, 0x89, 0x76, 0x60, 0x4b, 0xc8, 0x69, 0x77, 0xbb, 0xe4, 0xa4, 0x6d,
0xf6, 0x8c, 0xae, 0x5e, 0xe0, 0x9a, 0x28, 0x8e, 0x11, 0xe9, 0x9a, 0xa3, 0xf6, 0x31, 0x87, 0x8b,
0x7c, 0x4f, 0xb3, 0xff, 0x62, 0x60, 0x76, 0x0c, 0xd2, 0xe1, 0x62, 0x39, 0x0a, 0x9c, 0x39, 0x42,
0xcf, 0xfb, 0x5d, 0x03, 0x0f, 0xdb, 0x66, 0x57, 0x2f, 0xa1, 0x7d, 0xd8, 0x8b, 0x60, 0xe3, 0xdb,
0xa1, 0x89, 0xbf, 0x23, 0xe3, 0xc1, 0x80, 0x8c, 0x06, 0x83, 0xbe, 0x5e, 0x4e, 0x4a, 0xe2, 0xd6,
0x0e, 0x86, 0x46, 0x5f, 0xaf, 0xa0, 0x3d, 0xd8, 0x39, 0x1b, 0x0e, 0x49, 0x44, 0x89, 0x8c, 0xad,
0x72, 0xf6, 0x76, 0xb7, 0x8b, 0x8d, 0xd1, 0x88, 0x9c, 0x99, 0xa3, 0xb3, 0xf6, 0xb8, 0x73, 0xaa,
0x6f, 0x71, 0x93, 0x46, 0xc6, 0x98, 0x8c, 0x07, 0xe3, 0x76, 0x6f, 0x85, 0xeb, 0x5c, 0xa1, 0x15,
0xce, 0x37, 0xed, 0x0d, 0xbe, 0xd1, 0xb7, 0xf9, 0x81, 0x73, 0x78, 0xf0, 0x42, 0xa9, 0x88, 0xb8,
0xed, 0xca, 0x3d, 0xd1, 0x9e, 0xfa, 0x0e, 0x07, 0xcd, 0xfe, 0x8b, 0x76, 0xcf, 0xec, 0x92, 0xe7,
0xc6, 0x77, 0xa2, 0x99, 0xef, 0x72, 0x50, 0x6a, 0x46, 0x86, 0x78, 0xf0, 0x94, 0x2b, 0xa2, 0xbf,
0x8d, 0x10, 0x54, 0x3b, 0x26, 0xee, 0x9c, 0xf7, 0xda, 0x98, 0xe0, 0xc1, 0xf9, 0xd8, 0xd0, 0xef,
0x3d, 0xfe, 0x83, 0x06, 0xe5, 0x64, 0xb1, 0xe6, 0x5e, 0x37, 0xfb, 0xe4, 0xa4, 0x67, 0x3e, 0x3d,
0x1d, 0xcb, 0x20, 0x18, 0x9d, 0x77, 0xb8, 0xcb, 0x0c, 0x3e, 0x24, 0x20, 0xa8, 0xca, 0x43, 0x8f,
0x8d, 0xcd, 0xf0, 0xbd, 0x14, 0xd6, 0x1f, 0x28, 0xb9, 0x59, 0xae, 0xbc, 0x02, 0x0d, 0x8c, 0x07,
0x58, 0xcf, 0xa1, 0x0f, 0xa0, 0xa1, 0x10, 0xee, 0x57, 0x8c, 0x8d, 0xce, 0x98, 0x0c, 0xdb, 0xdf,
0x9d, 0x71, 0xb7, 0xcb, 0x20, 0x1b, 0xe9, 0x1b, 0xe8, 0x5d, 0xd8, 0x8f, 0xb9, 0xd6, 0xc5, 0xc5,
0xe3, 0xcf, 0xa1, 0x76, 0x57, 0xd0, 0x23, 0x80, 0xfc, 0xc8, 0x18, 0x8f, 0x7b, 0x86, 0x1c, 0x6c,
0x4e, 0x64, 0xe0, 0x02, 0xe4, 0xb1, 0x31, 0x3a, 0x3f, 0x33, 0xf4, 0xcc, 0xd1, 0x5f, 0x0a, 0x90,
0x17, 0x93, 0x76, 0x80, 0xbe, 0x82, 0x4a, 0xe2, 0xb1, 0xe9, 0xc5, 0x11, 0x7a, 0xf0, 0xda, 0x67,
0xa8, 0x7a, 0x74, 0x65, 0x57, 0xf0, 0xa1, 0x86, 0x8e, 0xa1, 0x9a, 0x7c, 0x75, 0x79, 0x71, 0x84,
0x92, 0x03, 0xea, 0x9a, 0x07, 0x99, 0x35, 0x32, 0x9e, 0x83, 0x6e, 0x84, 0xcc, 0x99, 0xf3, 0x3e,
0xa9, 0xde, 0x45, 0x50, 0x3d, 0x99, 0xe0, 0xe9, 0xc7, 0x96, 0xfa, 0xfe, 0x5a, 0x9a, 0x2a, 0x39,
0x5f, 0xf3, 0x99, 0x24, 0x7e, 0x99, 0xb8, 0x65, 0x50, 0xfa, 0x39, 0xa4, 0xfe, 0xce, 0x5d, 0x64,
0xf5, 0x9a, 0x90, 0xfd, 0x5d, 0x86, 0xdb, 0x58, 0x49, 0xd0, 0xd6, 0x9c, 0xd2, 0x0d, 0xa1, 0x6b,
0x3a, 0x37, 0xb2, 0x61, 0x67, 0xcd, 0xab, 0x05, 0xfa, 0x30, 0x5d, 0xc7, 0xee, 0x78, 0xf3, 0xa8,
0x3f, 0x7c, 0x13, 0x9b, 0x32, 0xde, 0x86, 0x9d, 0x35, 0xcf, 0x1b, 0xa9, 0x5d, 0xee, 0x7e, 0x1c,
0x49, 0xed, 0xf2, 0xba, 0x57, 0x92, 0xef, 0x41, 0xbf, 0x79, 0x1b, 0x46, 0xcd, 0x9b, 0x6b, 0x6f,
0x5f, 0xcb, 0xeb, 0xef, 0xbf, 0x96, 0x47, 0x09, 0x37, 0x01, 0x56, 0x17, 0x46, 0x74, 0x3f, 0xb1,
0xe4, 0xd6, 0x9d, 0xb8, 0xfe, 0xe0, 0x0e, 0xaa, 0x12, 0x35, 0x86, 0x9d, 0x35, 0x37, 0xc8, 0xd4,
0x69, 0xdc, 0x7d, 0xc3, 0xac, 0xef, 0xae, 0xbb, 0x68, 0x1d, 0x6a, 0xe8, 0x4c, 0x06, 0x58, 0xf4,
0x82, 0xfa, 0x86, 0x8c, 0xa9, 0xad, 0x1f, 0x08, 0x97, 0xa1, 0x08, 0xad, 0x43, 0x0d, 0x0d, 0xa0,
0x9c, 0xcc, 0x92, 0x37, 0xa6, 0xcf, 0x1b, 0x05, 0x4e, 0x61, 0x2b, 0xd5, 0x8c, 0xfd, 0x00, 0x3d,
0x7a, 0xe3, 0x48, 0x21, 0x4f, 0x2c, 0x15, 0x01, 0xaf, 0x99, 0x3d, 0x5a, 0xda, 0xa1, 0x76, 0xfc,
0xc9, 0x2f, 0x9f, 0x5c, 0x3a, 0x6c, 0xb6, 0xbc, 0x38, 0x98, 0xf8, 0xf3, 0x27, 0xe2, 0x81, 0xd4,
0x73, 0xbc, 0x4b, 0x8f, 0xb2, 0x57, 0x7e, 0x70, 0xf5, 0xc4, 0xf5, 0xec, 0x27, 0x22, 0x0d, 0x9e,
0xc4, 0x22, 0x2f, 0xf2, 0xe2, 0xef, 0x23, 0x3f, 0xfa, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02,
0x24, 0x31, 0xa7, 0x4f, 0x19, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
+3
View File
@@ -394,6 +394,9 @@ message BuildRouteRequest {
pubkey.
*/
repeated bytes hop_pubkeys = 4;
// An optional payment addr to be included within the last hop of the route.
bytes payment_addr = 5;
}
message BuildRouteResponse {
+5
View File
@@ -832,6 +832,11 @@
"format": "byte"
},
"description": "A list of hops that defines the route. This does not include the source hop\npubkey."
},
"payment_addr": {
"type": "string",
"format": "byte",
"description": "An optional payment addr to be included within the last hop of the route."
}
}
},
+9 -1
View File
@@ -564,9 +564,17 @@ func (s *Server) BuildRoute(ctx context.Context,
outgoingChan = &req.OutgoingChanId
}
var payAddr *[32]byte
if len(req.PaymentAddr) != 0 {
var backingPayAddr [32]byte
copy(backingPayAddr[:], req.PaymentAddr)
payAddr = &backingPayAddr
}
// Build the route and return it to the caller.
route, err := s.cfg.Router.BuildRoute(
amt, hops, outgoingChan, req.FinalCltvDelta,
amt, hops, outgoingChan, req.FinalCltvDelta, payAddr,
)
if err != nil {
return nil, err
+798 -773
View File
File diff suppressed because it is too large. Load diff
+14
View File
@@ -2897,6 +2897,13 @@ message Invoice {
[EXPERIMENTAL].
*/
bool is_keysend = 25;
/*
The payment address of this invoice. This value will be used in MPP
payments, and also for newer invoies that always require the MPP paylaod
for added end-to-end security.
*/
bytes payment_addr = 26;
}
enum InvoiceHTLCState {
@@ -2955,6 +2962,13 @@ message AddInvoiceResponse {
invoices with an add_index greater than this one.
*/
uint64 add_index = 16;
/*
The payment address of the generated invoice. This value should be used
in all payments for this invoice as we require it for end to end
security.
*/
bytes payment_addr = 17;
}
message PaymentHash {
/*
+10
View File
@@ -2461,6 +2461,11 @@
"type": "string",
"format": "uint64",
"description": "The \"add\" index of this invoice. Each newly created invoice will increment\nthis index making it monotonically increasing. Callers to the\nSubscribeInvoices call can use this to instantly get notified of all added\ninvoices with an add_index greater than this one."
},
"payment_addr": {
"type": "string",
"format": "byte",
"description": "The payment address of the generated invoice. This value should be used\nin all payments for this invoice as we require it for end to end\nsecurity."
}
}
},
@@ -4083,6 +4088,11 @@
"type": "boolean",
"format": "boolean",
"description": "Indicates if this invoice was a spontaneous payment that arrived via keysend\n[EXPERIMENTAL]."
},
"payment_addr": {
"type": "string",
"format": "byte",
"description": "The payment address of this invoice. This value will be used in MPP\npayments, and also for newer invoies that always require the MPP paylaod\nfor added end-to-end security."
}
}
},
+34 -9
View File
@@ -27,6 +27,7 @@ var (
type interceptorTestCase struct {
amountMsat int64
payAddr []byte
invoice *lnrpc.Invoice
shouldHold bool
interceptorAction routerrpc.ResolveHoldForwardAction
@@ -105,7 +106,9 @@ func testForwardInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
defer wg.Done()
for _, tc := range testCases {
attempt, err := testContext.sendAliceToCarolPayment(
context.Background(), tc.invoice.ValueMsat, tc.invoice.RHash)
context.Background(), tc.invoice.ValueMsat,
tc.invoice.RHash, tc.payAddr,
)
if t.t.Failed() {
return
@@ -118,7 +121,8 @@ func testForwardInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
// For 'fail' interceptor action we make sure the payment failed.
case routerrpc.ResolveHoldForwardAction_FAIL:
if attempt.Status != lnrpc.HTLCAttempt_FAILED {
t.t.Errorf("expected payment to fail, instead got %v", attempt.Status)
t.t.Errorf("expected payment to fail, "+
"instead got %v", attempt.Status)
}
// For settle and resume we make sure the payment is successful.
@@ -127,7 +131,8 @@ func testForwardInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
case routerrpc.ResolveHoldForwardAction_RESUME:
if attempt.Status != lnrpc.HTLCAttempt_SUCCEEDED {
t.t.Errorf("expected payment to succeed, instead got %v", attempt.Status)
t.t.Errorf("expected payment to "+
"succeed, instead got %v", attempt.Status)
}
}
}
@@ -202,7 +207,9 @@ func testForwardInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
foundPayment.Status != lnrpc.Payment_IN_FLIGHT {
t.Fatalf("expected to find in flight payment for"+
"amount %v, %v", testCase.invoice.ValueMsat, foundPayment.Status)
"amount %v, %v",
testCase.invoice.ValueMsat,
foundPayment.Status)
}
}
}
@@ -294,12 +301,25 @@ func (c *interceptorTestContext) prepareTestCases() (
if err != nil {
return nil, fmt.Errorf("unable to add invoice: %v", err)
}
// We'll need to also decode the returned invoice so we can
// grab the payment address which is now required for ALL
// payments.
payReq, err := c.carol.DecodePayReq(context.Background(), &lnrpc.PayReqString{
PayReq: invoice.PaymentRequest,
})
if err != nil {
return nil, fmt.Errorf("unable to decode invoice: %v", err)
}
t.invoice = invoice
t.payAddr = payReq.PaymentAddr
}
return cases, nil
}
func (c *interceptorTestContext) openChannel(from, to *lntest.HarnessNode, chanSize btcutil.Amount) {
func (c *interceptorTestContext) openChannel(from, to *lntest.HarnessNode,
chanSize btcutil.Amount) {
ctxb := context.Background()
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
@@ -365,10 +385,14 @@ func (c *interceptorTestContext) waitForChannels() {
// sendAliceToCarolPayment sends a payment from alice to carol and make an
// attempt to pay. The lnrpc.HTLCAttempt is returned.
func (c *interceptorTestContext) sendAliceToCarolPayment(ctx context.Context,
amtMsat int64, paymentHash []byte) (*lnrpc.HTLCAttempt, error) {
amtMsat int64,
paymentHash, paymentAddr []byte) (*lnrpc.HTLCAttempt, error) {
// Build a route from alice to carol.
route, err := c.buildRoute(ctx, amtMsat, []*lntest.HarnessNode{c.bob, c.carol})
route, err := c.buildRoute(
ctx, amtMsat, []*lntest.HarnessNode{c.bob, c.carol},
paymentAddr,
)
if err != nil {
return nil, err
}
@@ -387,8 +411,8 @@ func (c *interceptorTestContext) sendAliceToCarolPayment(ctx context.Context,
}
// buildRoute is a helper function to build a route with given hops.
func (c *interceptorTestContext) buildRoute(ctx context.Context, amtMsat int64, hops []*lntest.HarnessNode) (
*lnrpc.Route, error) {
func (c *interceptorTestContext) buildRoute(ctx context.Context, amtMsat int64,
hops []*lntest.HarnessNode, payAddr []byte) (*lnrpc.Route, error) {
rpcHops := make([][]byte, 0, len(hops))
for _, hop := range hops {
@@ -405,6 +429,7 @@ func (c *interceptorTestContext) buildRoute(ctx context.Context, amtMsat int64,
AmtMsat: amtMsat,
FinalCltvDelta: chainreg.DefaultBitcoinTimeLockDelta,
HopPubkeys: rpcHops,
PaymentAddr: payAddr,
}
routeResp, err := c.alice.RouterClient.BuildRoute(ctx, req)
+67 -73
View File
@@ -751,6 +751,10 @@ func createPayReqs(node *lntest.HarnessNode, paymentAmt btcutil.Amount,
"invoice: %v", err)
}
// Set the payment address in the invoice so the caller can
// properly use it.
invoice.PaymentAddr = resp.PaymentAddr
payReqs[i] = resp.PaymentRequest
rHashes[i] = resp.RHash
invoices[i] = invoice
@@ -2196,9 +2200,19 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
routes.Routes[0].Hops[1].AmtToForward = amtSat
routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat
// Manually set the MPP payload a new for each payment since
// the payment addr will change with each invoice, although we
// can re-use the route itself.
route := routes.Routes[0]
route.Hops[len(route.Hops)-1].TlvPayload = true
route.Hops[len(route.Hops)-1].MppRecord = &lnrpc.MPPRecord{
PaymentAddr: resp.PaymentAddr,
TotalAmtMsat: amtMSat,
}
sendReq = &lnrpc.SendToRouteRequest{
PaymentHash: resp.RHash,
Route: routes.Routes[0],
Route: route,
}
err = alicePayStream.Send(sendReq)
@@ -5351,10 +5365,6 @@ type singleHopSendToRouteCase struct {
// routerrpc submits the request to the routerrpc subserver if true,
// otherwise submits to the main rpc server.
routerrpc bool
// mpp sets the MPP fields on the request if true, otherwise submits a
// regular payment.
mpp bool
}
var singleHopSendToRouteCases = []singleHopSendToRouteCase{
@@ -5371,17 +5381,14 @@ var singleHopSendToRouteCases = []singleHopSendToRouteCase{
},
{
name: "mpp main sync",
mpp: true,
},
{
name: "mpp main stream",
streaming: true,
mpp: true,
},
{
name: "mpp routerrpc sync",
routerrpc: true,
mpp: true,
},
}
@@ -5558,11 +5565,7 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
// - routerrpc server sync
sendToRouteSync := func() {
for i, rHash := range rHashes {
// Populate the MPP fields for the final hop if we are
// testing MPP payments.
if test.mpp {
setMPPFields(i)
}
setMPPFields(i)
sendReq := &lnrpc.SendToRouteRequest{
PaymentHash: rHash,
@@ -5591,11 +5594,7 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
}
for i, rHash := range rHashes {
// Populate the MPP fields for the final hop if we are
// testing MPP payments.
if test.mpp {
setMPPFields(i)
}
setMPPFields(i)
sendReq := &lnrpc.SendToRouteRequest{
PaymentHash: rHash,
@@ -5619,11 +5618,7 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
}
sendToRouteRouterRPC := func() {
for i, rHash := range rHashes {
// Populate the MPP fields for the final hop if we are
// testing MPP payments.
if test.mpp {
setMPPFields(i)
}
setMPPFields(i)
sendReq := &routerrpc.SendToRouteRequest{
PaymentHash: rHash,
@@ -5719,26 +5714,22 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
// properly populated. Otherwise the hop should not have an MPP
// record.
hop := htlc.Route.Hops[0]
if test.mpp {
if hop.MppRecord == nil {
t.Fatalf("expected mpp record for mpp payment")
}
if hop.MppRecord == nil {
t.Fatalf("expected mpp record for mpp payment")
}
if hop.MppRecord.TotalAmtMsat != paymentAmtSat*1000 {
t.Fatalf("incorrect mpp total msat for payment %d "+
"want: %d, got: %d",
i, paymentAmtSat*1000,
hop.MppRecord.TotalAmtMsat)
}
if hop.MppRecord.TotalAmtMsat != paymentAmtSat*1000 {
t.Fatalf("incorrect mpp total msat for payment %d "+
"want: %d, got: %d",
i, paymentAmtSat*1000,
hop.MppRecord.TotalAmtMsat)
}
expAddr := payAddrs[i]
if !bytes.Equal(hop.MppRecord.PaymentAddr, expAddr) {
t.Fatalf("incorrect mpp payment addr for payment %d "+
"want: %x, got: %x",
i, expAddr, hop.MppRecord.PaymentAddr)
}
} else if hop.MppRecord != nil {
t.Fatalf("unexpected mpp record for non-mpp payment")
expAddr := payAddrs[i]
if !bytes.Equal(hop.MppRecord.PaymentAddr, expAddr) {
t.Fatalf("incorrect mpp payment addr for payment %d "+
"want: %x, got: %x",
i, expAddr, hop.MppRecord.PaymentAddr)
}
}
@@ -5877,11 +5868,23 @@ func testMultiHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
}
}
// Query for routes to pay from Alice to Carol.
// We set FinalCltvDelta to 40 since by default QueryRoutes returns
// the last hop with a final cltv delta of 9 where as the default in
// htlcswitch is 40.
const paymentAmt = 1000
// Create 5 invoices for Carol, which expect a payment from Alice for 1k
// satoshis with a different preimage each time.
const (
numPayments = 5
paymentAmt = 1000
)
_, rHashes, invoices, err := createPayReqs(
carol, paymentAmt, numPayments,
)
if err != nil {
t.Fatalf("unable to create pay reqs: %v", err)
}
// Construct a route from Alice to Carol for each of the invoices
// created above. We set FinalCltvDelta to 40 since by default
// QueryRoutes returns the last hop with a final cltv delta of 9 where
// as the default in htlcswitch is 40.
routesReq := &lnrpc.QueryRoutesRequest{
PubKey: carol.PubKeyStr,
Amt: paymentAmt,
@@ -5893,16 +5896,6 @@ func testMultiHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to get route: %v", err)
}
// Create 5 invoices for Carol, which expect a payment from Alice for 1k
// satoshis with a different preimage each time.
const numPayments = 5
_, rHashes, _, err := createPayReqs(
carol, paymentAmt, numPayments,
)
if err != nil {
t.Fatalf("unable to create pay reqs: %v", err)
}
// We'll wait for all parties to recognize the new channels within the
// network.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
@@ -5916,30 +5909,31 @@ func testMultiHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
// Using Alice as the source, pay to the 5 invoices from Carol created
// above.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
alicePayStream, err := net.Alice.SendToRoute(ctxt)
if err != nil {
t.Fatalf("unable to create payment stream for alice: %v", err)
}
for _, rHash := range rHashes {
sendReq := &lnrpc.SendToRouteRequest{
for i, rHash := range rHashes {
// Manually set the MPP payload a new for each payment since
// the payment addr will change with each invoice, although we
// can re-use the route itself.
route := *routes.Routes[0]
route.Hops[len(route.Hops)-1].TlvPayload = true
route.Hops[len(route.Hops)-1].MppRecord = &lnrpc.MPPRecord{
PaymentAddr: invoices[i].PaymentAddr,
TotalAmtMsat: int64(
lnwire.NewMSatFromSatoshis(paymentAmt),
),
}
sendReq := &routerrpc.SendToRouteRequest{
PaymentHash: rHash,
Route: routes.Routes[0],
Route: &route,
}
err := alicePayStream.Send(sendReq)
resp, err := net.Alice.RouterClient.SendToRouteV2(ctxt, sendReq)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
}
for range rHashes {
resp, err := alicePayStream.Recv()
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
if resp.PaymentError != "" {
t.Fatalf("received payment error: %v", resp.PaymentError)
if resp.Failure != nil {
t.Fatalf("received payment error: %v", resp.Failure)
}
}
+14
View File
@@ -398,6 +398,20 @@ func (fv *FeatureVector) HasFeature(feature FeatureBit) bool {
(fv.isFeatureBitPair(feature) && fv.IsSet(feature^1))
}
// RequiresFeature returns true if the referenced feature vector *requires*
// that the given required bit be set. This method can be used with both
// optional and required feature bits as a parameter.
func (fv *FeatureVector) RequiresFeature(feature FeatureBit) bool {
// If we weren't passed a required feature bit, then we'll flip the
// lowest bit to query for the required version of the feature. This
// lets callers pass in both the optional and required bits.
if !feature.IsRequired() {
feature ^= 1
}
return fv.IsSet(feature)
}
// UnknownRequiredFeatures returns a list of feature bits set in the vector
// that are unknown and in an even bit position. Feature bits with an even
// index must be known to a node receiving the feature vector in a message.
+29 -1
View File
@@ -5,6 +5,8 @@ import (
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
var testFeatureNames = map[FeatureBit]string{
@@ -87,6 +89,7 @@ func TestFeatureVectorSetUnset(t *testing.T) {
t.Errorf("Expectation failed in case %d, bit %d", i, j)
break
}
}
for _, bit := range test.bits {
@@ -95,6 +98,31 @@ func TestFeatureVectorSetUnset(t *testing.T) {
}
}
// TestFeatureVectorRequiresFeature tests that if a feature vector only
// includes a required feature bit (it's even), then the RequiresFeature method
// will return true for both that bit as well as it's optional counter party.
func TestFeatureVectorRequiresFeature(t *testing.T) {
t.Parallel()
// Create a new feature vector with the features above, and set only
// the set of required bits. These will be all the even features
// referenced above.
fv := NewFeatureVector(nil, testFeatureNames)
fv.Set(0)
fv.Set(4)
// Next we'll query for those exact bits, these should show up as being
// required.
require.True(t, fv.RequiresFeature(0))
require.True(t, fv.RequiresFeature(4))
// If we query for the odd (optional) counter party to each of the
// features, the method should still return that the backing feature
// vector requires the feature to be set.
require.True(t, fv.RequiresFeature(1))
require.True(t, fv.RequiresFeature(5))
}
func TestFeatureVectorEncodeDecode(t *testing.T) {
t.Parallel()
@@ -277,7 +305,7 @@ func TestIsRequired(t *testing.T) {
}
// TestFeatures asserts that the Features() method on a FeatureVector properly
// returns the set of feature bits it stores internallly.
// returns the set of feature bits it stores internally.
func TestFeatures(t *testing.T) {
tests := []struct {
name string
+6 -5
View File
@@ -2432,7 +2432,7 @@ func (e ErrNoChannel) Error() string {
// outgoing channel, use the outgoingChan parameter.
func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
hops []route.Vertex, outgoingChan *uint64,
finalCltvDelta int32) (*route.Route, error) {
finalCltvDelta int32, payAddr *[32]byte) (*route.Route, error) {
log.Tracef("BuildRoute called: hopsCount=%v, amt=%v",
len(hops), amt)
@@ -2582,10 +2582,11 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
return newRoute(
source, pathEdges, uint32(height),
finalHopParams{
amt: receiverAmt,
totalAmt: receiverAmt,
cltvDelta: uint16(finalCltvDelta),
records: nil,
amt: receiverAmt,
totalAmt: receiverAmt,
cltvDelta: uint16(finalCltvDelta),
records: nil,
paymentAddr: payAddr,
},
)
}
+41 -22
View File
@@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/clock"
@@ -2921,6 +2922,10 @@ func TestSendToRouteMaxHops(t *testing.T) {
func TestBuildRoute(t *testing.T) {
// Setup a three node network.
chanCapSat := btcutil.Amount(100000)
paymentAddrFeatures := lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(lnwire.PaymentAddrOptional),
lnwire.Features,
)
testChannels := []*testChannel{
// Create two local channels from a. The bandwidth is estimated
// in this test as the channel capacity. For building routes, we
@@ -2945,29 +2950,33 @@ func TestBuildRoute(t *testing.T) {
// nodes are recommended to keep their channel policies towards
// the same peer identical.
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 50000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
Expiry: 144,
FeeRate: 50000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
Features: paymentAddrFeatures,
}, 2),
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 60000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
Expiry: 144,
FeeRate: 60000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
Features: paymentAddrFeatures,
}, 7),
symmetricTestChannel("a", "e", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 80000,
MinHTLC: lnwire.NewMSatFromSatoshis(5),
MaxHTLC: lnwire.NewMSatFromSatoshis(10),
Expiry: 144,
FeeRate: 80000,
MinHTLC: lnwire.NewMSatFromSatoshis(5),
MaxHTLC: lnwire.NewMSatFromSatoshis(10),
Features: paymentAddrFeatures,
}, 5),
symmetricTestChannel("e", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 100000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
Expiry: 144,
FeeRate: 100000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
Features: paymentAddrFeatures,
}, 4),
}
@@ -2987,7 +2996,9 @@ func TestBuildRoute(t *testing.T) {
}
defer cleanUp()
checkHops := func(rt *route.Route, expected []uint64) {
checkHops := func(rt *route.Route, expected []uint64,
payAddr [32]byte) {
t.Helper()
if len(rt.Hops) != len(expected) {
@@ -3000,8 +3011,16 @@ func TestBuildRoute(t *testing.T) {
expected[i], i, hop.ChannelID)
}
}
lastHop := rt.Hops[len(rt.Hops)-1]
require.NotNil(t, lastHop.MPP)
require.Equal(t, lastHop.MPP.PaymentAddr(), payAddr)
}
var payAddr [32]byte
_, err = rand.Read(payAddr[:])
require.NoError(t, err)
// Create hop list from the route node pubkeys.
hops := []route.Vertex{
ctx.aliases["b"], ctx.aliases["c"],
@@ -3010,7 +3029,7 @@ func TestBuildRoute(t *testing.T) {
// Build the route for the given amount.
rt, err := ctx.router.BuildRoute(
&amt, hops, nil, 40,
&amt, hops, nil, 40, &payAddr,
)
if err != nil {
t.Fatal(err)
@@ -3019,14 +3038,14 @@ func TestBuildRoute(t *testing.T) {
// Check that we get the expected route back. The total amount should be
// the amount to deliver to hop c (100 sats) plus the max fee for the
// connection b->c (6 sats).
checkHops(rt, []uint64{1, 7})
checkHops(rt, []uint64{1, 7}, payAddr)
if rt.TotalAmount != 106000 {
t.Fatalf("unexpected total amount %v", rt.TotalAmount)
}
// Build the route for the minimum amount.
rt, err = ctx.router.BuildRoute(
nil, hops, nil, 40,
nil, hops, nil, 40, &payAddr,
)
if err != nil {
t.Fatal(err)
@@ -3036,7 +3055,7 @@ func TestBuildRoute(t *testing.T) {
// send from b to c is 20 sats. Hop b charges 1200 msat for the
// forwarding. The channel between hop a and b can carry amounts in the
// range [5, 100], so 21200 msats is the minimum amount for this route.
checkHops(rt, []uint64{1, 7})
checkHops(rt, []uint64{1, 7}, payAddr)
if rt.TotalAmount != 21200 {
t.Fatalf("unexpected total amount %v", rt.TotalAmount)
}
@@ -3047,7 +3066,7 @@ func TestBuildRoute(t *testing.T) {
ctx.aliases["e"], ctx.aliases["c"],
}
_, err = ctx.router.BuildRoute(
nil, hops, nil, 40,
nil, hops, nil, 40, nil,
)
errNoChannel, ok := err.(ErrNoChannel)
if !ok {
+1
View File
@@ -4766,6 +4766,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
AddIndex: dbInvoice.AddIndex,
PaymentRequest: string(dbInvoice.PaymentRequest),
RHash: hash[:],
PaymentAddr: dbInvoice.Terms.PaymentAddr[:],
}, nil
}