mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-03 17:26:57 +01:00
Merge pull request #7576 from lightningnetwork/0-17-0-staging
lnd: merge v0.17.0 staging branch into master
This commit is contained in:
commit
f9f08079d0
6 changed files with 372 additions and 96 deletions
|
@ -461,7 +461,8 @@ func TestInvoiceCancelSingleHtlc(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
AddHtlcs: htlcs,
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
|
AddHtlcs: htlcs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +480,7 @@ func TestInvoiceCancelSingleHtlc(t *testing.T) {
|
||||||
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
UpdateType: invpkg.CancelHTLCsUpdate,
|
||||||
CancelHtlcs: map[models.CircuitKey]struct{}{
|
CancelHtlcs: map[models.CircuitKey]struct{}{
|
||||||
key: {},
|
key: {},
|
||||||
},
|
},
|
||||||
|
@ -554,6 +556,7 @@ func TestInvoiceCancelSingleHtlcAMP(t *testing.T) {
|
||||||
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
UpdateType: invpkg.CancelHTLCsUpdate,
|
||||||
CancelHtlcs: map[models.CircuitKey]struct{}{
|
CancelHtlcs: map[models.CircuitKey]struct{}{
|
||||||
{HtlcID: 0}: {},
|
{HtlcID: 0}: {},
|
||||||
},
|
},
|
||||||
|
@ -616,6 +619,7 @@ func TestInvoiceCancelSingleHtlcAMP(t *testing.T) {
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
UpdateType: invpkg.CancelHTLCsUpdate,
|
||||||
CancelHtlcs: map[models.CircuitKey]struct{}{
|
CancelHtlcs: map[models.CircuitKey]struct{}{
|
||||||
{HtlcID: 1}: {},
|
{HtlcID: 1}: {},
|
||||||
},
|
},
|
||||||
|
@ -643,6 +647,7 @@ func TestInvoiceCancelSingleHtlcAMP(t *testing.T) {
|
||||||
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
UpdateType: invpkg.CancelHTLCsUpdate,
|
||||||
CancelHtlcs: map[models.CircuitKey]struct{}{
|
CancelHtlcs: map[models.CircuitKey]struct{}{
|
||||||
{HtlcID: 2}: {},
|
{HtlcID: 2}: {},
|
||||||
},
|
},
|
||||||
|
@ -1529,6 +1534,7 @@ func getUpdateInvoice(amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
update := &invpkg.InvoiceUpdateDesc{
|
update := &invpkg.InvoiceUpdateDesc{
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
State: &invpkg.InvoiceStateUpdateDesc{
|
State: &invpkg.InvoiceStateUpdateDesc{
|
||||||
Preimage: invoice.Terms.PaymentPreimage,
|
Preimage: invoice.Terms.PaymentPreimage,
|
||||||
NewState: invpkg.ContractSettled,
|
NewState: invpkg.ContractSettled,
|
||||||
|
@ -1586,7 +1592,10 @@ func TestCustomRecords(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{AddHtlcs: htlcs}, nil
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
AddHtlcs: htlcs,
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.UpdateInvoice(ref, nil, callback)
|
_, err = db.UpdateInvoice(ref, nil, callback)
|
||||||
|
@ -1663,7 +1672,10 @@ func testInvoiceHtlcAMPFields(t *testing.T, isAMP bool) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &invpkg.InvoiceUpdateDesc{AddHtlcs: htlcs}, nil
|
return &invpkg.InvoiceUpdateDesc{
|
||||||
|
AddHtlcs: htlcs,
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := invpkg.InvoiceRefByHash(payHash)
|
ref := invpkg.InvoiceRefByHash(payHash)
|
||||||
|
@ -2128,8 +2140,9 @@ func updateAcceptAMPHtlc(id uint64, amt lnwire.MilliSatoshi,
|
||||||
}
|
}
|
||||||
|
|
||||||
update := &invpkg.InvoiceUpdateDesc{
|
update := &invpkg.InvoiceUpdateDesc{
|
||||||
State: state,
|
State: state,
|
||||||
AddHtlcs: htlcs,
|
AddHtlcs: htlcs,
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
return update, nil
|
return update, nil
|
||||||
|
@ -2152,6 +2165,10 @@ func getUpdateInvoiceAMPSettle(setID *[32]byte, preimage [32]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
update := &invpkg.InvoiceUpdateDesc{
|
update := &invpkg.InvoiceUpdateDesc{
|
||||||
|
// TODO(positiveblue): this would be an invalid update
|
||||||
|
// because tires to settle an AMP invoice without adding
|
||||||
|
// any new htlc.
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
State: &invpkg.InvoiceStateUpdateDesc{
|
State: &invpkg.InvoiceStateUpdateDesc{
|
||||||
Preimage: nil,
|
Preimage: nil,
|
||||||
NewState: invpkg.ContractSettled,
|
NewState: invpkg.ContractSettled,
|
||||||
|
@ -2272,12 +2289,16 @@ func testUpdateHTLCPreimages(t *testing.T, test updateHTLCPreimageTestCase) {
|
||||||
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
update := &invpkg.InvoiceUpdateDesc{
|
update := &invpkg.InvoiceUpdateDesc{
|
||||||
|
// TODO(positiveblue): this would be an invalid update
|
||||||
|
// because tires to settle an AMP invoice without adding
|
||||||
|
// any new htlc.
|
||||||
State: &invpkg.InvoiceStateUpdateDesc{
|
State: &invpkg.InvoiceStateUpdateDesc{
|
||||||
Preimage: nil,
|
Preimage: nil,
|
||||||
NewState: invpkg.ContractSettled,
|
NewState: invpkg.ContractSettled,
|
||||||
HTLCPreimages: htlcPreimages,
|
HTLCPreimages: htlcPreimages,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
},
|
},
|
||||||
|
UpdateType: invpkg.AddHTLCsUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
return update, nil
|
return update, nil
|
||||||
|
|
|
@ -1858,7 +1858,7 @@ func settleHtlcsAmp(invoice *invpkg.Invoice,
|
||||||
|
|
||||||
// updateInvoice fetches the invoice, obtains the update descriptor from the
|
// updateInvoice fetches the invoice, obtains the update descriptor from the
|
||||||
// callback and applies the updates in a single db transaction.
|
// callback and applies the updates in a single db transaction.
|
||||||
func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices, //nolint:lll,funlen
|
func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
settleIndex, setIDIndex kvdb.RwBucket, invoiceNum []byte,
|
settleIndex, setIDIndex kvdb.RwBucket, invoiceNum []byte,
|
||||||
callback invpkg.InvoiceUpdateCallback) (*invpkg.Invoice, error) {
|
callback invpkg.InvoiceUpdateCallback) (*invpkg.Invoice, error) {
|
||||||
|
|
||||||
|
@ -1893,27 +1893,128 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
return &invoice, nil
|
return &invoice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
switch update.UpdateType {
|
||||||
newState = invoice.State
|
case invpkg.CancelHTLCsUpdate:
|
||||||
setID *[32]byte
|
return d.cancelHTLCs(invoices, invoiceNum, &invoice, update)
|
||||||
)
|
|
||||||
|
|
||||||
// We can either get the set ID from the main state update (if the
|
case invpkg.AddHTLCsUpdate:
|
||||||
// state is changing), or via the hint passed in returned by the update
|
return d.addHTLCs(
|
||||||
// call back.
|
invoices, settleIndex, setIDIndex, invoiceNum, &invoice,
|
||||||
if update.State != nil {
|
hash, update,
|
||||||
setID = update.State.SetID
|
)
|
||||||
newState = update.State.NewState
|
|
||||||
} else if update.SetID != nil {
|
case invpkg.SettleHodlInvoiceUpdate:
|
||||||
// When we go to cancel HTLCs, there's no new state, but the
|
return d.settleHodlInvoice(
|
||||||
// set of HTLCs to be cancelled along with the setID affected
|
invoices, settleIndex, invoiceNum, &invoice, hash,
|
||||||
// will be passed in.
|
update.State,
|
||||||
setID = (*[32]byte)(update.SetID)
|
)
|
||||||
|
|
||||||
|
case invpkg.CancelInvoiceUpdate:
|
||||||
|
return d.cancelInvoice(
|
||||||
|
invoices, invoiceNum, &invoice, hash, update.State,
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown update type: %s",
|
||||||
|
update.UpdateType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancelHTLCs tries to cancel the htlcs in the given InvoiceUpdateDesc.
|
||||||
|
//
|
||||||
|
// NOTE: cancelHTLCs updates will only use the `CancelHtlcs` field in the
|
||||||
|
// InvoiceUpdateDesc.
|
||||||
|
func (d *DB) cancelHTLCs(invoices kvdb.RwBucket, invoiceNum []byte,
|
||||||
|
invoice *invpkg.Invoice,
|
||||||
|
update *invpkg.InvoiceUpdateDesc) (*invpkg.Invoice, error) {
|
||||||
|
|
||||||
|
timestamp := d.clock.Now()
|
||||||
|
|
||||||
|
// Process add actions from update descriptor.
|
||||||
|
htlcsAmpUpdate := make(map[invpkg.SetID]map[models.CircuitKey]*invpkg.InvoiceHTLC) //nolint:lll
|
||||||
|
|
||||||
|
// Process cancel actions from update descriptor.
|
||||||
|
cancelHtlcs := update.CancelHtlcs
|
||||||
|
for key, htlc := range invoice.Htlcs {
|
||||||
|
htlc := htlc
|
||||||
|
|
||||||
|
// Check whether this htlc needs to be canceled. If it does,
|
||||||
|
// update the htlc state to Canceled.
|
||||||
|
_, cancel := cancelHtlcs[key]
|
||||||
|
if !cancel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cancelSingleHtlc(timestamp, htlc, invoice.State)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete processed cancel action, so that we can check later
|
||||||
|
// that there are no actions left.
|
||||||
|
delete(cancelHtlcs, key)
|
||||||
|
|
||||||
|
// Tally this into the set of HTLCs that need to be updated on
|
||||||
|
// disk, but once again, only if this is an AMP invoice.
|
||||||
|
if invoice.IsAMP() {
|
||||||
|
cancelHtlcsAmp(
|
||||||
|
invoice, htlcsAmpUpdate, htlc, key,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
now := d.clock.Now()
|
// Verify that we didn't get an action for htlcs that are not present on
|
||||||
|
// the invoice.
|
||||||
|
if len(cancelHtlcs) > 0 {
|
||||||
|
return nil, errors.New("cancel action on non-existent htlc(s)")
|
||||||
|
}
|
||||||
|
|
||||||
invoiceIsAMP := invoiceCopy.IsAMP()
|
err := d.serializeAndStoreInvoice(invoices, invoiceNum, invoice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is an AMP invoice, then we'll actually store the rest of the
|
||||||
|
// HTLCs in-line with the invoice, using the invoice ID as a prefix,
|
||||||
|
// and the AMP key as a suffix: invoiceNum || setID.
|
||||||
|
if invoice.IsAMP() {
|
||||||
|
err := updateAMPInvoices(invoices, invoiceNum, htlcsAmpUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeAndStoreInvoice is a helper function used to store invoices.
|
||||||
|
func (d *DB) serializeAndStoreInvoice(invoices kvdb.RwBucket, invoiceNum []byte,
|
||||||
|
invoice *invpkg.Invoice) error {
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := serializeInvoice(&buf, invoice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := invoices.Put(invoiceNum, buf.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addHTLCs tries to add the htlcs in the given InvoiceUpdateDesc.
|
||||||
|
func (d *DB) addHTLCs(invoices, settleIndex, //nolint:funlen
|
||||||
|
setIDIndex kvdb.RwBucket, invoiceNum []byte, invoice *invpkg.Invoice,
|
||||||
|
hash *lntypes.Hash, update *invpkg.InvoiceUpdateDesc) (*invpkg.Invoice,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
var setID *[32]byte
|
||||||
|
invoiceIsAMP := invoice.IsAMP()
|
||||||
|
if invoiceIsAMP && update.State != nil {
|
||||||
|
setID = update.State.SetID
|
||||||
|
}
|
||||||
|
timestamp := d.clock.Now()
|
||||||
|
|
||||||
// Process add actions from update descriptor.
|
// Process add actions from update descriptor.
|
||||||
htlcsAmpUpdate := make(map[invpkg.SetID]map[models.CircuitKey]*invpkg.InvoiceHTLC) //nolint:lll
|
htlcsAmpUpdate := make(map[invpkg.SetID]map[models.CircuitKey]*invpkg.InvoiceHTLC) //nolint:lll
|
||||||
|
@ -1928,21 +2029,26 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
return nil, errors.New("nil custom records map")
|
return nil, errors.New("nil custom records map")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a newly added HTLC has an associated set id, use it to
|
if invoiceIsAMP {
|
||||||
// index this invoice in the set id index. An error is returned
|
if htlcUpdate.AMP == nil {
|
||||||
// if we find the index already points to a different invoice.
|
return nil, fmt.Errorf("unable to add htlc "+
|
||||||
var setID [32]byte
|
"without AMP data to AMP invoice(%v)",
|
||||||
if htlcUpdate.AMP != nil {
|
invoice.AddIndex)
|
||||||
setID = htlcUpdate.AMP.Record.SetID()
|
}
|
||||||
setIDInvNum := setIDIndex.Get(setID[:])
|
|
||||||
|
// Check if this SetID already exist.
|
||||||
|
htlcSetID := htlcUpdate.AMP.Record.SetID()
|
||||||
|
setIDInvNum := setIDIndex.Get(htlcSetID[:])
|
||||||
|
|
||||||
if setIDInvNum == nil {
|
if setIDInvNum == nil {
|
||||||
err = setIDIndex.Put(setID[:], invoiceNum)
|
err := setIDIndex.Put(htlcSetID[:], invoiceNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if !bytes.Equal(setIDInvNum, invoiceNum) {
|
} else if !bytes.Equal(setIDInvNum, invoiceNum) {
|
||||||
err = invpkg.ErrDuplicateSetID{SetID: setID}
|
return nil, invpkg.ErrDuplicateSetID{
|
||||||
return nil, err
|
SetID: htlcSetID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1951,7 +2057,7 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
MppTotalAmt: htlcUpdate.MppTotalAmt,
|
MppTotalAmt: htlcUpdate.MppTotalAmt,
|
||||||
Expiry: htlcUpdate.Expiry,
|
Expiry: htlcUpdate.Expiry,
|
||||||
AcceptHeight: uint32(htlcUpdate.AcceptHeight),
|
AcceptHeight: uint32(htlcUpdate.AcceptHeight),
|
||||||
AcceptTime: now,
|
AcceptTime: timestamp,
|
||||||
State: invpkg.HtlcStateAccepted,
|
State: invpkg.HtlcStateAccepted,
|
||||||
CustomRecords: htlcUpdate.CustomRecords,
|
CustomRecords: htlcUpdate.CustomRecords,
|
||||||
AMP: htlcUpdate.AMP.Copy(),
|
AMP: htlcUpdate.AMP.Copy(),
|
||||||
|
@ -1963,53 +2069,12 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
// below, but only if this is an AMP invoice.
|
// below, but only if this is an AMP invoice.
|
||||||
if invoiceIsAMP {
|
if invoiceIsAMP {
|
||||||
updateHtlcsAmp(
|
updateHtlcsAmp(
|
||||||
&invoice, htlcsAmpUpdate, htlc, setID, key,
|
invoice, htlcsAmpUpdate, htlc,
|
||||||
|
htlcUpdate.AMP.Record.SetID(), key,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process cancel actions from update descriptor.
|
|
||||||
cancelHtlcs := update.CancelHtlcs
|
|
||||||
for key, htlc := range invoice.Htlcs {
|
|
||||||
htlc := htlc
|
|
||||||
|
|
||||||
// Check whether this htlc needs to be canceled. If it does,
|
|
||||||
// update the htlc state to Canceled.
|
|
||||||
_, cancel := cancelHtlcs[key]
|
|
||||||
if !cancel {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consistency check to verify that there is no overlap between
|
|
||||||
// the add and cancel sets.
|
|
||||||
if _, added := update.AddHtlcs[key]; added {
|
|
||||||
return nil, fmt.Errorf("added htlc %v canceled", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cancelSingleHtlc(now, htlc, newState)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete processed cancel action, so that we can check later
|
|
||||||
// that there are no actions left.
|
|
||||||
delete(cancelHtlcs, key)
|
|
||||||
|
|
||||||
// Tally this into the set of HTLCs that need to be updated on
|
|
||||||
// disk, but once again, only if this is an AMP invoice.
|
|
||||||
if invoiceIsAMP {
|
|
||||||
cancelHtlcsAmp(
|
|
||||||
&invoice, htlcsAmpUpdate, htlc, key,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that we didn't get an action for htlcs that are not present on
|
|
||||||
// the invoice.
|
|
||||||
if len(cancelHtlcs) > 0 {
|
|
||||||
return nil, errors.New("cancel action on non-existent htlc(s)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the set of accepted HTLCs should be fully
|
// At this point, the set of accepted HTLCs should be fully
|
||||||
// populated with added HTLCs or removed of canceled ones. Update
|
// populated with added HTLCs or removed of canceled ones. Update
|
||||||
// invoice state if the update descriptor indicates an invoice state
|
// invoice state if the update descriptor indicates an invoice state
|
||||||
|
@ -2017,7 +2082,7 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
// HTLCs.
|
// HTLCs.
|
||||||
if update.State != nil {
|
if update.State != nil {
|
||||||
newState, err := updateInvoiceState(
|
newState, err := updateInvoiceState(
|
||||||
&invoice, hash, *update.State,
|
invoice, hash, *update.State,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2039,7 +2104,8 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
isSettled := update.State.NewState == invpkg.ContractSettled
|
isSettled := update.State.NewState == invpkg.ContractSettled
|
||||||
if !invoiceIsAMP && isSettled {
|
if !invoiceIsAMP && isSettled {
|
||||||
err := setSettleMetaFields(
|
err := setSettleMetaFields(
|
||||||
settleIndex, invoiceNum, &invoice, now, nil,
|
settleIndex, invoiceNum, invoice, timestamp,
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2093,7 +2159,7 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
htlcContextState = invpkg.ContractSettled
|
htlcContextState = invpkg.ContractSettled
|
||||||
}
|
}
|
||||||
htlcSettled, err := updateHtlc(
|
htlcSettled, err := updateHtlc(
|
||||||
now, htlc, htlcContextState, setID,
|
timestamp, htlc, htlcContextState, setID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2104,7 +2170,7 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
// meta data state.
|
// meta data state.
|
||||||
if htlcSettled && invoiceIsAMP {
|
if htlcSettled && invoiceIsAMP {
|
||||||
settleHtlcsAmp(
|
settleHtlcsAmp(
|
||||||
&invoice, settledSetIDs, htlcsAmpUpdate, htlc,
|
invoice, settledSetIDs, htlcsAmpUpdate, htlc,
|
||||||
key,
|
key,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2156,20 +2222,16 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
for settledSetID := range settledSetIDs {
|
for settledSetID := range settledSetIDs {
|
||||||
settledSetID := settledSetID
|
settledSetID := settledSetID
|
||||||
err := setSettleMetaFields(
|
err := setSettleMetaFields(
|
||||||
settleIndex, invoiceNum, &invoice, now, &settledSetID,
|
settleIndex, invoiceNum, invoice, timestamp,
|
||||||
|
&settledSetID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserialize and update invoice.
|
err := d.serializeAndStoreInvoice(invoices, invoiceNum, invoice)
|
||||||
var buf bytes.Buffer
|
if err != nil {
|
||||||
if err := serializeInvoice(&buf, &invoice); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := invoices.Put(invoiceNum[:], buf.Bytes()); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2183,7 +2245,137 @@ func (d *DB) updateInvoice(hash *lntypes.Hash, refSetID *invpkg.SetID, invoices,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &invoice, nil
|
return invoice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// settleHodlInvoice marks a hodl invoice as settled.
|
||||||
|
//
|
||||||
|
// NOTE: Currently it is not possible to have HODL AMP invoices.
|
||||||
|
func (d *DB) settleHodlInvoice(invoices, settleIndex kvdb.RwBucket,
|
||||||
|
invoiceNum []byte, invoice *invpkg.Invoice, hash *lntypes.Hash,
|
||||||
|
update *invpkg.InvoiceStateUpdateDesc) (*invpkg.Invoice, error) {
|
||||||
|
|
||||||
|
if !invoice.HodlInvoice {
|
||||||
|
return nil, fmt.Errorf("unable to settle hodl invoice: %v is "+
|
||||||
|
"not a hodl invoice", invoice.AddIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(positiveblue): because NewState can only be ContractSettled we
|
||||||
|
// can remove it from the API and set it here directly.
|
||||||
|
switch {
|
||||||
|
case update == nil:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case update.NewState != invpkg.ContractSettled:
|
||||||
|
return nil, fmt.Errorf("unable to settle hodl invoice: "+
|
||||||
|
"not valid InvoiceUpdateDesc.State: %v", update)
|
||||||
|
|
||||||
|
case update.Preimage == nil:
|
||||||
|
return nil, fmt.Errorf("unable to settle hodl invoice: " +
|
||||||
|
"preimage is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(positiveblue): create a invoice.CanSettleHodlInvoice func.
|
||||||
|
newState, err := updateInvoiceState(invoice, hash, *update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if newState == nil || *newState != invpkg.ContractSettled {
|
||||||
|
return nil, fmt.Errorf("unable to settle hodl invoice: "+
|
||||||
|
"new computed state is not settled: %s", newState)
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.State = invpkg.ContractSettled
|
||||||
|
timestamp := d.clock.Now()
|
||||||
|
|
||||||
|
err = setSettleMetaFields(
|
||||||
|
settleIndex, invoiceNum, invoice, timestamp, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(positiveblue): this logic can be further simplified.
|
||||||
|
var amtPaid lnwire.MilliSatoshi
|
||||||
|
for _, htlc := range invoice.Htlcs {
|
||||||
|
_, err := updateHtlc(
|
||||||
|
timestamp, htlc, invpkg.ContractSettled, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if htlc.State == invpkg.HtlcStateSettled {
|
||||||
|
amtPaid += htlc.Amt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.AmtPaid = amtPaid
|
||||||
|
|
||||||
|
err = d.serializeAndStoreInvoice(invoices, invoiceNum, invoice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancelInvoice attempts to cancel the given invoice. That includes changing
|
||||||
|
// the invoice state and the state of any relevant HTLC.
|
||||||
|
func (d *DB) cancelInvoice(invoices kvdb.RwBucket, invoiceNum []byte,
|
||||||
|
invoice *invpkg.Invoice, hash *lntypes.Hash,
|
||||||
|
update *invpkg.InvoiceStateUpdateDesc) (*invpkg.Invoice, error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case update == nil:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case update.NewState != invpkg.ContractCanceled:
|
||||||
|
return nil, fmt.Errorf("unable to cancel invoice: "+
|
||||||
|
"InvoiceUpdateDesc.State not valid: %v", update)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
setID *[32]byte
|
||||||
|
invoiceIsAMP bool
|
||||||
|
)
|
||||||
|
|
||||||
|
invoiceIsAMP = invoice.IsAMP()
|
||||||
|
if invoiceIsAMP {
|
||||||
|
setID = update.SetID
|
||||||
|
}
|
||||||
|
|
||||||
|
newState, err := updateInvoiceState(invoice, hash, *update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if newState == nil || *newState != invpkg.ContractCanceled {
|
||||||
|
return nil, fmt.Errorf("unable to cancel invoice(%v): new "+
|
||||||
|
"computed state is not canceled: %s", invoice.AddIndex,
|
||||||
|
newState)
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.State = invpkg.ContractCanceled
|
||||||
|
timestamp := d.clock.Now()
|
||||||
|
|
||||||
|
// TODO(positiveblue): this logic can be simplified.
|
||||||
|
for _, htlc := range invoice.Htlcs {
|
||||||
|
_, err := updateHtlc(
|
||||||
|
timestamp, htlc, invpkg.ContractCanceled, setID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.serializeAndStoreInvoice(invoices, invoiceNum, invoice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateInvoiceState validates and processes an invoice state update. The new
|
// updateInvoiceState validates and processes an invoice state update. The new
|
||||||
|
|
12
docs/release-notes/release-notes-0.17.0.md
Normal file
12
docs/release-notes/release-notes-0.17.0.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Release Notes
|
||||||
|
|
||||||
|
## DB
|
||||||
|
|
||||||
|
* Split channeldb [`UpdateInvoice`
|
||||||
|
implementation](https://github.com/lightningnetwork/lnd/pull/7377) logic in
|
||||||
|
different update types.
|
||||||
|
|
||||||
|
# Contributors (Alphabetical Order)
|
||||||
|
|
||||||
|
* Jordi Montes
|
||||||
|
|
|
@ -711,6 +711,7 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef InvoiceRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
|
UpdateType: CancelHTLCsUpdate,
|
||||||
CancelHtlcs: canceledHtlcs,
|
CancelHtlcs: canceledHtlcs,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -1230,9 +1231,7 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
||||||
i.Lock()
|
i.Lock()
|
||||||
defer i.Unlock()
|
defer i.Unlock()
|
||||||
|
|
||||||
updateInvoice := func(invoice *Invoice) (
|
updateInvoice := func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
||||||
*InvoiceUpdateDesc, error) {
|
|
||||||
|
|
||||||
switch invoice.State {
|
switch invoice.State {
|
||||||
case ContractOpen:
|
case ContractOpen:
|
||||||
return nil, ErrInvoiceStillOpen
|
return nil, ErrInvoiceStillOpen
|
||||||
|
@ -1245,6 +1244,7 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
|
UpdateType: SettleHodlInvoiceUpdate,
|
||||||
State: &InvoiceStateUpdateDesc{
|
State: &InvoiceStateUpdateDesc{
|
||||||
NewState: ContractSettled,
|
NewState: ContractSettled,
|
||||||
Preimage: &preimage,
|
Preimage: &preimage,
|
||||||
|
@ -1329,6 +1329,7 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
||||||
// channeldb to return an error if the invoice is already
|
// channeldb to return an error if the invoice is already
|
||||||
// settled or canceled.
|
// settled or canceled.
|
||||||
return &InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
|
UpdateType: CancelInvoiceUpdate,
|
||||||
State: &InvoiceStateUpdateDesc{
|
State: &InvoiceStateUpdateDesc{
|
||||||
NewState: ContractCanceled,
|
NewState: ContractCanceled,
|
||||||
},
|
},
|
||||||
|
|
|
@ -656,6 +656,51 @@ type HtlcAcceptDesc struct {
|
||||||
AMP *InvoiceHtlcAMPData
|
AMP *InvoiceHtlcAMPData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateType is an enum that describes the type of update that was applied to
|
||||||
|
// an invoice.
|
||||||
|
type UpdateType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnknownUpdate indicates that the UpdateType has not been set for a
|
||||||
|
// given udpate. This kind of updates are not allowed.
|
||||||
|
UnknownUpdate UpdateType = iota
|
||||||
|
|
||||||
|
// CancelHTLCsUpdate indicates that this update cancels one or more
|
||||||
|
// HTLCs.
|
||||||
|
CancelHTLCsUpdate
|
||||||
|
|
||||||
|
// AddHTLCsUpdate indicates that this update adds one or more HTLCs.
|
||||||
|
AddHTLCsUpdate
|
||||||
|
|
||||||
|
// SettleHodlInvoiceUpdate indicates that this update settles one or
|
||||||
|
// more HTLCs from a hodl invoice.
|
||||||
|
SettleHodlInvoiceUpdate
|
||||||
|
|
||||||
|
// CancelInvoiceUpdate indicates that this update is trying to cancel
|
||||||
|
// an invoice.
|
||||||
|
CancelInvoiceUpdate
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a human readable string for the UpdateType.
|
||||||
|
func (u UpdateType) String() string {
|
||||||
|
switch u {
|
||||||
|
case CancelHTLCsUpdate:
|
||||||
|
return "CancelHTLCsUpdate"
|
||||||
|
|
||||||
|
case AddHTLCsUpdate:
|
||||||
|
return "AddHTLCsUpdate"
|
||||||
|
|
||||||
|
case SettleHodlInvoiceUpdate:
|
||||||
|
return "SettleHodlInvoiceUpdate"
|
||||||
|
|
||||||
|
case CancelInvoiceUpdate:
|
||||||
|
return "CancelInvoiceUpdate"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("unknown invoice update type: %d", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// InvoiceUpdateDesc describes the changes that should be applied to the
|
// InvoiceUpdateDesc describes the changes that should be applied to the
|
||||||
// invoice.
|
// invoice.
|
||||||
type InvoiceUpdateDesc struct {
|
type InvoiceUpdateDesc struct {
|
||||||
|
@ -674,6 +719,9 @@ type InvoiceUpdateDesc struct {
|
||||||
// to be more efficient by ensuring we don't need to read out the
|
// to be more efficient by ensuring we don't need to read out the
|
||||||
// entire HTLC set each timee an HTLC is to be cancelled.
|
// entire HTLC set each timee an HTLC is to be cancelled.
|
||||||
SetID *SetID
|
SetID *SetID
|
||||||
|
|
||||||
|
// UpdateType indicates what type of update is being applied.
|
||||||
|
UpdateType UpdateType
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvoiceStateUpdateDesc describes an invoice-level state transition.
|
// InvoiceStateUpdateDesc describes an invoice-level state transition.
|
||||||
|
|
|
@ -238,7 +238,8 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
update := InvoiceUpdateDesc{
|
update := InvoiceUpdateDesc{
|
||||||
AddHtlcs: newHtlcs,
|
UpdateType: AddHTLCsUpdate,
|
||||||
|
AddHtlcs: newHtlcs,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the invoice cannot be settled yet, only record the htlc.
|
// If the invoice cannot be settled yet, only record the htlc.
|
||||||
|
@ -252,7 +253,6 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
|
||||||
if inv.HodlInvoice {
|
if inv.HodlInvoice {
|
||||||
update.State = &InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: ContractAccepted,
|
NewState: ContractAccepted,
|
||||||
SetID: setID,
|
|
||||||
}
|
}
|
||||||
return &update, ctx.acceptRes(resultAccepted), nil
|
return &update, ctx.acceptRes(resultAccepted), nil
|
||||||
}
|
}
|
||||||
|
@ -265,6 +265,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
|
||||||
var failRes *HtlcFailResolution
|
var failRes *HtlcFailResolution
|
||||||
htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
|
htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
|
||||||
if failRes != nil {
|
if failRes != nil {
|
||||||
|
update.UpdateType = CancelInvoiceUpdate
|
||||||
update.State = &InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: ContractCanceled,
|
NewState: ContractCanceled,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
|
@ -427,7 +428,8 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
update := InvoiceUpdateDesc{
|
update := InvoiceUpdateDesc{
|
||||||
AddHtlcs: newHtlcs,
|
AddHtlcs: newHtlcs,
|
||||||
|
UpdateType: AddHTLCsUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't update invoice state if we are accepting a duplicate payment.
|
// Don't update invoice state if we are accepting a duplicate payment.
|
||||||
|
|
Loading…
Add table
Reference in a new issue