multi: add explicit hodl invoice flag to invoice
Previously it wasn't possible to store a preimage in the invoice database and signal that a payment should not be settled right away. The only way to hold a payment was to insert the magic UnknownPreimage value in the invoice database. This commit introduces a distinct flag to signal that an invoice is a hold invoice and thereby allows the preimage to be present in the database already. Preparation for (key send) hodl invoices for which we already know the preimage.
This commit is contained in:
parent
bdc0d875bc
commit
d416ed59ea
@ -20,7 +20,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
|
func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
|
||||||
var pre, payAddr [32]byte
|
var (
|
||||||
|
pre lntypes.Preimage
|
||||||
|
payAddr [32]byte
|
||||||
|
)
|
||||||
if _, err := rand.Read(pre[:]); err != nil {
|
if _, err := rand.Read(pre[:]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -32,7 +35,7 @@ func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
|
|||||||
CreationDate: testNow,
|
CreationDate: testNow,
|
||||||
Terms: ContractTerm{
|
Terms: ContractTerm{
|
||||||
Expiry: 4000,
|
Expiry: 4000,
|
||||||
PaymentPreimage: pre,
|
PaymentPreimage: &pre,
|
||||||
PaymentAddr: payAddr,
|
PaymentAddr: payAddr,
|
||||||
Value: value,
|
Value: value,
|
||||||
Features: emptyFeatures,
|
Features: emptyFeatures,
|
||||||
@ -360,13 +363,18 @@ func TestInvoiceCancelSingleHtlc(t *testing.T) {
|
|||||||
t.Fatalf("unable to make test db: %v", err)
|
t.Fatalf("unable to make test db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preimage := lntypes.Preimage{1}
|
||||||
|
paymentHash := preimage.Hash()
|
||||||
|
|
||||||
testInvoice := &Invoice{
|
testInvoice := &Invoice{
|
||||||
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
||||||
|
Terms: ContractTerm{
|
||||||
|
Value: lnwire.NewMSatFromSatoshis(10000),
|
||||||
|
Features: emptyFeatures,
|
||||||
|
PaymentPreimage: &preimage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
testInvoice.Terms.Value = lnwire.NewMSatFromSatoshis(10000)
|
|
||||||
testInvoice.Terms.Features = emptyFeatures
|
|
||||||
|
|
||||||
var paymentHash lntypes.Hash
|
|
||||||
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
||||||
t.Fatalf("unable to find invoice: %v", err)
|
t.Fatalf("unable to find invoice: %v", err)
|
||||||
}
|
}
|
||||||
@ -1059,15 +1067,20 @@ func TestCustomRecords(t *testing.T) {
|
|||||||
t.Fatalf("unable to make test db: %v", err)
|
t.Fatalf("unable to make test db: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preimage := lntypes.Preimage{1}
|
||||||
|
paymentHash := preimage.Hash()
|
||||||
|
|
||||||
testInvoice := &Invoice{
|
testInvoice := &Invoice{
|
||||||
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
||||||
|
Terms: ContractTerm{
|
||||||
|
Value: lnwire.NewMSatFromSatoshis(10000),
|
||||||
|
Features: emptyFeatures,
|
||||||
|
PaymentPreimage: &preimage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
testInvoice.Terms.Value = lnwire.NewMSatFromSatoshis(10000)
|
|
||||||
testInvoice.Terms.Features = emptyFeatures
|
|
||||||
|
|
||||||
var paymentHash lntypes.Hash
|
|
||||||
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
||||||
t.Fatalf("unable to find invoice: %v", err)
|
t.Fatalf("unable to add invoice: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept an htlc with custom records on this invoice.
|
// Accept an htlc with custom records on this invoice.
|
||||||
|
@ -17,9 +17,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// UnknownPreimage is an all-zeroes preimage that indicates that the
|
// unknownPreimage is an all-zeroes preimage that indicates that the
|
||||||
// preimage for this invoice is not yet known.
|
// preimage for this invoice is not yet known.
|
||||||
UnknownPreimage lntypes.Preimage
|
unknownPreimage lntypes.Preimage
|
||||||
|
|
||||||
// invoiceBucket is the name of the bucket within the database that
|
// invoiceBucket is the name of the bucket within the database that
|
||||||
// stores all data related to invoices no matter their final state.
|
// stores all data related to invoices no matter their final state.
|
||||||
@ -150,6 +150,7 @@ const (
|
|||||||
featuresType tlv.Type = 11
|
featuresType tlv.Type = 11
|
||||||
invStateType tlv.Type = 12
|
invStateType tlv.Type = 12
|
||||||
amtPaidType tlv.Type = 13
|
amtPaidType tlv.Type = 13
|
||||||
|
hodlInvoiceType tlv.Type = 14
|
||||||
)
|
)
|
||||||
|
|
||||||
// InvoiceRef is a composite identifier for invoices. Invoices can be referenced
|
// InvoiceRef is a composite identifier for invoices. Invoices can be referenced
|
||||||
@ -261,8 +262,8 @@ type ContractTerm struct {
|
|||||||
|
|
||||||
// PaymentPreimage is the preimage which is to be revealed in the
|
// PaymentPreimage is the preimage which is to be revealed in the
|
||||||
// occasion that an HTLC paying to the hash of this preimage is
|
// occasion that an HTLC paying to the hash of this preimage is
|
||||||
// extended.
|
// extended. Set to nil if the preimage isn't known yet.
|
||||||
PaymentPreimage lntypes.Preimage
|
PaymentPreimage *lntypes.Preimage
|
||||||
|
|
||||||
// Value is the expected amount of milli-satoshis to be paid to an HTLC
|
// Value is the expected amount of milli-satoshis to be paid to an HTLC
|
||||||
// which can be satisfied by the above preimage.
|
// which can be satisfied by the above preimage.
|
||||||
@ -346,6 +347,10 @@ type Invoice struct {
|
|||||||
// Htlcs records all htlcs that paid to this invoice. Some of these
|
// Htlcs records all htlcs that paid to this invoice. Some of these
|
||||||
// htlcs may have been marked as canceled.
|
// htlcs may have been marked as canceled.
|
||||||
Htlcs map[CircuitKey]*InvoiceHTLC
|
Htlcs map[CircuitKey]*InvoiceHTLC
|
||||||
|
|
||||||
|
// HodlInvoice indicates whether the invoice should be held in the
|
||||||
|
// Accepted state or be settled right away.
|
||||||
|
HodlInvoice bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HtlcState defines the states an htlc paying to an invoice can be in.
|
// HtlcState defines the states an htlc paying to an invoice can be in.
|
||||||
@ -439,14 +444,19 @@ type InvoiceStateUpdateDesc struct {
|
|||||||
NewState ContractState
|
NewState ContractState
|
||||||
|
|
||||||
// Preimage must be set to the preimage when NewState is settled.
|
// Preimage must be set to the preimage when NewState is settled.
|
||||||
Preimage lntypes.Preimage
|
Preimage *lntypes.Preimage
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvoiceUpdateCallback is a callback used in the db transaction to update the
|
// InvoiceUpdateCallback is a callback used in the db transaction to update the
|
||||||
// invoice.
|
// invoice.
|
||||||
type InvoiceUpdateCallback = func(invoice *Invoice) (*InvoiceUpdateDesc, error)
|
type InvoiceUpdateCallback = func(invoice *Invoice) (*InvoiceUpdateDesc, error)
|
||||||
|
|
||||||
func validateInvoice(i *Invoice) error {
|
func validateInvoice(i *Invoice, paymentHash lntypes.Hash) error {
|
||||||
|
// Avoid conflicts with all-zeroes magic value in the database.
|
||||||
|
if paymentHash == unknownPreimage.Hash() {
|
||||||
|
return fmt.Errorf("cannot use hash of all-zeroes preimage")
|
||||||
|
}
|
||||||
|
|
||||||
if len(i.Memo) > MaxMemoSize {
|
if len(i.Memo) > MaxMemoSize {
|
||||||
return fmt.Errorf("max length a memo is %v, and invoice "+
|
return fmt.Errorf("max length a memo is %v, and invoice "+
|
||||||
"of length %v was provided", MaxMemoSize, len(i.Memo))
|
"of length %v was provided", MaxMemoSize, len(i.Memo))
|
||||||
@ -459,6 +469,10 @@ func validateInvoice(i *Invoice) error {
|
|||||||
if i.Terms.Features == nil {
|
if i.Terms.Features == nil {
|
||||||
return errors.New("invoice must have a feature vector")
|
return errors.New("invoice must have a feature vector")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i.Terms.PaymentPreimage == nil && !i.HodlInvoice {
|
||||||
|
return errors.New("non-hodl invoices must have a preimage")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,7 +489,7 @@ func (i *Invoice) IsPending() bool {
|
|||||||
func (d *DB) AddInvoice(newInvoice *Invoice, paymentHash lntypes.Hash) (
|
func (d *DB) AddInvoice(newInvoice *Invoice, paymentHash lntypes.Hash) (
|
||||||
uint64, error) {
|
uint64, error) {
|
||||||
|
|
||||||
if err := validateInvoice(newInvoice); err != nil {
|
if err := validateInvoice(newInvoice, paymentHash); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1131,7 +1145,13 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
|
|||||||
}
|
}
|
||||||
featureBytes := fb.Bytes()
|
featureBytes := fb.Bytes()
|
||||||
|
|
||||||
preimage := [32]byte(i.Terms.PaymentPreimage)
|
preimage := [32]byte(unknownPreimage)
|
||||||
|
if i.Terms.PaymentPreimage != nil {
|
||||||
|
preimage = *i.Terms.PaymentPreimage
|
||||||
|
if preimage == unknownPreimage {
|
||||||
|
return errors.New("cannot use all-zeroes preimage")
|
||||||
|
}
|
||||||
|
}
|
||||||
value := uint64(i.Terms.Value)
|
value := uint64(i.Terms.Value)
|
||||||
cltvDelta := uint32(i.Terms.FinalCltvDelta)
|
cltvDelta := uint32(i.Terms.FinalCltvDelta)
|
||||||
expiry := uint64(i.Terms.Expiry)
|
expiry := uint64(i.Terms.Expiry)
|
||||||
@ -1139,6 +1159,11 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
|
|||||||
amtPaid := uint64(i.AmtPaid)
|
amtPaid := uint64(i.AmtPaid)
|
||||||
state := uint8(i.State)
|
state := uint8(i.State)
|
||||||
|
|
||||||
|
var hodlInvoice uint8
|
||||||
|
if i.HodlInvoice {
|
||||||
|
hodlInvoice = 1
|
||||||
|
}
|
||||||
|
|
||||||
tlvStream, err := tlv.NewStream(
|
tlvStream, err := tlv.NewStream(
|
||||||
// Memo and payreq.
|
// Memo and payreq.
|
||||||
tlv.MakePrimitiveRecord(memoType, &i.Memo),
|
tlv.MakePrimitiveRecord(memoType, &i.Memo),
|
||||||
@ -1161,6 +1186,8 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
|
|||||||
// Invoice state.
|
// Invoice state.
|
||||||
tlv.MakePrimitiveRecord(invStateType, &state),
|
tlv.MakePrimitiveRecord(invStateType, &state),
|
||||||
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
|
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
|
||||||
|
|
||||||
|
tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1256,12 +1283,13 @@ func fetchInvoice(invoiceNum []byte, invoices kvdb.RBucket) (Invoice, error) {
|
|||||||
|
|
||||||
func deserializeInvoice(r io.Reader) (Invoice, error) {
|
func deserializeInvoice(r io.Reader) (Invoice, error) {
|
||||||
var (
|
var (
|
||||||
preimage [32]byte
|
preimageBytes [32]byte
|
||||||
value uint64
|
value uint64
|
||||||
cltvDelta uint32
|
cltvDelta uint32
|
||||||
expiry uint64
|
expiry uint64
|
||||||
amtPaid uint64
|
amtPaid uint64
|
||||||
state uint8
|
state uint8
|
||||||
|
hodlInvoice uint8
|
||||||
|
|
||||||
creationDateBytes []byte
|
creationDateBytes []byte
|
||||||
settleDateBytes []byte
|
settleDateBytes []byte
|
||||||
@ -1281,7 +1309,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
|
|||||||
tlv.MakePrimitiveRecord(settleIndexType, &i.SettleIndex),
|
tlv.MakePrimitiveRecord(settleIndexType, &i.SettleIndex),
|
||||||
|
|
||||||
// Terms.
|
// Terms.
|
||||||
tlv.MakePrimitiveRecord(preimageType, &preimage),
|
tlv.MakePrimitiveRecord(preimageType, &preimageBytes),
|
||||||
tlv.MakePrimitiveRecord(valueType, &value),
|
tlv.MakePrimitiveRecord(valueType, &value),
|
||||||
tlv.MakePrimitiveRecord(cltvDeltaType, &cltvDelta),
|
tlv.MakePrimitiveRecord(cltvDeltaType, &cltvDelta),
|
||||||
tlv.MakePrimitiveRecord(expiryType, &expiry),
|
tlv.MakePrimitiveRecord(expiryType, &expiry),
|
||||||
@ -1291,6 +1319,8 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
|
|||||||
// Invoice state.
|
// Invoice state.
|
||||||
tlv.MakePrimitiveRecord(invStateType, &state),
|
tlv.MakePrimitiveRecord(invStateType, &state),
|
||||||
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
|
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
|
||||||
|
|
||||||
|
tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
@ -1307,13 +1337,21 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Terms.PaymentPreimage = lntypes.Preimage(preimage)
|
preimage := lntypes.Preimage(preimageBytes)
|
||||||
|
if preimage != unknownPreimage {
|
||||||
|
i.Terms.PaymentPreimage = &preimage
|
||||||
|
}
|
||||||
|
|
||||||
i.Terms.Value = lnwire.MilliSatoshi(value)
|
i.Terms.Value = lnwire.MilliSatoshi(value)
|
||||||
i.Terms.FinalCltvDelta = int32(cltvDelta)
|
i.Terms.FinalCltvDelta = int32(cltvDelta)
|
||||||
i.Terms.Expiry = time.Duration(expiry)
|
i.Terms.Expiry = time.Duration(expiry)
|
||||||
i.AmtPaid = lnwire.MilliSatoshi(amtPaid)
|
i.AmtPaid = lnwire.MilliSatoshi(amtPaid)
|
||||||
i.State = ContractState(state)
|
i.State = ContractState(state)
|
||||||
|
|
||||||
|
if hodlInvoice != 0 {
|
||||||
|
i.HodlInvoice = true
|
||||||
|
}
|
||||||
|
|
||||||
err = i.CreationDate.UnmarshalBinary(creationDateBytes)
|
err = i.CreationDate.UnmarshalBinary(creationDateBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
@ -1443,10 +1481,16 @@ func copyInvoice(src *Invoice) *Invoice {
|
|||||||
Htlcs: make(
|
Htlcs: make(
|
||||||
map[CircuitKey]*InvoiceHTLC, len(src.Htlcs),
|
map[CircuitKey]*InvoiceHTLC, len(src.Htlcs),
|
||||||
),
|
),
|
||||||
|
HodlInvoice: src.HodlInvoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.Terms.Features = src.Terms.Features.Clone()
|
dest.Terms.Features = src.Terms.Features.Clone()
|
||||||
|
|
||||||
|
if src.Terms.PaymentPreimage != nil {
|
||||||
|
preimage := *src.Terms.PaymentPreimage
|
||||||
|
dest.Terms.PaymentPreimage = &preimage
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range src.Htlcs {
|
for k, v := range src.Htlcs {
|
||||||
dest.Htlcs[k] = copyInvoiceHTLC(v)
|
dest.Htlcs[k] = copyInvoiceHTLC(v)
|
||||||
}
|
}
|
||||||
@ -1619,10 +1663,16 @@ func updateInvoiceState(invoice *Invoice, hash lntypes.Hash,
|
|||||||
case ContractOpen:
|
case ContractOpen:
|
||||||
if update.NewState == ContractSettled {
|
if update.NewState == ContractSettled {
|
||||||
// Validate preimage.
|
// Validate preimage.
|
||||||
if update.Preimage.Hash() != hash {
|
switch {
|
||||||
return ErrInvoicePreimageMismatch
|
case update.Preimage != nil:
|
||||||
|
if update.Preimage.Hash() != hash {
|
||||||
|
return ErrInvoicePreimageMismatch
|
||||||
|
}
|
||||||
|
invoice.Terms.PaymentPreimage = update.Preimage
|
||||||
|
|
||||||
|
case invoice.Terms.PaymentPreimage == nil:
|
||||||
|
return errors.New("unknown preimage")
|
||||||
}
|
}
|
||||||
invoice.Terms.PaymentPreimage = update.Preimage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once settled, we are in a terminal state.
|
// Once settled, we are in a terminal state.
|
||||||
|
@ -1457,8 +1457,7 @@ func (c *ChannelArbitrator) isPreimageAvailable(hash lntypes.Hash) (bool,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
preimageAvailable = invoice.Terms.PaymentPreimage !=
|
preimageAvailable = invoice.Terms.PaymentPreimage != nil
|
||||||
channeldb.UnknownPreimage
|
|
||||||
|
|
||||||
return preimageAvailable, nil
|
return preimageAvailable, nil
|
||||||
}
|
}
|
||||||
|
@ -437,7 +437,9 @@ func TestChannelLinkCancelFullCommitment(t *testing.T) {
|
|||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
preimages[i] = lntypes.Preimage{byte(i >> 8), byte(i)}
|
// Deterministically generate preimages. Avoid the all-zeroes
|
||||||
|
// preimage because that will be rejected by the database.
|
||||||
|
preimages[i] = lntypes.Preimage{byte(i >> 8), byte(i), 1}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
@ -2015,13 +2017,13 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
|
|||||||
// If we now send in a valid HTLC settle for the prior HTLC we added,
|
// If we now send in a valid HTLC settle for the prior HTLC we added,
|
||||||
// then the bandwidth should remain unchanged as the remote party will
|
// then the bandwidth should remain unchanged as the remote party will
|
||||||
// gain additional channel balance.
|
// gain additional channel balance.
|
||||||
err = bobChannel.SettleHTLC(invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
|
err = bobChannel.SettleHTLC(*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to settle htlc: %v", err)
|
t.Fatalf("unable to settle htlc: %v", err)
|
||||||
}
|
}
|
||||||
htlcSettle := &lnwire.UpdateFulfillHTLC{
|
htlcSettle := &lnwire.UpdateFulfillHTLC{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
PaymentPreimage: invoice.Terms.PaymentPreimage,
|
PaymentPreimage: *invoice.Terms.PaymentPreimage,
|
||||||
}
|
}
|
||||||
aliceLink.HandleChannelUpdate(htlcSettle)
|
aliceLink.HandleChannelUpdate(htlcSettle)
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
@ -2193,7 +2195,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
|
|||||||
outgoingHTLCID: addPkt.outgoingHTLCID,
|
outgoingHTLCID: addPkt.outgoingHTLCID,
|
||||||
htlc: &lnwire.UpdateFulfillHTLC{
|
htlc: &lnwire.UpdateFulfillHTLC{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
PaymentPreimage: invoice.Terms.PaymentPreimage,
|
PaymentPreimage: *invoice.Terms.PaymentPreimage,
|
||||||
},
|
},
|
||||||
obfuscator: NewMockObfuscator(),
|
obfuscator: NewMockObfuscator(),
|
||||||
}
|
}
|
||||||
@ -3153,13 +3155,13 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
|
|||||||
// If we now send in a valid HTLC settle for the prior HTLC we added,
|
// If we now send in a valid HTLC settle for the prior HTLC we added,
|
||||||
// then the bandwidth should remain unchanged as the remote party will
|
// then the bandwidth should remain unchanged as the remote party will
|
||||||
// gain additional channel balance.
|
// gain additional channel balance.
|
||||||
err = bobChannel.SettleHTLC(invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
|
err = bobChannel.SettleHTLC(*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to settle htlc: %v", err)
|
t.Fatalf("unable to settle htlc: %v", err)
|
||||||
}
|
}
|
||||||
htlcSettle := &lnwire.UpdateFulfillHTLC{
|
htlcSettle := &lnwire.UpdateFulfillHTLC{
|
||||||
ID: bobIndex,
|
ID: bobIndex,
|
||||||
PaymentPreimage: invoice.Terms.PaymentPreimage,
|
PaymentPreimage: *invoice.Terms.PaymentPreimage,
|
||||||
}
|
}
|
||||||
aliceLink.HandleChannelUpdate(htlcSettle)
|
aliceLink.HandleChannelUpdate(htlcSettle)
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
@ -4730,7 +4732,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
|
|||||||
for i, invoice := range invoices {
|
for i, invoice := range invoices {
|
||||||
ctx.sendSettleBobToAlice(
|
ctx.sendSettleBobToAlice(
|
||||||
uint64(i),
|
uint64(i),
|
||||||
invoice.Terms.PaymentPreimage,
|
*invoice.Terms.PaymentPreimage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5772,7 +5774,8 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
|
|||||||
|
|
||||||
// Convert into a hodl invoice and save the preimage for later.
|
// Convert into a hodl invoice and save the preimage for later.
|
||||||
preimage := invoice.Terms.PaymentPreimage
|
preimage := invoice.Terms.PaymentPreimage
|
||||||
invoice.Terms.PaymentPreimage = channeldb.UnknownPreimage
|
invoice.Terms.PaymentPreimage = nil
|
||||||
|
invoice.HodlInvoice = true
|
||||||
|
|
||||||
// We must add the invoice to the registry, such that Alice
|
// We must add the invoice to the registry, such that Alice
|
||||||
// expects this payment.
|
// expects this payment.
|
||||||
@ -5814,7 +5817,10 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
|
|||||||
<-registry.settleChan
|
<-registry.settleChan
|
||||||
|
|
||||||
// Settle the invoice with the preimage.
|
// Settle the invoice with the preimage.
|
||||||
registry.SettleHodlInvoice(preimage)
|
err = registry.SettleHodlInvoice(*preimage)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("settle hodl invoice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Expect alice to send a settle and commitsig message to bob.
|
// Expect alice to send a settle and commitsig message to bob.
|
||||||
ctx.receiveSettleAliceToBob()
|
ctx.receiveSettleAliceToBob()
|
||||||
@ -5957,10 +5963,12 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
|
|||||||
|
|
||||||
// Convert into hodl invoices and save the preimages for later.
|
// Convert into hodl invoices and save the preimages for later.
|
||||||
preimage1 := invoice1.Terms.PaymentPreimage
|
preimage1 := invoice1.Terms.PaymentPreimage
|
||||||
invoice1.Terms.PaymentPreimage = channeldb.UnknownPreimage
|
invoice1.Terms.PaymentPreimage = nil
|
||||||
|
invoice1.HodlInvoice = true
|
||||||
|
|
||||||
preimage2 := invoice2.Terms.PaymentPreimage
|
preimage2 := invoice2.Terms.PaymentPreimage
|
||||||
invoice2.Terms.PaymentPreimage = channeldb.UnknownPreimage
|
invoice2.Terms.PaymentPreimage = nil
|
||||||
|
invoice2.HodlInvoice = true
|
||||||
|
|
||||||
// We must add the invoices to the registry, such that Alice
|
// We must add the invoices to the registry, such that Alice
|
||||||
// expects the payments.
|
// expects the payments.
|
||||||
@ -6009,7 +6017,10 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Settle invoice 1 with the preimage.
|
// Settle invoice 1 with the preimage.
|
||||||
registry.SettleHodlInvoice(preimage1)
|
err = registry.SettleHodlInvoice(*preimage1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("settle hodl invoice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Expect alice to send a settle and commitsig message to bob. Bob does
|
// Expect alice to send a settle and commitsig message to bob. Bob does
|
||||||
// not yet send the revocation.
|
// not yet send the revocation.
|
||||||
@ -6017,7 +6028,10 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
|
|||||||
ctx.receiveCommitSigAliceToBob(1)
|
ctx.receiveCommitSigAliceToBob(1)
|
||||||
|
|
||||||
// Settle invoice 2 with the preimage.
|
// Settle invoice 2 with the preimage.
|
||||||
registry.SettleHodlInvoice(preimage2)
|
err = registry.SettleHodlInvoice(*preimage2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("settle hodl invoice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Expect alice to send a settle for htlc 2.
|
// Expect alice to send a settle for htlc 2.
|
||||||
ctx.receiveSettleAliceToBob()
|
ctx.receiveSettleAliceToBob()
|
||||||
|
@ -547,8 +547,8 @@ func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
|
|||||||
// invoice which should be added by destination peer.
|
// invoice which should be added by destination peer.
|
||||||
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
||||||
timelock uint32, blob [lnwire.OnionPacketSize]byte,
|
timelock uint32, blob [lnwire.OnionPacketSize]byte,
|
||||||
preimage, rhash, payAddr [32]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC,
|
preimage *lntypes.Preimage, rhash, payAddr [32]byte) (
|
||||||
uint64, error) {
|
*channeldb.Invoice, *lnwire.UpdateAddHTLC, uint64, error) {
|
||||||
|
|
||||||
// Create the db invoice. Normally the payment requests needs to be set,
|
// Create the db invoice. Normally the payment requests needs to be set,
|
||||||
// because it is decoded in InvoiceRegistry to obtain the cltv expiry.
|
// because it is decoded in InvoiceRegistry to obtain the cltv expiry.
|
||||||
@ -556,6 +556,7 @@ func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|||||||
// step and always returning the value of testInvoiceCltvExpiry, we
|
// step and always returning the value of testInvoiceCltvExpiry, we
|
||||||
// don't need to bother here with creating and signing a payment
|
// don't need to bother here with creating and signing a payment
|
||||||
// request.
|
// request.
|
||||||
|
|
||||||
invoice := &channeldb.Invoice{
|
invoice := &channeldb.Invoice{
|
||||||
CreationDate: time.Now(),
|
CreationDate: time.Now(),
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: channeldb.ContractTerm{
|
||||||
@ -567,6 +568,7 @@ func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|||||||
nil, lnwire.Features,
|
nil, lnwire.Features,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
HodlInvoice: preimage == nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
htlc := &lnwire.UpdateAddHTLC{
|
htlc := &lnwire.UpdateAddHTLC{
|
||||||
@ -591,7 +593,7 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
|||||||
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice,
|
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice,
|
||||||
*lnwire.UpdateAddHTLC, uint64, error) {
|
*lnwire.UpdateAddHTLC, uint64, error) {
|
||||||
|
|
||||||
var preimage [sha256.Size]byte
|
var preimage lntypes.Preimage
|
||||||
r, err := generateRandomBytes(sha256.Size)
|
r, err := generateRandomBytes(sha256.Size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
@ -608,7 +610,7 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
|||||||
copy(payAddr[:], r)
|
copy(payAddr[:], r)
|
||||||
|
|
||||||
return generatePaymentWithPreimage(
|
return generatePaymentWithPreimage(
|
||||||
invoiceAmt, htlcAmt, timelock, blob, preimage, rhash, payAddr,
|
invoiceAmt, htlcAmt, timelock, blob, &preimage, rhash, payAddr,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1345,7 +1347,7 @@ func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
|
|||||||
// Generate payment: invoice and htlc.
|
// Generate payment: invoice and htlc.
|
||||||
invoice, htlc, pid, err := generatePaymentWithPreimage(
|
invoice, htlc, pid, err := generatePaymentWithPreimage(
|
||||||
invoiceAmt, htlcAmt, timelock, blob,
|
invoiceAmt, htlcAmt, timelock, blob,
|
||||||
channeldb.UnknownPreimage, rhash, payAddr,
|
nil, rhash, payAddr,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
paymentErr <- err
|
paymentErr <- err
|
||||||
|
@ -652,12 +652,6 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
|
|||||||
return errors.New("invalid keysend preimage")
|
return errors.New("invalid keysend preimage")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't accept zero preimages as those have a special meaning in our
|
|
||||||
// database for hodl invoices.
|
|
||||||
if preimage == channeldb.UnknownPreimage {
|
|
||||||
return errors.New("invalid keysend preimage")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only allow keysend for non-mpp payments.
|
// Only allow keysend for non-mpp payments.
|
||||||
if ctx.mpp != nil {
|
if ctx.mpp != nil {
|
||||||
return errors.New("no mpp keysend supported")
|
return errors.New("no mpp keysend supported")
|
||||||
@ -688,7 +682,7 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
|
|||||||
Terms: channeldb.ContractTerm{
|
Terms: channeldb.ContractTerm{
|
||||||
FinalCltvDelta: finalCltvDelta,
|
FinalCltvDelta: finalCltvDelta,
|
||||||
Value: amt,
|
Value: amt,
|
||||||
PaymentPreimage: preimage,
|
PaymentPreimage: &preimage,
|
||||||
Features: features,
|
Features: features,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -948,7 +942,7 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
|||||||
return &channeldb.InvoiceUpdateDesc{
|
return &channeldb.InvoiceUpdateDesc{
|
||||||
State: &channeldb.InvoiceStateUpdateDesc{
|
State: &channeldb.InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractSettled,
|
NewState: channeldb.ContractSettled,
|
||||||
Preimage: preimage,
|
Preimage: &preimage,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ var (
|
|||||||
testInvoiceAmt = lnwire.MilliSatoshi(100000)
|
testInvoiceAmt = lnwire.MilliSatoshi(100000)
|
||||||
testInvoice = &channeldb.Invoice{
|
testInvoice = &channeldb.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: channeldb.ContractTerm{
|
||||||
PaymentPreimage: testInvoicePreimage,
|
PaymentPreimage: &testInvoicePreimage,
|
||||||
Value: testInvoiceAmt,
|
Value: testInvoiceAmt,
|
||||||
Expiry: time.Hour,
|
Expiry: time.Hour,
|
||||||
Features: testFeatures,
|
Features: testFeatures,
|
||||||
@ -102,12 +102,12 @@ var (
|
|||||||
|
|
||||||
testHodlInvoice = &channeldb.Invoice{
|
testHodlInvoice = &channeldb.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: channeldb.ContractTerm{
|
||||||
PaymentPreimage: channeldb.UnknownPreimage,
|
Value: testInvoiceAmt,
|
||||||
Value: testInvoiceAmt,
|
Expiry: time.Hour,
|
||||||
Expiry: time.Hour,
|
Features: testFeatures,
|
||||||
Features: testFeatures,
|
|
||||||
},
|
},
|
||||||
CreationDate: testInvoiceCreationDate,
|
CreationDate: testInvoiceCreationDate,
|
||||||
|
HodlInvoice: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
|
|||||||
|
|
||||||
return &channeldb.Invoice{
|
return &channeldb.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: channeldb.ContractTerm{
|
||||||
PaymentPreimage: preimage,
|
PaymentPreimage: &preimage,
|
||||||
PaymentAddr: payAddr,
|
PaymentAddr: payAddr,
|
||||||
Value: testInvoiceAmount,
|
Value: testInvoiceAmount,
|
||||||
Expiry: expiry,
|
Expiry: expiry,
|
||||||
|
@ -81,7 +81,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
|
|
||||||
case channeldb.HtlcStateSettled:
|
case channeldb.HtlcStateSettled:
|
||||||
return nil, ctx.settleRes(
|
return nil, ctx.settleRes(
|
||||||
inv.Terms.PaymentPreimage,
|
*inv.Terms.PaymentPreimage,
|
||||||
ResultReplayToSettled,
|
ResultReplayToSettled,
|
||||||
), nil
|
), nil
|
||||||
|
|
||||||
@ -187,8 +187,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
|
|
||||||
// Check to see if we can settle or this is an hold invoice and
|
// Check to see if we can settle or this is an hold invoice and
|
||||||
// we need to wait for the preimage.
|
// we need to wait for the preimage.
|
||||||
holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage
|
if inv.HodlInvoice {
|
||||||
if holdInvoice {
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: channeldb.ContractAccepted,
|
||||||
}
|
}
|
||||||
@ -201,7 +200,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &update, ctx.settleRes(
|
return &update, ctx.settleRes(
|
||||||
inv.Terms.PaymentPreimage, ResultSettled,
|
*inv.Terms.PaymentPreimage, ResultSettled,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,14 +268,13 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
|
|
||||||
case channeldb.ContractSettled:
|
case channeldb.ContractSettled:
|
||||||
return &update, ctx.settleRes(
|
return &update, ctx.settleRes(
|
||||||
inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
|
*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we can settle or this is an hold invoice and we need
|
// Check to see if we can settle or this is an hold invoice and we need
|
||||||
// to wait for the preimage.
|
// to wait for the preimage.
|
||||||
holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage
|
if inv.HodlInvoice {
|
||||||
if holdInvoice {
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: channeldb.ContractAccepted,
|
||||||
}
|
}
|
||||||
@ -290,6 +288,6 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &update, ctx.settleRes(
|
return &update, ctx.settleRes(
|
||||||
inv.Terms.PaymentPreimage, ResultSettled,
|
*inv.Terms.PaymentPreimage, ResultSettled,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,10 @@ type AddInvoiceData struct {
|
|||||||
// Whether this invoice should include routing hints for private
|
// Whether this invoice should include routing hints for private
|
||||||
// channels.
|
// channels.
|
||||||
Private bool
|
Private bool
|
||||||
|
|
||||||
|
// HodlInvoice signals that this invoice shouldn't be settled
|
||||||
|
// immediately upon receiving the payment.
|
||||||
|
HodlInvoice bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddInvoice attempts to add a new invoice to the invoice database. Any
|
// AddInvoice attempts to add a new invoice to the invoice database. Any
|
||||||
@ -97,7 +101,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {
|
invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
paymentPreimage lntypes.Preimage
|
paymentPreimage *lntypes.Preimage
|
||||||
paymentHash lntypes.Hash
|
paymentHash lntypes.Hash
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -108,26 +112,9 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
return nil, nil,
|
return nil, nil,
|
||||||
errors.New("preimage and hash both set")
|
errors.New("preimage and hash both set")
|
||||||
|
|
||||||
// Prevent the unknown preimage magic value from being used for a
|
|
||||||
// regular invoice. This would cause the invoice the be handled as if it
|
|
||||||
// was a hold invoice.
|
|
||||||
case invoice.Preimage != nil &&
|
|
||||||
*invoice.Preimage == channeldb.UnknownPreimage:
|
|
||||||
|
|
||||||
return nil, nil,
|
|
||||||
fmt.Errorf("cannot use all zeroes as a preimage")
|
|
||||||
|
|
||||||
// Prevent the hash of the unknown preimage magic value to be used for a
|
|
||||||
// hold invoice. This would make it impossible to settle the invoice,
|
|
||||||
// because it would still be interpreted as not having a preimage.
|
|
||||||
case invoice.Hash != nil &&
|
|
||||||
*invoice.Hash == channeldb.UnknownPreimage.Hash():
|
|
||||||
|
|
||||||
return nil, nil,
|
|
||||||
fmt.Errorf("cannot use hash of all zeroes preimage")
|
|
||||||
|
|
||||||
// If no hash or preimage is given, generate a random preimage.
|
// If no hash or preimage is given, generate a random preimage.
|
||||||
case invoice.Preimage == nil && invoice.Hash == nil:
|
case invoice.Preimage == nil && invoice.Hash == nil:
|
||||||
|
paymentPreimage = &lntypes.Preimage{}
|
||||||
if _, err := rand.Read(paymentPreimage[:]); err != nil {
|
if _, err := rand.Read(paymentPreimage[:]); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -136,12 +123,12 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
// If just a hash is given, we create a hold invoice by setting the
|
// If just a hash is given, we create a hold invoice by setting the
|
||||||
// preimage to unknown.
|
// preimage to unknown.
|
||||||
case invoice.Preimage == nil && invoice.Hash != nil:
|
case invoice.Preimage == nil && invoice.Hash != nil:
|
||||||
paymentPreimage = channeldb.UnknownPreimage
|
|
||||||
paymentHash = *invoice.Hash
|
paymentHash = *invoice.Hash
|
||||||
|
|
||||||
// A specific preimage was supplied. Use that for the invoice.
|
// A specific preimage was supplied. Use that for the invoice.
|
||||||
case invoice.Preimage != nil && invoice.Hash == nil:
|
case invoice.Preimage != nil && invoice.Hash == nil:
|
||||||
paymentPreimage = *invoice.Preimage
|
preimage := *invoice.Preimage
|
||||||
|
paymentPreimage = &preimage
|
||||||
paymentHash = invoice.Preimage.Hash()
|
paymentHash = invoice.Preimage.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,6 +397,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
PaymentAddr: paymentAddr,
|
PaymentAddr: paymentAddr,
|
||||||
Features: invoiceFeatures,
|
Features: invoiceFeatures,
|
||||||
},
|
},
|
||||||
|
HodlInvoice: invoice.HodlInvoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("[addinvoice] adding new invoice %v",
|
log.Tracef("[addinvoice] adding new invoice %v",
|
||||||
|
@ -274,6 +274,8 @@ func (s *Server) AddHoldInvoice(ctx context.Context,
|
|||||||
FallbackAddr: invoice.FallbackAddr,
|
FallbackAddr: invoice.FallbackAddr,
|
||||||
CltvExpiry: invoice.CltvExpiry,
|
CltvExpiry: invoice.CltvExpiry,
|
||||||
Private: invoice.Private,
|
Private: invoice.Private,
|
||||||
|
HodlInvoice: true,
|
||||||
|
Preimage: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, dbInvoice, err := AddInvoice(ctx, addInvoiceCfg, addInvoiceData)
|
_, dbInvoice, err := AddInvoice(ctx, addInvoiceCfg, addInvoiceData)
|
||||||
|
@ -22,7 +22,7 @@ func decodePayReq(invoice *channeldb.Invoice,
|
|||||||
paymentRequest := string(invoice.PaymentRequest)
|
paymentRequest := string(invoice.PaymentRequest)
|
||||||
if paymentRequest == "" {
|
if paymentRequest == "" {
|
||||||
preimage := invoice.Terms.PaymentPreimage
|
preimage := invoice.Terms.PaymentPreimage
|
||||||
if preimage == channeldb.UnknownPreimage {
|
if preimage == nil {
|
||||||
return nil, errors.New("cannot reconstruct pay req")
|
return nil, errors.New("cannot reconstruct pay req")
|
||||||
}
|
}
|
||||||
hash := [32]byte(preimage.Hash())
|
hash := [32]byte(preimage.Hash())
|
||||||
@ -149,7 +149,7 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
IsKeysend: len(invoice.PaymentRequest) == 0,
|
IsKeysend: len(invoice.PaymentRequest) == 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if preimage != channeldb.UnknownPreimage {
|
if preimage != nil {
|
||||||
rpcInvoice.RPreimage = preimage[:]
|
rpcInvoice.RPreimage = preimage[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user