Merge pull request #4334 from joostjager/hold-keysend-part1

invoices: add explicit hodl invoice flag
This commit is contained in:
Joost Jager 2020-06-02 10:59:47 +02:00 committed by GitHub
commit 9f32942a90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 153 additions and 93 deletions

@ -20,7 +20,10 @@ var (
)
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 {
return nil, err
}
@ -32,7 +35,7 @@ func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
CreationDate: testNow,
Terms: ContractTerm{
Expiry: 4000,
PaymentPreimage: pre,
PaymentPreimage: &pre,
PaymentAddr: payAddr,
Value: value,
Features: emptyFeatures,
@ -360,13 +363,18 @@ func TestInvoiceCancelSingleHtlc(t *testing.T) {
t.Fatalf("unable to make test db: %v", err)
}
preimage := lntypes.Preimage{1}
paymentHash := preimage.Hash()
testInvoice := &Invoice{
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 {
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)
}
preimage := lntypes.Preimage{1}
paymentHash := preimage.Hash()
testInvoice := &Invoice{
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 {
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.

@ -17,9 +17,9 @@ import (
)
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.
UnknownPreimage lntypes.Preimage
unknownPreimage lntypes.Preimage
// invoiceBucket is the name of the bucket within the database that
// stores all data related to invoices no matter their final state.
@ -150,6 +150,7 @@ const (
featuresType tlv.Type = 11
invStateType tlv.Type = 12
amtPaidType tlv.Type = 13
hodlInvoiceType tlv.Type = 14
)
// 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
// occasion that an HTLC paying to the hash of this preimage is
// extended.
PaymentPreimage lntypes.Preimage
// extended. Set to nil if the preimage isn't known yet.
PaymentPreimage *lntypes.Preimage
// Value is the expected amount of milli-satoshis to be paid to an HTLC
// 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 may have been marked as canceled.
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.
@ -439,14 +444,19 @@ type InvoiceStateUpdateDesc struct {
NewState ContractState
// 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
// invoice.
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 {
return fmt.Errorf("max length a memo is %v, and invoice "+
"of length %v was provided", MaxMemoSize, len(i.Memo))
@ -459,6 +469,10 @@ func validateInvoice(i *Invoice) error {
if i.Terms.Features == nil {
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
}
@ -475,7 +489,7 @@ func (i *Invoice) IsPending() bool {
func (d *DB) AddInvoice(newInvoice *Invoice, paymentHash lntypes.Hash) (
uint64, error) {
if err := validateInvoice(newInvoice); err != nil {
if err := validateInvoice(newInvoice, paymentHash); err != nil {
return 0, err
}
@ -1131,7 +1145,13 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
}
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)
cltvDelta := uint32(i.Terms.FinalCltvDelta)
expiry := uint64(i.Terms.Expiry)
@ -1139,6 +1159,11 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
amtPaid := uint64(i.AmtPaid)
state := uint8(i.State)
var hodlInvoice uint8
if i.HodlInvoice {
hodlInvoice = 1
}
tlvStream, err := tlv.NewStream(
// Memo and payreq.
tlv.MakePrimitiveRecord(memoType, &i.Memo),
@ -1161,6 +1186,8 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
// Invoice state.
tlv.MakePrimitiveRecord(invStateType, &state),
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
)
if err != nil {
return err
@ -1256,12 +1283,13 @@ func fetchInvoice(invoiceNum []byte, invoices kvdb.RBucket) (Invoice, error) {
func deserializeInvoice(r io.Reader) (Invoice, error) {
var (
preimage [32]byte
value uint64
cltvDelta uint32
expiry uint64
amtPaid uint64
state uint8
preimageBytes [32]byte
value uint64
cltvDelta uint32
expiry uint64
amtPaid uint64
state uint8
hodlInvoice uint8
creationDateBytes []byte
settleDateBytes []byte
@ -1281,7 +1309,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
tlv.MakePrimitiveRecord(settleIndexType, &i.SettleIndex),
// Terms.
tlv.MakePrimitiveRecord(preimageType, &preimage),
tlv.MakePrimitiveRecord(preimageType, &preimageBytes),
tlv.MakePrimitiveRecord(valueType, &value),
tlv.MakePrimitiveRecord(cltvDeltaType, &cltvDelta),
tlv.MakePrimitiveRecord(expiryType, &expiry),
@ -1291,6 +1319,8 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
// Invoice state.
tlv.MakePrimitiveRecord(invStateType, &state),
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
)
if err != nil {
return i, err
@ -1307,13 +1337,21 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
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.FinalCltvDelta = int32(cltvDelta)
i.Terms.Expiry = time.Duration(expiry)
i.AmtPaid = lnwire.MilliSatoshi(amtPaid)
i.State = ContractState(state)
if hodlInvoice != 0 {
i.HodlInvoice = true
}
err = i.CreationDate.UnmarshalBinary(creationDateBytes)
if err != nil {
return i, err
@ -1443,10 +1481,16 @@ func copyInvoice(src *Invoice) *Invoice {
Htlcs: make(
map[CircuitKey]*InvoiceHTLC, len(src.Htlcs),
),
HodlInvoice: src.HodlInvoice,
}
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 {
dest.Htlcs[k] = copyInvoiceHTLC(v)
}
@ -1619,10 +1663,16 @@ func updateInvoiceState(invoice *Invoice, hash lntypes.Hash,
case ContractOpen:
if update.NewState == ContractSettled {
// Validate preimage.
if update.Preimage.Hash() != hash {
return ErrInvoicePreimageMismatch
switch {
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.

@ -1457,8 +1457,7 @@ func (c *ChannelArbitrator) isPreimageAvailable(hash lntypes.Hash) (bool,
return false, err
}
preimageAvailable = invoice.Terms.PaymentPreimage !=
channeldb.UnknownPreimage
preimageAvailable = invoice.Terms.PaymentPreimage != nil
return preimageAvailable, nil
}

@ -437,7 +437,9 @@ func TestChannelLinkCancelFullCommitment(t *testing.T) {
var wg sync.WaitGroup
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)
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,
// then the bandwidth should remain unchanged as the remote party will
// 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 {
t.Fatalf("unable to settle htlc: %v", err)
}
htlcSettle := &lnwire.UpdateFulfillHTLC{
ID: 0,
PaymentPreimage: invoice.Terms.PaymentPreimage,
PaymentPreimage: *invoice.Terms.PaymentPreimage,
}
aliceLink.HandleChannelUpdate(htlcSettle)
time.Sleep(time.Millisecond * 500)
@ -2193,7 +2195,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
outgoingHTLCID: addPkt.outgoingHTLCID,
htlc: &lnwire.UpdateFulfillHTLC{
ID: 0,
PaymentPreimage: invoice.Terms.PaymentPreimage,
PaymentPreimage: *invoice.Terms.PaymentPreimage,
},
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,
// then the bandwidth should remain unchanged as the remote party will
// 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 {
t.Fatalf("unable to settle htlc: %v", err)
}
htlcSettle := &lnwire.UpdateFulfillHTLC{
ID: bobIndex,
PaymentPreimage: invoice.Terms.PaymentPreimage,
PaymentPreimage: *invoice.Terms.PaymentPreimage,
}
aliceLink.HandleChannelUpdate(htlcSettle)
time.Sleep(time.Millisecond * 500)
@ -4730,7 +4732,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
for i, invoice := range invoices {
ctx.sendSettleBobToAlice(
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.
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
// expects this payment.
@ -5814,7 +5817,10 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
<-registry.settleChan
// 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.
ctx.receiveSettleAliceToBob()
@ -5957,10 +5963,12 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
// Convert into hodl invoices and save the preimages for later.
preimage1 := invoice1.Terms.PaymentPreimage
invoice1.Terms.PaymentPreimage = channeldb.UnknownPreimage
invoice1.Terms.PaymentPreimage = nil
invoice1.HodlInvoice = true
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
// expects the payments.
@ -6009,7 +6017,10 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
}
// 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
// not yet send the revocation.
@ -6017,7 +6028,10 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
ctx.receiveCommitSigAliceToBob(1)
// 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.
ctx.receiveSettleAliceToBob()

@ -547,8 +547,8 @@ func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
// invoice which should be added by destination peer.
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
timelock uint32, blob [lnwire.OnionPacketSize]byte,
preimage, rhash, payAddr [32]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC,
uint64, error) {
preimage *lntypes.Preimage, rhash, payAddr [32]byte) (
*channeldb.Invoice, *lnwire.UpdateAddHTLC, uint64, error) {
// Create the db invoice. Normally the payment requests needs to be set,
// 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
// don't need to bother here with creating and signing a payment
// request.
invoice := &channeldb.Invoice{
CreationDate: time.Now(),
Terms: channeldb.ContractTerm{
@ -567,6 +568,7 @@ func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
nil, lnwire.Features,
),
},
HodlInvoice: preimage == nil,
}
htlc := &lnwire.UpdateAddHTLC{
@ -591,7 +593,7 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice,
*lnwire.UpdateAddHTLC, uint64, error) {
var preimage [sha256.Size]byte
var preimage lntypes.Preimage
r, err := generateRandomBytes(sha256.Size)
if err != nil {
return nil, nil, 0, err
@ -608,7 +610,7 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
copy(payAddr[:], r)
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.
invoice, htlc, pid, err := generatePaymentWithPreimage(
invoiceAmt, htlcAmt, timelock, blob,
channeldb.UnknownPreimage, rhash, payAddr,
nil, rhash, payAddr,
)
if err != nil {
paymentErr <- err

@ -652,12 +652,6 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
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.
if ctx.mpp != nil {
return errors.New("no mpp keysend supported")
@ -688,7 +682,7 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
Terms: channeldb.ContractTerm{
FinalCltvDelta: finalCltvDelta,
Value: amt,
PaymentPreimage: preimage,
PaymentPreimage: &preimage,
Features: features,
},
}
@ -948,7 +942,7 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
return &channeldb.InvoiceUpdateDesc{
State: &channeldb.InvoiceStateUpdateDesc{
NewState: channeldb.ContractSettled,
Preimage: preimage,
Preimage: &preimage,
},
}, nil
}

@ -92,7 +92,7 @@ var (
testInvoiceAmt = lnwire.MilliSatoshi(100000)
testInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: testInvoicePreimage,
PaymentPreimage: &testInvoicePreimage,
Value: testInvoiceAmt,
Expiry: time.Hour,
Features: testFeatures,
@ -102,12 +102,12 @@ var (
testHodlInvoice = &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: channeldb.UnknownPreimage,
Value: testInvoiceAmt,
Expiry: time.Hour,
Features: testFeatures,
Value: testInvoiceAmt,
Expiry: time.Hour,
Features: testFeatures,
},
CreationDate: testInvoiceCreationDate,
HodlInvoice: true,
}
)
@ -225,7 +225,7 @@ func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
return &channeldb.Invoice{
Terms: channeldb.ContractTerm{
PaymentPreimage: preimage,
PaymentPreimage: &preimage,
PaymentAddr: payAddr,
Value: testInvoiceAmount,
Expiry: expiry,

@ -81,7 +81,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
case channeldb.HtlcStateSettled:
return nil, ctx.settleRes(
inv.Terms.PaymentPreimage,
*inv.Terms.PaymentPreimage,
ResultReplayToSettled,
), nil
@ -187,8 +187,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
// Check to see if we can settle or this is an hold invoice and
// we need to wait for the preimage.
holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage
if holdInvoice {
if inv.HodlInvoice {
update.State = &channeldb.InvoiceStateUpdateDesc{
NewState: channeldb.ContractAccepted,
}
@ -201,7 +200,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
}
return &update, ctx.settleRes(
inv.Terms.PaymentPreimage, ResultSettled,
*inv.Terms.PaymentPreimage, ResultSettled,
), nil
}
@ -269,14 +268,13 @@ func updateLegacy(ctx *invoiceUpdateCtx,
case channeldb.ContractSettled:
return &update, ctx.settleRes(
inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
), nil
}
// Check to see if we can settle or this is an hold invoice and we need
// to wait for the preimage.
holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage
if holdInvoice {
if inv.HodlInvoice {
update.State = &channeldb.InvoiceStateUpdateDesc{
NewState: channeldb.ContractAccepted,
}
@ -290,6 +288,6 @@ func updateLegacy(ctx *invoiceUpdateCtx,
}
return &update, ctx.settleRes(
inv.Terms.PaymentPreimage, ResultSettled,
*inv.Terms.PaymentPreimage, ResultSettled,
), nil
}

@ -88,6 +88,10 @@ type AddInvoiceData struct {
// Whether this invoice should include routing hints for private
// channels.
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
@ -97,7 +101,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {
var (
paymentPreimage lntypes.Preimage
paymentPreimage *lntypes.Preimage
paymentHash lntypes.Hash
)
@ -108,26 +112,9 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
return nil, nil,
errors.New("preimage and hash both set")
// Prevent the unknown preimage magic value from being used for a
// regular invoice. This would cause the invoice the be handled as if it
// was a hold invoice.
case invoice.Preimage != nil &&
*invoice.Preimage == channeldb.UnknownPreimage:
return nil, nil,
fmt.Errorf("cannot use all zeroes as a preimage")
// Prevent the hash of the unknown preimage magic value to be used for a
// hold invoice. This would make it impossible to settle the invoice,
// because it would still be interpreted as not having a preimage.
case invoice.Hash != nil &&
*invoice.Hash == channeldb.UnknownPreimage.Hash():
return nil, nil,
fmt.Errorf("cannot use hash of all zeroes preimage")
// If no hash or preimage is given, generate a random preimage.
case invoice.Preimage == nil && invoice.Hash == nil:
paymentPreimage = &lntypes.Preimage{}
if _, err := rand.Read(paymentPreimage[:]); err != nil {
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
// preimage to unknown.
case invoice.Preimage == nil && invoice.Hash != nil:
paymentPreimage = channeldb.UnknownPreimage
paymentHash = *invoice.Hash
// A specific preimage was supplied. Use that for the invoice.
case invoice.Preimage != nil && invoice.Hash == nil:
paymentPreimage = *invoice.Preimage
preimage := *invoice.Preimage
paymentPreimage = &preimage
paymentHash = invoice.Preimage.Hash()
}
@ -410,6 +397,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
PaymentAddr: paymentAddr,
Features: invoiceFeatures,
},
HodlInvoice: invoice.HodlInvoice,
}
log.Tracef("[addinvoice] adding new invoice %v",

@ -274,6 +274,8 @@ func (s *Server) AddHoldInvoice(ctx context.Context,
FallbackAddr: invoice.FallbackAddr,
CltvExpiry: invoice.CltvExpiry,
Private: invoice.Private,
HodlInvoice: true,
Preimage: nil,
}
_, dbInvoice, err := AddInvoice(ctx, addInvoiceCfg, addInvoiceData)

@ -22,7 +22,7 @@ func decodePayReq(invoice *channeldb.Invoice,
paymentRequest := string(invoice.PaymentRequest)
if paymentRequest == "" {
preimage := invoice.Terms.PaymentPreimage
if preimage == channeldb.UnknownPreimage {
if preimage == nil {
return nil, errors.New("cannot reconstruct pay req")
}
hash := [32]byte(preimage.Hash())
@ -149,7 +149,7 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
IsKeysend: len(invoice.PaymentRequest) == 0,
}
if preimage != channeldb.UnknownPreimage {
if preimage != nil {
rpcInvoice.RPreimage = preimage[:]
}