channeldb: only accept/settle non-empty HTLC sets

Prior to AMP, there could only be one HTLC set. Now that there can be
multiple, we introduce the inherent risk that we might be persisting a
settle/accept of the wrong HTLC set. To migitigate this risk, we add a
check to assert that an invoice can only be transitioned into an
Accepted or Settled state if the specified HTLC set is non-empty, i.e.
has accepted HTLCs.

To do this check, we unroll the loops in UpdateInvoice to first process
any added or canceled HTLCs. This ensures that the set of accepted HTLCs
is up-to-date when we got to transition the invoice into a new state, at
which point we can accurately check that the HTLC set is non-empty.
Previously this sort of check wasn't possible because a newly added HTLC
wouldn't be populated on the invoice when going to call
updateInvoiceState.

Once the invoice-level transition checks have completed, we complete a
final pass to move the HTLCs into their final states and recompute the
amount paid.

With this refactor, it is now possible to make stronger assertions
about the invoice and the state transitions being made by the
invoiceregistry before those changes are ultimately persisted.
This commit is contained in:
Conner Fromknecht committed 2021-03-03 16:28:02 -08:00
1 parent e1b0fe5e98
commit fc9af92dd9
2 files changed
+57 -32

No files matched your search

+7
View File
@@ -1421,6 +1421,13 @@ func TestSetIDIndex(t *testing.T) {
require.Nil(t, err)
require.Equal(t, invoice, &dbInvoiceBySetID)
// Now settle the first htlc set, asserting that the two htlcs with set
// id 2 get canceled as a result.
_, err = db.UpdateInvoice(
ref, getUpdateInvoiceAMPSettle(&[32]byte{}),
)
require.Equal(t, ErrEmptyHTLCSet, err)
// Now settle the first htlc set, asserting that the two htlcs with set
// id 2 get canceled as a result.
dbInvoice, err = db.UpdateInvoice(ref, getUpdateInvoiceAMPSettle(setID))