Merge pull request #7215 from positiveblue/invoices-channeldb-dep
Invoices: invert package dependency with `channeldb`
This commit is contained in:
57 files changed
+3272
-2590
No files matched your search
@@ -31,7 +31,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
|
func initHintCache(t *testing.T) *channeldb.HeightHintCache {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
db, err := channeldb.Open(t.TempDir())
|
db, err := channeldb.Open(t.TempDir())
|
||||||
@@ -40,10 +40,10 @@ func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
|
|||||||
require.NoError(t, db.Close())
|
require.NoError(t, db.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
testCfg := chainntnfs.CacheConfig{
|
testCfg := channeldb.CacheConfig{
|
||||||
QueryDisable: false,
|
QueryDisable: false,
|
||||||
}
|
}
|
||||||
hintCache, err := chainntnfs.NewHeightHintCache(testCfg, db.Backend)
|
hintCache, err := channeldb.NewHeightHintCache(testCfg, db.Backend)
|
||||||
require.NoError(t, err, "unable to create hint cache")
|
require.NoError(t, err, "unable to create hint cache")
|
||||||
|
|
||||||
return hintCache
|
return hintCache
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
|
func initHintCache(t *testing.T) *channeldb.HeightHintCache {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
db, err := channeldb.Open(t.TempDir())
|
db, err := channeldb.Open(t.TempDir())
|
||||||
@@ -38,10 +38,10 @@ func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
|
|||||||
require.NoError(t, db.Close())
|
require.NoError(t, db.Close())
|
||||||
})
|
})
|
||||||
|
|
||||||
testCfg := chainntnfs.CacheConfig{
|
testCfg := channeldb.CacheConfig{
|
||||||
QueryDisable: false,
|
QueryDisable: false,
|
||||||
}
|
}
|
||||||
hintCache, err := chainntnfs.NewHeightHintCache(testCfg, db.Backend)
|
hintCache, err := channeldb.NewHeightHintCache(testCfg, db.Backend)
|
||||||
require.NoError(t, err, "unable to create hint cache")
|
require.NoError(t, err, "unable to create hint cache")
|
||||||
|
|
||||||
return hintCache
|
return hintCache
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package chainntnfs
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCorruptedHeightHintCache indicates that the on-disk representation
|
||||||
|
// has altered since the height hint cache instance was initialized.
|
||||||
|
ErrCorruptedHeightHintCache = errors.New("height hint cache has been " +
|
||||||
|
"corrupted")
|
||||||
|
|
||||||
|
// ErrSpendHintNotFound is an error returned when a spend hint for an
|
||||||
|
// outpoint was not found.
|
||||||
|
ErrSpendHintNotFound = errors.New("spend hint not found")
|
||||||
|
|
||||||
|
// ErrConfirmHintNotFound is an error returned when a confirm hint for a
|
||||||
|
// transaction was not found.
|
||||||
|
ErrConfirmHintNotFound = errors.New("confirm hint not found")
|
||||||
|
)
|
||||||
@@ -773,3 +773,38 @@ func ConfDetailsFromTxIndex(chainConn TxIndexConn, r ConfRequest,
|
|||||||
return nil, TxNotFoundIndex, fmt.Errorf("unable to locate "+
|
return nil, TxNotFoundIndex, fmt.Errorf("unable to locate "+
|
||||||
"tx %v in block %v", r.TxID, blockHash)
|
"tx %v in block %v", r.TxID, blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpendHintCache is an interface whose duty is to cache spend hints for
|
||||||
|
// outpoints. A spend hint is defined as the earliest height in the chain at
|
||||||
|
// which an outpoint could have been spent within.
|
||||||
|
type SpendHintCache interface {
|
||||||
|
// CommitSpendHint commits a spend hint for the outpoints to the cache.
|
||||||
|
CommitSpendHint(height uint32, spendRequests ...SpendRequest) error
|
||||||
|
|
||||||
|
// QuerySpendHint returns the latest spend hint for an outpoint.
|
||||||
|
// ErrSpendHintNotFound is returned if a spend hint does not exist
|
||||||
|
// within the cache for the outpoint.
|
||||||
|
QuerySpendHint(spendRequest SpendRequest) (uint32, error)
|
||||||
|
|
||||||
|
// PurgeSpendHint removes the spend hint for the outpoints from the
|
||||||
|
// cache.
|
||||||
|
PurgeSpendHint(spendRequests ...SpendRequest) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfirmHintCache is an interface whose duty is to cache confirm hints for
|
||||||
|
// transactions. A confirm hint is defined as the earliest height in the chain
|
||||||
|
// at which a transaction could have been included in a block.
|
||||||
|
type ConfirmHintCache interface {
|
||||||
|
// CommitConfirmHint commits a confirm hint for the transactions to the
|
||||||
|
// cache.
|
||||||
|
CommitConfirmHint(height uint32, confRequests ...ConfRequest) error
|
||||||
|
|
||||||
|
// QueryConfirmHint returns the latest confirm hint for a transaction
|
||||||
|
// hash. ErrConfirmHintNotFound is returned if a confirm hint does not
|
||||||
|
// exist within the cache for the transaction hash.
|
||||||
|
QueryConfirmHint(confRequest ConfRequest) (uint32, error)
|
||||||
|
|
||||||
|
// PurgeConfirmHint removes the confirm hint for the transactions from
|
||||||
|
// the cache.
|
||||||
|
PurgeConfirmHint(confRequests ...ConfRequest) error
|
||||||
|
}
|
||||||
@@ -1836,10 +1836,10 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create db: %v", err)
|
t.Fatalf("unable to create db: %v", err)
|
||||||
}
|
}
|
||||||
testCfg := chainntnfs.CacheConfig{
|
testCfg := channeldb.CacheConfig{
|
||||||
QueryDisable: false,
|
QueryDisable: false,
|
||||||
}
|
}
|
||||||
hintCache, err := chainntnfs.NewHeightHintCache(
|
hintCache, err := channeldb.NewHeightHintCache(
|
||||||
testCfg, db.Backend,
|
testCfg, db.Backend,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -201,21 +200,6 @@ func (r ConfRequest) String() string {
|
|||||||
return fmt.Sprintf("script=%v", r.PkScript)
|
return fmt.Sprintf("script=%v", r.PkScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfHintKey returns the key that will be used to index the confirmation
|
|
||||||
// request's hint within the height hint cache.
|
|
||||||
func (r ConfRequest) ConfHintKey() ([]byte, error) {
|
|
||||||
if r.TxID == ZeroHash {
|
|
||||||
return r.PkScript.Script(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var txid bytes.Buffer
|
|
||||||
if err := channeldb.WriteElement(&txid, r.TxID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return txid.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchesTx determines whether the given transaction satisfies the confirmation
|
// MatchesTx determines whether the given transaction satisfies the confirmation
|
||||||
// request. If the confirmation request is for a script, then we'll check all of
|
// request. If the confirmation request is for a script, then we'll check all of
|
||||||
// the outputs of the transaction to determine if it matches. Otherwise, we'll
|
// the outputs of the transaction to determine if it matches. Otherwise, we'll
|
||||||
@@ -369,22 +353,6 @@ func (r SpendRequest) String() string {
|
|||||||
return fmt.Sprintf("outpoint=<zero>, script=%v", r.PkScript)
|
return fmt.Sprintf("outpoint=<zero>, script=%v", r.PkScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpendHintKey returns the key that will be used to index the spend request's
|
|
||||||
// hint within the height hint cache.
|
|
||||||
func (r SpendRequest) SpendHintKey() ([]byte, error) {
|
|
||||||
if r.OutPoint == ZeroOutPoint {
|
|
||||||
return r.PkScript.Script(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var outpoint bytes.Buffer
|
|
||||||
err := channeldb.WriteElement(&outpoint, r.OutPoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return outpoint.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchesTx determines whether the given transaction satisfies the spend
|
// MatchesTx determines whether the given transaction satisfies the spend
|
||||||
// request. If the spend request is for an outpoint, then we'll check all of
|
// request. If the spend request is for an outpoint, then we'll check all of
|
||||||
// the outputs being spent by the inputs of the transaction to determine if it
|
// the outputs being spent by the inputs of the transaction to determine if it
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
heightHintCacheConfig := chainntnfs.CacheConfig{
|
heightHintCacheConfig := channeldb.CacheConfig{
|
||||||
QueryDisable: cfg.HeightHintCacheQueryDisable,
|
QueryDisable: cfg.HeightHintCacheQueryDisable,
|
||||||
}
|
}
|
||||||
if cfg.HeightHintCacheQueryDisable {
|
if cfg.HeightHintCacheQueryDisable {
|
||||||
@@ -300,7 +300,7 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the height hint cache within the chain directory.
|
// Initialize the height hint cache within the chain directory.
|
||||||
hintCache, err := chainntnfs.NewHeightHintCache(
|
hintCache, err := channeldb.NewHeightHintCache(
|
||||||
heightHintCacheConfig, cfg.HeightHintDB,
|
heightHintCacheConfig, cfg.HeightHintDB,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+5
-77
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
@@ -177,11 +178,6 @@ var (
|
|||||||
// tolerant.
|
// tolerant.
|
||||||
ErrNoPendingCommit = fmt.Errorf("no pending commits found")
|
ErrNoPendingCommit = fmt.Errorf("no pending commits found")
|
||||||
|
|
||||||
// ErrInvalidCircuitKeyLen signals that a circuit key could not be
|
|
||||||
// decoded because the byte slice is of an invalid length.
|
|
||||||
ErrInvalidCircuitKeyLen = fmt.Errorf(
|
|
||||||
"length of serialized circuit key must be 16 bytes")
|
|
||||||
|
|
||||||
// ErrNoCommitPoint is returned when no data loss commit point is found
|
// ErrNoCommitPoint is returned when no data loss commit point is found
|
||||||
// in the database.
|
// in the database.
|
||||||
ErrNoCommitPoint = fmt.Errorf("no commit point found")
|
ErrNoCommitPoint = fmt.Errorf("no commit point found")
|
||||||
@@ -2156,74 +2152,6 @@ func deserializeLogUpdate(r io.Reader) (*LogUpdate, error) {
|
|||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CircuitKey is used by a channel to uniquely identify the HTLCs it receives
|
|
||||||
// from the switch, and is used to purge our in-memory state of HTLCs that have
|
|
||||||
// already been processed by a link. Two list of CircuitKeys are included in
|
|
||||||
// each CommitDiff to allow a link to determine which in-memory htlcs directed
|
|
||||||
// the opening and closing of circuits in the switch's circuit map.
|
|
||||||
type CircuitKey struct {
|
|
||||||
// ChanID is the short chanid indicating the HTLC's origin.
|
|
||||||
//
|
|
||||||
// NOTE: It is fine for this value to be blank, as this indicates a
|
|
||||||
// locally-sourced payment.
|
|
||||||
ChanID lnwire.ShortChannelID
|
|
||||||
|
|
||||||
// HtlcID is the unique htlc index predominately assigned by links,
|
|
||||||
// though can also be assigned by switch in the case of locally-sourced
|
|
||||||
// payments.
|
|
||||||
HtlcID uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes deserializes the given bytes into this CircuitKey.
|
|
||||||
func (k *CircuitKey) SetBytes(bs []byte) error {
|
|
||||||
if len(bs) != 16 {
|
|
||||||
return ErrInvalidCircuitKeyLen
|
|
||||||
}
|
|
||||||
|
|
||||||
k.ChanID = lnwire.NewShortChanIDFromInt(
|
|
||||||
binary.BigEndian.Uint64(bs[:8]))
|
|
||||||
k.HtlcID = binary.BigEndian.Uint64(bs[8:])
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the serialized bytes for this circuit key.
|
|
||||||
func (k CircuitKey) Bytes() []byte {
|
|
||||||
bs := make([]byte, 16)
|
|
||||||
binary.BigEndian.PutUint64(bs[:8], k.ChanID.ToUint64())
|
|
||||||
binary.BigEndian.PutUint64(bs[8:], k.HtlcID)
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes a CircuitKey to the provided io.Writer.
|
|
||||||
func (k *CircuitKey) Encode(w io.Writer) error {
|
|
||||||
var scratch [16]byte
|
|
||||||
binary.BigEndian.PutUint64(scratch[:8], k.ChanID.ToUint64())
|
|
||||||
binary.BigEndian.PutUint64(scratch[8:], k.HtlcID)
|
|
||||||
|
|
||||||
_, err := w.Write(scratch[:])
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode reads a CircuitKey from the provided io.Reader.
|
|
||||||
func (k *CircuitKey) Decode(r io.Reader) error {
|
|
||||||
var scratch [16]byte
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k.ChanID = lnwire.NewShortChanIDFromInt(
|
|
||||||
binary.BigEndian.Uint64(scratch[:8]))
|
|
||||||
k.HtlcID = binary.BigEndian.Uint64(scratch[8:])
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the CircuitKey.
|
|
||||||
func (k CircuitKey) String() string {
|
|
||||||
return fmt.Sprintf("(Chan ID=%s, HTLC ID=%d)", k.ChanID, k.HtlcID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommitDiff represents the delta needed to apply the state transition between
|
// CommitDiff represents the delta needed to apply the state transition between
|
||||||
// two subsequent commitment states. Given state N and state N+1, one is able
|
// two subsequent commitment states. Given state N and state N+1, one is able
|
||||||
// to apply the set of messages contained within the CommitDiff to N to arrive
|
// to apply the set of messages contained within the CommitDiff to N to arrive
|
||||||
@@ -2252,13 +2180,13 @@ type CommitDiff struct {
|
|||||||
// Add packets included in this commitment txn. After a restart, this
|
// Add packets included in this commitment txn. After a restart, this
|
||||||
// set of htlcs is acked from the link's incoming mailbox to ensure
|
// set of htlcs is acked from the link's incoming mailbox to ensure
|
||||||
// there isn't an attempt to re-add them to this commitment txn.
|
// there isn't an attempt to re-add them to this commitment txn.
|
||||||
OpenedCircuitKeys []CircuitKey
|
OpenedCircuitKeys []models.CircuitKey
|
||||||
|
|
||||||
// ClosedCircuitKeys records the unique identifiers for any settle/fail
|
// ClosedCircuitKeys records the unique identifiers for any settle/fail
|
||||||
// packets that were resolved by this commitment txn. After a restart,
|
// packets that were resolved by this commitment txn. After a restart,
|
||||||
// this is used to ensure those circuits are removed from the circuit
|
// this is used to ensure those circuits are removed from the circuit
|
||||||
// map, and the downstream packets in the link's mailbox are removed.
|
// map, and the downstream packets in the link's mailbox are removed.
|
||||||
ClosedCircuitKeys []CircuitKey
|
ClosedCircuitKeys []models.CircuitKey
|
||||||
|
|
||||||
// AddAcks specifies the locations (commit height, pkg index) of any
|
// AddAcks specifies the locations (commit height, pkg index) of any
|
||||||
// Adds that were failed/settled in this commit diff. This will ack
|
// Adds that were failed/settled in this commit diff. This will ack
|
||||||
@@ -2387,7 +2315,7 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.OpenedCircuitKeys = make([]CircuitKey, numOpenRefs)
|
d.OpenedCircuitKeys = make([]models.CircuitKey, numOpenRefs)
|
||||||
for i := 0; i < int(numOpenRefs); i++ {
|
for i := 0; i < int(numOpenRefs); i++ {
|
||||||
err := ReadElements(r,
|
err := ReadElements(r,
|
||||||
&d.OpenedCircuitKeys[i].ChanID,
|
&d.OpenedCircuitKeys[i].ChanID,
|
||||||
@@ -2402,7 +2330,7 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.ClosedCircuitKeys = make([]CircuitKey, numClosedRefs)
|
d.ClosedCircuitKeys = make([]models.CircuitKey, numClosedRefs)
|
||||||
for i := 0; i < int(numClosedRefs); i++ {
|
for i := 0; i < int(numClosedRefs); i++ {
|
||||||
err := ReadElements(r,
|
err := ReadElements(r,
|
||||||
&d.ClosedCircuitKeys[i].ChanID,
|
&d.ClosedCircuitKeys[i].ChanID,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
@@ -716,8 +717,8 @@ func TestChannelStateTransition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OpenedCircuitKeys: []CircuitKey{},
|
OpenedCircuitKeys: []models.CircuitKey{},
|
||||||
ClosedCircuitKeys: []CircuitKey{},
|
ClosedCircuitKeys: []models.CircuitKey{},
|
||||||
}
|
}
|
||||||
copy(commitDiff.LogUpdates[0].UpdateMsg.(*lnwire.UpdateAddHTLC).PaymentHash[:],
|
copy(commitDiff.LogUpdates[0].UpdateMsg.(*lnwire.UpdateAddHTLC).PaymentHash[:],
|
||||||
bytes.Repeat([]byte{1}, 32))
|
bytes.Repeat([]byte{1}, 32))
|
||||||
|
|||||||
@@ -31,30 +31,6 @@ var (
|
|||||||
// created.
|
// created.
|
||||||
ErrNoPastDeltas = fmt.Errorf("channel has no recorded deltas")
|
ErrNoPastDeltas = fmt.Errorf("channel has no recorded deltas")
|
||||||
|
|
||||||
// ErrInvoiceNotFound is returned when a targeted invoice can't be
|
|
||||||
// found.
|
|
||||||
ErrInvoiceNotFound = fmt.Errorf("unable to locate invoice")
|
|
||||||
|
|
||||||
// ErrNoInvoicesCreated is returned when we don't have invoices in
|
|
||||||
// our database to return.
|
|
||||||
ErrNoInvoicesCreated = fmt.Errorf("there are no existing invoices")
|
|
||||||
|
|
||||||
// ErrDuplicateInvoice is returned when an invoice with the target
|
|
||||||
// payment hash already exists.
|
|
||||||
ErrDuplicateInvoice = fmt.Errorf("invoice with payment hash already exists")
|
|
||||||
|
|
||||||
// ErrDuplicatePayAddr is returned when an invoice with the target
|
|
||||||
// payment addr already exists.
|
|
||||||
ErrDuplicatePayAddr = fmt.Errorf("invoice with payemnt addr already exists")
|
|
||||||
|
|
||||||
// ErrInvRefEquivocation is returned when an InvoiceRef targets
|
|
||||||
// multiple, distinct invoices.
|
|
||||||
ErrInvRefEquivocation = errors.New("inv ref matches multiple invoices")
|
|
||||||
|
|
||||||
// ErrNoPaymentsCreated is returned when bucket of payments hasn't been
|
|
||||||
// created.
|
|
||||||
ErrNoPaymentsCreated = fmt.Errorf("there are no existing payments")
|
|
||||||
|
|
||||||
// ErrNodeNotFound is returned when node bucket exists, but node with
|
// ErrNodeNotFound is returned when node bucket exists, but node with
|
||||||
// specific identity can't be found.
|
// specific identity can't be found.
|
||||||
ErrNodeNotFound = fmt.Errorf("link node with target identity not found")
|
ErrNodeNotFound = fmt.Errorf("link node with target identity not found")
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package chainntnfs
|
package channeldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,20 +18,6 @@ var (
|
|||||||
// height at which its corresponding transaction could have been
|
// height at which its corresponding transaction could have been
|
||||||
// confirmed within.
|
// confirmed within.
|
||||||
confirmHintBucket = []byte("confirm-hints")
|
confirmHintBucket = []byte("confirm-hints")
|
||||||
|
|
||||||
// ErrCorruptedHeightHintCache indicates that the on-disk bucketing
|
|
||||||
// structure has altered since the height hint cache instance was
|
|
||||||
// initialized.
|
|
||||||
ErrCorruptedHeightHintCache = errors.New("height hint cache has been " +
|
|
||||||
"corrupted")
|
|
||||||
|
|
||||||
// ErrSpendHintNotFound is an error returned when a spend hint for an
|
|
||||||
// outpoint was not found.
|
|
||||||
ErrSpendHintNotFound = errors.New("spend hint not found")
|
|
||||||
|
|
||||||
// ErrConfirmHintNotFound is an error returned when a confirm hint for a
|
|
||||||
// transaction was not found.
|
|
||||||
ErrConfirmHintNotFound = errors.New("confirm hint not found")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CacheConfig contains the HeightHintCache configuration.
|
// CacheConfig contains the HeightHintCache configuration.
|
||||||
@@ -44,41 +29,6 @@ type CacheConfig struct {
|
|||||||
QueryDisable bool
|
QueryDisable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpendHintCache is an interface whose duty is to cache spend hints for
|
|
||||||
// outpoints. A spend hint is defined as the earliest height in the chain at
|
|
||||||
// which an outpoint could have been spent within.
|
|
||||||
type SpendHintCache interface {
|
|
||||||
// CommitSpendHint commits a spend hint for the outpoints to the cache.
|
|
||||||
CommitSpendHint(height uint32, spendRequests ...SpendRequest) error
|
|
||||||
|
|
||||||
// QuerySpendHint returns the latest spend hint for an outpoint.
|
|
||||||
// ErrSpendHintNotFound is returned if a spend hint does not exist
|
|
||||||
// within the cache for the outpoint.
|
|
||||||
QuerySpendHint(spendRequest SpendRequest) (uint32, error)
|
|
||||||
|
|
||||||
// PurgeSpendHint removes the spend hint for the outpoints from the
|
|
||||||
// cache.
|
|
||||||
PurgeSpendHint(spendRequests ...SpendRequest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfirmHintCache is an interface whose duty is to cache confirm hints for
|
|
||||||
// transactions. A confirm hint is defined as the earliest height in the chain
|
|
||||||
// at which a transaction could have been included in a block.
|
|
||||||
type ConfirmHintCache interface {
|
|
||||||
// CommitConfirmHint commits a confirm hint for the transactions to the
|
|
||||||
// cache.
|
|
||||||
CommitConfirmHint(height uint32, confRequests ...ConfRequest) error
|
|
||||||
|
|
||||||
// QueryConfirmHint returns the latest confirm hint for a transaction
|
|
||||||
// hash. ErrConfirmHintNotFound is returned if a confirm hint does not
|
|
||||||
// exist within the cache for the transaction hash.
|
|
||||||
QueryConfirmHint(confRequest ConfRequest) (uint32, error)
|
|
||||||
|
|
||||||
// PurgeConfirmHint removes the confirm hint for the transactions from
|
|
||||||
// the cache.
|
|
||||||
PurgeConfirmHint(confRequests ...ConfRequest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeightHintCache is an implementation of the SpendHintCache and
|
// HeightHintCache is an implementation of the SpendHintCache and
|
||||||
// ConfirmHintCache interfaces backed by a channeldb DB instance where the hints
|
// ConfirmHintCache interfaces backed by a channeldb DB instance where the hints
|
||||||
// will be stored.
|
// will be stored.
|
||||||
@@ -89,8 +39,8 @@ type HeightHintCache struct {
|
|||||||
|
|
||||||
// Compile-time checks to ensure HeightHintCache satisfies the SpendHintCache
|
// Compile-time checks to ensure HeightHintCache satisfies the SpendHintCache
|
||||||
// and ConfirmHintCache interfaces.
|
// and ConfirmHintCache interfaces.
|
||||||
var _ SpendHintCache = (*HeightHintCache)(nil)
|
var _ chainntnfs.SpendHintCache = (*HeightHintCache)(nil)
|
||||||
var _ ConfirmHintCache = (*HeightHintCache)(nil)
|
var _ chainntnfs.ConfirmHintCache = (*HeightHintCache)(nil)
|
||||||
|
|
||||||
// NewHeightHintCache returns a new height hint cache backed by a database.
|
// NewHeightHintCache returns a new height hint cache backed by a database.
|
||||||
func NewHeightHintCache(cfg CacheConfig, db kvdb.Backend) (*HeightHintCache,
|
func NewHeightHintCache(cfg CacheConfig, db kvdb.Backend) (*HeightHintCache,
|
||||||
@@ -120,28 +70,29 @@ func (c *HeightHintCache) initBuckets() error {
|
|||||||
|
|
||||||
// CommitSpendHint commits a spend hint for the outpoints to the cache.
|
// CommitSpendHint commits a spend hint for the outpoints to the cache.
|
||||||
func (c *HeightHintCache) CommitSpendHint(height uint32,
|
func (c *HeightHintCache) CommitSpendHint(height uint32,
|
||||||
spendRequests ...SpendRequest) error {
|
spendRequests ...chainntnfs.SpendRequest) error {
|
||||||
|
|
||||||
if len(spendRequests) == 0 {
|
if len(spendRequests) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Tracef("Updating spend hint to height %d for %v", height,
|
log.Tracef("Updating spend hint to height %d for %v", height,
|
||||||
spendRequests)
|
spendRequests)
|
||||||
|
|
||||||
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
||||||
spendHints := tx.ReadWriteBucket(spendHintBucket)
|
spendHints := tx.ReadWriteBucket(spendHintBucket)
|
||||||
if spendHints == nil {
|
if spendHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
var hint bytes.Buffer
|
var hint bytes.Buffer
|
||||||
if err := channeldb.WriteElement(&hint, height); err != nil {
|
if err := WriteElement(&hint, height); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, spendRequest := range spendRequests {
|
for _, spendRequest := range spendRequests {
|
||||||
spendHintKey, err := spendRequest.SpendHintKey()
|
spendRequest := spendRequest
|
||||||
|
spendHintKey, err := spendHintKey(&spendRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -158,29 +109,31 @@ func (c *HeightHintCache) CommitSpendHint(height uint32,
|
|||||||
// QuerySpendHint returns the latest spend hint for an outpoint.
|
// QuerySpendHint returns the latest spend hint for an outpoint.
|
||||||
// ErrSpendHintNotFound is returned if a spend hint does not exist within the
|
// ErrSpendHintNotFound is returned if a spend hint does not exist within the
|
||||||
// cache for the outpoint.
|
// cache for the outpoint.
|
||||||
func (c *HeightHintCache) QuerySpendHint(spendRequest SpendRequest) (uint32, error) {
|
func (c *HeightHintCache) QuerySpendHint(
|
||||||
|
spendRequest chainntnfs.SpendRequest) (uint32, error) {
|
||||||
|
|
||||||
var hint uint32
|
var hint uint32
|
||||||
if c.cfg.QueryDisable {
|
if c.cfg.QueryDisable {
|
||||||
Log.Debugf("Ignoring spend height hint for %v (height hint cache "+
|
log.Debugf("Ignoring spend height hint for %v (height hint "+
|
||||||
"query disabled)", spendRequest)
|
"cache query disabled)", spendRequest)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||||
spendHints := tx.ReadBucket(spendHintBucket)
|
spendHints := tx.ReadBucket(spendHintBucket)
|
||||||
if spendHints == nil {
|
if spendHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
spendHintKey, err := spendRequest.SpendHintKey()
|
spendHintKey, err := spendHintKey(&spendRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
spendHint := spendHints.Get(spendHintKey)
|
spendHint := spendHints.Get(spendHintKey)
|
||||||
if spendHint == nil {
|
if spendHint == nil {
|
||||||
return ErrSpendHintNotFound
|
return chainntnfs.ErrSpendHintNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return channeldb.ReadElement(bytes.NewReader(spendHint), &hint)
|
return ReadElement(bytes.NewReader(spendHint), &hint)
|
||||||
}, func() {
|
}, func() {
|
||||||
hint = 0
|
hint = 0
|
||||||
})
|
})
|
||||||
@@ -192,21 +145,24 @@ func (c *HeightHintCache) QuerySpendHint(spendRequest SpendRequest) (uint32, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PurgeSpendHint removes the spend hint for the outpoints from the cache.
|
// PurgeSpendHint removes the spend hint for the outpoints from the cache.
|
||||||
func (c *HeightHintCache) PurgeSpendHint(spendRequests ...SpendRequest) error {
|
func (c *HeightHintCache) PurgeSpendHint(
|
||||||
|
spendRequests ...chainntnfs.SpendRequest) error {
|
||||||
|
|
||||||
if len(spendRequests) == 0 {
|
if len(spendRequests) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Tracef("Removing spend hints for %v", spendRequests)
|
log.Tracef("Removing spend hints for %v", spendRequests)
|
||||||
|
|
||||||
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
||||||
spendHints := tx.ReadWriteBucket(spendHintBucket)
|
spendHints := tx.ReadWriteBucket(spendHintBucket)
|
||||||
if spendHints == nil {
|
if spendHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, spendRequest := range spendRequests {
|
for _, spendRequest := range spendRequests {
|
||||||
spendHintKey, err := spendRequest.SpendHintKey()
|
spendRequest := spendRequest
|
||||||
|
spendHintKey, err := spendHintKey(&spendRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -221,28 +177,29 @@ func (c *HeightHintCache) PurgeSpendHint(spendRequests ...SpendRequest) error {
|
|||||||
|
|
||||||
// CommitConfirmHint commits a confirm hint for the transactions to the cache.
|
// CommitConfirmHint commits a confirm hint for the transactions to the cache.
|
||||||
func (c *HeightHintCache) CommitConfirmHint(height uint32,
|
func (c *HeightHintCache) CommitConfirmHint(height uint32,
|
||||||
confRequests ...ConfRequest) error {
|
confRequests ...chainntnfs.ConfRequest) error {
|
||||||
|
|
||||||
if len(confRequests) == 0 {
|
if len(confRequests) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Tracef("Updating confirm hints to height %d for %v", height,
|
log.Tracef("Updating confirm hints to height %d for %v", height,
|
||||||
confRequests)
|
confRequests)
|
||||||
|
|
||||||
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
||||||
confirmHints := tx.ReadWriteBucket(confirmHintBucket)
|
confirmHints := tx.ReadWriteBucket(confirmHintBucket)
|
||||||
if confirmHints == nil {
|
if confirmHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
var hint bytes.Buffer
|
var hint bytes.Buffer
|
||||||
if err := channeldb.WriteElement(&hint, height); err != nil {
|
if err := WriteElement(&hint, height); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, confRequest := range confRequests {
|
for _, confRequest := range confRequests {
|
||||||
confHintKey, err := confRequest.ConfHintKey()
|
confRequest := confRequest
|
||||||
|
confHintKey, err := confHintKey(&confRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -259,29 +216,31 @@ func (c *HeightHintCache) CommitConfirmHint(height uint32,
|
|||||||
// QueryConfirmHint returns the latest confirm hint for a transaction hash.
|
// QueryConfirmHint returns the latest confirm hint for a transaction hash.
|
||||||
// ErrConfirmHintNotFound is returned if a confirm hint does not exist within
|
// ErrConfirmHintNotFound is returned if a confirm hint does not exist within
|
||||||
// the cache for the transaction hash.
|
// the cache for the transaction hash.
|
||||||
func (c *HeightHintCache) QueryConfirmHint(confRequest ConfRequest) (uint32, error) {
|
func (c *HeightHintCache) QueryConfirmHint(
|
||||||
|
confRequest chainntnfs.ConfRequest) (uint32, error) {
|
||||||
|
|
||||||
var hint uint32
|
var hint uint32
|
||||||
if c.cfg.QueryDisable {
|
if c.cfg.QueryDisable {
|
||||||
Log.Debugf("Ignoring confirmation height hint for %v (height hint "+
|
log.Debugf("Ignoring confirmation height hint for %v (height "+
|
||||||
"cache query disabled)", confRequest)
|
"hint cache query disabled)", confRequest)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||||
confirmHints := tx.ReadBucket(confirmHintBucket)
|
confirmHints := tx.ReadBucket(confirmHintBucket)
|
||||||
if confirmHints == nil {
|
if confirmHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
confHintKey, err := confRequest.ConfHintKey()
|
confHintKey, err := confHintKey(&confRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
confirmHint := confirmHints.Get(confHintKey)
|
confirmHint := confirmHints.Get(confHintKey)
|
||||||
if confirmHint == nil {
|
if confirmHint == nil {
|
||||||
return ErrConfirmHintNotFound
|
return chainntnfs.ErrConfirmHintNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return channeldb.ReadElement(bytes.NewReader(confirmHint), &hint)
|
return ReadElement(bytes.NewReader(confirmHint), &hint)
|
||||||
}, func() {
|
}, func() {
|
||||||
hint = 0
|
hint = 0
|
||||||
})
|
})
|
||||||
@@ -294,21 +253,24 @@ func (c *HeightHintCache) QueryConfirmHint(confRequest ConfRequest) (uint32, err
|
|||||||
|
|
||||||
// PurgeConfirmHint removes the confirm hint for the transactions from the
|
// PurgeConfirmHint removes the confirm hint for the transactions from the
|
||||||
// cache.
|
// cache.
|
||||||
func (c *HeightHintCache) PurgeConfirmHint(confRequests ...ConfRequest) error {
|
func (c *HeightHintCache) PurgeConfirmHint(
|
||||||
|
confRequests ...chainntnfs.ConfRequest) error {
|
||||||
|
|
||||||
if len(confRequests) == 0 {
|
if len(confRequests) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Tracef("Removing confirm hints for %v", confRequests)
|
log.Tracef("Removing confirm hints for %v", confRequests)
|
||||||
|
|
||||||
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
|
||||||
confirmHints := tx.ReadWriteBucket(confirmHintBucket)
|
confirmHints := tx.ReadWriteBucket(confirmHintBucket)
|
||||||
if confirmHints == nil {
|
if confirmHints == nil {
|
||||||
return ErrCorruptedHeightHintCache
|
return chainntnfs.ErrCorruptedHeightHintCache
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, confRequest := range confRequests {
|
for _, confRequest := range confRequests {
|
||||||
confHintKey, err := confRequest.ConfHintKey()
|
confRequest := confRequest
|
||||||
|
confHintKey, err := confHintKey(&confRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -320,3 +282,34 @@ func (c *HeightHintCache) PurgeConfirmHint(confRequests ...ConfRequest) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// confHintKey returns the key that will be used to index the confirmation
|
||||||
|
// request's hint within the height hint cache.
|
||||||
|
func confHintKey(r *chainntnfs.ConfRequest) ([]byte, error) {
|
||||||
|
if r.TxID == chainntnfs.ZeroHash {
|
||||||
|
return r.PkScript.Script(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var txid bytes.Buffer
|
||||||
|
if err := WriteElement(&txid, r.TxID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return txid.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// spendHintKey returns the key that will be used to index the spend request's
|
||||||
|
// hint within the height hint cache.
|
||||||
|
func spendHintKey(r *chainntnfs.SpendRequest) ([]byte, error) {
|
||||||
|
if r.OutPoint == chainntnfs.ZeroOutPoint {
|
||||||
|
return r.PkScript.Script(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var outpoint bytes.Buffer
|
||||||
|
err := WriteElement(&outpoint, r.OutPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outpoint.Bytes(), nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package chainntnfs
|
package channeldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ func initHintCache(t *testing.T) *HeightHintCache {
|
|||||||
func initHintCacheWithConfig(t *testing.T, cfg CacheConfig) *HeightHintCache {
|
func initHintCacheWithConfig(t *testing.T, cfg CacheConfig) *HeightHintCache {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
db, err := channeldb.Open(t.TempDir())
|
db, err := Open(t.TempDir())
|
||||||
require.NoError(t, err, "unable to create db")
|
require.NoError(t, err, "unable to create db")
|
||||||
hintCache, err := NewHeightHintCache(cfg, db.Backend)
|
hintCache, err := NewHeightHintCache(cfg, db.Backend)
|
||||||
require.NoError(t, err, "unable to create hint cache")
|
require.NoError(t, err, "unable to create hint cache")
|
||||||
@@ -46,21 +46,19 @@ func TestHeightHintCacheConfirms(t *testing.T) {
|
|||||||
// return an error indication so.
|
// return an error indication so.
|
||||||
var unknownHash chainhash.Hash
|
var unknownHash chainhash.Hash
|
||||||
copy(unknownHash[:], bytes.Repeat([]byte{0x01}, 32))
|
copy(unknownHash[:], bytes.Repeat([]byte{0x01}, 32))
|
||||||
unknownConfRequest := ConfRequest{TxID: unknownHash}
|
unknownConfRequest := chainntnfs.ConfRequest{TxID: unknownHash}
|
||||||
_, err := hintCache.QueryConfirmHint(unknownConfRequest)
|
_, err := hintCache.QueryConfirmHint(unknownConfRequest)
|
||||||
if err != ErrConfirmHintNotFound {
|
require.ErrorIs(t, err, chainntnfs.ErrConfirmHintNotFound)
|
||||||
t.Fatalf("expected ErrConfirmHintNotFound, got: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, we'll create some transaction hashes and commit them to the
|
// Now, we'll create some transaction hashes and commit them to the
|
||||||
// cache with the same confirm hint.
|
// cache with the same confirm hint.
|
||||||
const height = 100
|
const height = 100
|
||||||
const numHashes = 5
|
const numHashes = 5
|
||||||
confRequests := make([]ConfRequest, numHashes)
|
confRequests := make([]chainntnfs.ConfRequest, numHashes)
|
||||||
for i := 0; i < numHashes; i++ {
|
for i := 0; i < numHashes; i++ {
|
||||||
var txHash chainhash.Hash
|
var txHash chainhash.Hash
|
||||||
copy(txHash[:], bytes.Repeat([]byte{byte(i + 1)}, 32))
|
copy(txHash[:], bytes.Repeat([]byte{byte(i + 1)}, 32))
|
||||||
confRequests[i] = ConfRequest{TxID: txHash}
|
confRequests[i] = chainntnfs.ConfRequest{TxID: txHash}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hintCache.CommitConfirmHint(height, confRequests...)
|
err = hintCache.CommitConfirmHint(height, confRequests...)
|
||||||
@@ -70,28 +68,20 @@ func TestHeightHintCacheConfirms(t *testing.T) {
|
|||||||
// we're able to properly retrieve the confirm hints.
|
// we're able to properly retrieve the confirm hints.
|
||||||
for _, confRequest := range confRequests {
|
for _, confRequest := range confRequests {
|
||||||
confirmHint, err := hintCache.QueryConfirmHint(confRequest)
|
confirmHint, err := hintCache.QueryConfirmHint(confRequest)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("unable to query for hint of %v: %v", confRequest, err)
|
require.EqualValues(t, height, confirmHint)
|
||||||
}
|
|
||||||
if confirmHint != height {
|
|
||||||
t.Fatalf("expected confirm hint %d, got %d", height,
|
|
||||||
confirmHint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll also attempt to purge all of them in a single database
|
// We'll also attempt to purge all of them in a single database
|
||||||
// transaction.
|
// transaction.
|
||||||
if err := hintCache.PurgeConfirmHint(confRequests...); err != nil {
|
err = hintCache.PurgeConfirmHint(confRequests...)
|
||||||
t.Fatalf("unable to remove confirm hints: %v", err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, we'll attempt to query for each hash. We should expect not
|
// Finally, we'll attempt to query for each hash. We should expect not
|
||||||
// to find a hint for any of them.
|
// to find a hint for any of them.
|
||||||
for _, confRequest := range confRequests {
|
for _, confRequest := range confRequests {
|
||||||
_, err := hintCache.QueryConfirmHint(confRequest)
|
_, err := hintCache.QueryConfirmHint(confRequest)
|
||||||
if err != ErrConfirmHintNotFound {
|
require.ErrorIs(t, err, chainntnfs.ErrConfirmHintNotFound)
|
||||||
t.Fatalf("expected ErrConfirmHintNotFound, got :%v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,19 +95,19 @@ func TestHeightHintCacheSpends(t *testing.T) {
|
|||||||
// Querying for an outpoint not found within the cache should return an
|
// Querying for an outpoint not found within the cache should return an
|
||||||
// error indication so.
|
// error indication so.
|
||||||
unknownOutPoint := wire.OutPoint{Index: 1}
|
unknownOutPoint := wire.OutPoint{Index: 1}
|
||||||
unknownSpendRequest := SpendRequest{OutPoint: unknownOutPoint}
|
unknownSpendRequest := chainntnfs.SpendRequest{
|
||||||
_, err := hintCache.QuerySpendHint(unknownSpendRequest)
|
OutPoint: unknownOutPoint,
|
||||||
if err != ErrSpendHintNotFound {
|
|
||||||
t.Fatalf("expected ErrSpendHintNotFound, got: %v", err)
|
|
||||||
}
|
}
|
||||||
|
_, err := hintCache.QuerySpendHint(unknownSpendRequest)
|
||||||
|
require.ErrorIs(t, err, chainntnfs.ErrSpendHintNotFound)
|
||||||
|
|
||||||
// Now, we'll create some outpoints and commit them to the cache with
|
// Now, we'll create some outpoints and commit them to the cache with
|
||||||
// the same spend hint.
|
// the same spend hint.
|
||||||
const height = 100
|
const height = 100
|
||||||
const numOutpoints = 5
|
const numOutpoints = 5
|
||||||
spendRequests := make([]SpendRequest, numOutpoints)
|
spendRequests := make([]chainntnfs.SpendRequest, numOutpoints)
|
||||||
for i := uint32(0); i < numOutpoints; i++ {
|
for i := uint32(0); i < numOutpoints; i++ {
|
||||||
spendRequests[i] = SpendRequest{
|
spendRequests[i] = chainntnfs.SpendRequest{
|
||||||
OutPoint: wire.OutPoint{Index: i + 1},
|
OutPoint: wire.OutPoint{Index: i + 1},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,28 +119,20 @@ func TestHeightHintCacheSpends(t *testing.T) {
|
|||||||
// that we're able to properly retrieve the confirm hints.
|
// that we're able to properly retrieve the confirm hints.
|
||||||
for _, spendRequest := range spendRequests {
|
for _, spendRequest := range spendRequests {
|
||||||
spendHint, err := hintCache.QuerySpendHint(spendRequest)
|
spendHint, err := hintCache.QuerySpendHint(spendRequest)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("unable to query for hint: %v", err)
|
require.EqualValues(t, height, spendHint)
|
||||||
}
|
|
||||||
if spendHint != height {
|
|
||||||
t.Fatalf("expected spend hint %d, got %d", height,
|
|
||||||
spendHint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll also attempt to purge all of them in a single database
|
// We'll also attempt to purge all of them in a single database
|
||||||
// transaction.
|
// transaction.
|
||||||
if err := hintCache.PurgeSpendHint(spendRequests...); err != nil {
|
err = hintCache.PurgeSpendHint(spendRequests...)
|
||||||
t.Fatalf("unable to remove spend hint: %v", err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, we'll attempt to query for each outpoint. We should expect
|
// Finally, we'll attempt to query for each outpoint. We should expect
|
||||||
// not to find a hint for any of them.
|
// not to find a hint for any of them.
|
||||||
for _, spendRequest := range spendRequests {
|
for _, spendRequest := range spendRequests {
|
||||||
_, err = hintCache.QuerySpendHint(spendRequest)
|
_, err = hintCache.QuerySpendHint(spendRequest)
|
||||||
if err != ErrSpendHintNotFound {
|
require.ErrorIs(t, err, chainntnfs.ErrSpendHintNotFound)
|
||||||
t.Fatalf("expected ErrSpendHintNotFound, got: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +147,7 @@ func TestQueryDisable(t *testing.T) {
|
|||||||
|
|
||||||
// Insert a new confirmation hint with a non-zero height.
|
// Insert a new confirmation hint with a non-zero height.
|
||||||
const confHeight = 100
|
const confHeight = 100
|
||||||
confRequest := ConfRequest{
|
confRequest := chainntnfs.ConfRequest{
|
||||||
TxID: chainhash.Hash{0x01, 0x02, 0x03},
|
TxID: chainhash.Hash{0x01, 0x02, 0x03},
|
||||||
}
|
}
|
||||||
err := hintCache.CommitConfirmHint(confHeight, confRequest)
|
err := hintCache.CommitConfirmHint(confHeight, confRequest)
|
||||||
@@ -178,7 +160,7 @@ func TestQueryDisable(t *testing.T) {
|
|||||||
|
|
||||||
// Insert a new spend hint with a non-zero height.
|
// Insert a new spend hint with a non-zero height.
|
||||||
const spendHeight = 200
|
const spendHeight = 200
|
||||||
spendRequest := SpendRequest{
|
spendRequest := chainntnfs.SpendRequest{
|
||||||
OutPoint: wire.OutPoint{
|
OutPoint: wire.OutPoint{
|
||||||
Hash: chainhash.Hash{0x4, 0x05, 0x06},
|
Hash: chainhash.Hash{0x4, 0x05, 0x06},
|
||||||
Index: 42,
|
Index: 42,
|
||||||
+594
-498
File diff suppressed because it is too large.
Load diff
+295
-1119
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,92 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// serializedCircuitKeyLen is the exact length needed to encode a
|
||||||
|
// serialized CircuitKey.
|
||||||
|
serializedCircuitKeyLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidCircuitKeyLen signals that a circuit key could not be
|
||||||
|
// decoded because the byte slice is of an invalid length.
|
||||||
|
ErrInvalidCircuitKeyLen = fmt.Errorf("length of serialized circuit " +
|
||||||
|
"key must be 16 bytes")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CircuitKey is used by a channel to uniquely identify the HTLCs it receives
|
||||||
|
// from the switch, and is used to purge our in-memory state of HTLCs that have
|
||||||
|
// already been processed by a link. Two list of CircuitKeys are included in
|
||||||
|
// each CommitDiff to allow a link to determine which in-memory htlcs directed
|
||||||
|
// the opening and closing of circuits in the switch's circuit map.
|
||||||
|
type CircuitKey struct {
|
||||||
|
// ChanID is the short chanid indicating the HTLC's origin.
|
||||||
|
//
|
||||||
|
// NOTE: It is fine for this value to be blank, as this indicates a
|
||||||
|
// locally-sourced payment.
|
||||||
|
ChanID lnwire.ShortChannelID
|
||||||
|
|
||||||
|
// HtlcID is the unique htlc index predominately assigned by links,
|
||||||
|
// though can also be assigned by switch in the case of locally-sourced
|
||||||
|
// payments.
|
||||||
|
HtlcID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytes deserializes the given bytes into this CircuitKey.
|
||||||
|
func (k *CircuitKey) SetBytes(bs []byte) error {
|
||||||
|
if len(bs) != serializedCircuitKeyLen {
|
||||||
|
return ErrInvalidCircuitKeyLen
|
||||||
|
}
|
||||||
|
|
||||||
|
k.ChanID = lnwire.NewShortChanIDFromInt(
|
||||||
|
binary.BigEndian.Uint64(bs[:8]))
|
||||||
|
k.HtlcID = binary.BigEndian.Uint64(bs[8:])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the serialized bytes for this circuit key.
|
||||||
|
func (k CircuitKey) Bytes() []byte {
|
||||||
|
bs := make([]byte, serializedCircuitKeyLen)
|
||||||
|
binary.BigEndian.PutUint64(bs[:8], k.ChanID.ToUint64())
|
||||||
|
binary.BigEndian.PutUint64(bs[8:], k.HtlcID)
|
||||||
|
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes a CircuitKey to the provided io.Writer.
|
||||||
|
func (k *CircuitKey) Encode(w io.Writer) error {
|
||||||
|
var scratch [serializedCircuitKeyLen]byte
|
||||||
|
binary.BigEndian.PutUint64(scratch[:8], k.ChanID.ToUint64())
|
||||||
|
binary.BigEndian.PutUint64(scratch[8:], k.HtlcID)
|
||||||
|
|
||||||
|
_, err := w.Write(scratch[:])
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads a CircuitKey from the provided io.Reader.
|
||||||
|
func (k *CircuitKey) Decode(r io.Reader) error {
|
||||||
|
var scratch [serializedCircuitKeyLen]byte
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
k.ChanID = lnwire.NewShortChanIDFromInt(
|
||||||
|
binary.BigEndian.Uint64(scratch[:8]))
|
||||||
|
k.HtlcID = binary.BigEndian.Uint64(scratch[8:])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the CircuitKey.
|
||||||
|
func (k CircuitKey) String() string {
|
||||||
|
return fmt.Sprintf("(Chan ID=%s, HTLC ID=%d)", k.ChanID, k.HtlcID)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/blockcache"
|
"github.com/lightningnetwork/lnd/blockcache"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lncfg"
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
@@ -764,6 +765,9 @@ type DatabaseInstances struct {
|
|||||||
// HeightHintDB is the database that stores height hints for spends.
|
// HeightHintDB is the database that stores height hints for spends.
|
||||||
HeightHintDB kvdb.Backend
|
HeightHintDB kvdb.Backend
|
||||||
|
|
||||||
|
// InvoiceDB is the database that stores information about invoices.
|
||||||
|
InvoiceDB invoices.InvoiceDB
|
||||||
|
|
||||||
// MacaroonDB is the database that stores macaroon root keys.
|
// MacaroonDB is the database that stores macaroon root keys.
|
||||||
MacaroonDB kvdb.Backend
|
MacaroonDB kvdb.Backend
|
||||||
|
|
||||||
@@ -915,6 +919,12 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
|
|||||||
// using the same struct (and DB backend) instance.
|
// using the same struct (and DB backend) instance.
|
||||||
dbs.ChanStateDB = dbs.GraphDB
|
dbs.ChanStateDB = dbs.GraphDB
|
||||||
|
|
||||||
|
// For now the only InvoiceDB implementation is the *channeldb.DB.
|
||||||
|
//
|
||||||
|
// TODO(positiveblue): use a sql first implementation for this
|
||||||
|
// interface.
|
||||||
|
dbs.InvoiceDB = dbs.GraphDB
|
||||||
|
|
||||||
// Wrap the watchtower client DB and make sure we clean up.
|
// Wrap the watchtower client DB and make sure we clean up.
|
||||||
if cfg.WtClient.Active {
|
if cfg.WtClient.Active {
|
||||||
dbs.TowerClientDB, err = wtdb.OpenClientDB(
|
dbs.TowerClientDB, err = wtdb.OpenClientDB(
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/labels"
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
@@ -1747,7 +1749,7 @@ func (c *ChannelArbitrator) isPreimageAvailable(hash lntypes.Hash) (bool,
|
|||||||
invoice, err := c.cfg.Registry.LookupInvoice(hash)
|
invoice, err := c.cfg.Registry.LookupInvoice(hash)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
case channeldb.ErrInvoiceNotFound, channeldb.ErrNoInvoicesCreated:
|
case invoices.ErrInvoiceNotFound, invoices.ErrNoInvoicesCreated:
|
||||||
return false, nil
|
return false, nil
|
||||||
default:
|
default:
|
||||||
return false, err
|
return false, err
|
||||||
@@ -2230,7 +2232,7 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
for _, htlc := range htlcs {
|
for _, htlc := range htlcs {
|
||||||
htlc := htlc
|
htlc := htlc
|
||||||
|
|
||||||
key := channeldb.CircuitKey{
|
key := models.CircuitKey{
|
||||||
ChanID: c.cfg.ShortChanID,
|
ChanID: c.cfg.ShortChanID,
|
||||||
HtlcID: htlc.HtlcIndex,
|
HtlcID: htlc.HtlcIndex,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
@@ -61,7 +62,7 @@ func (h *htlcIncomingContestResolver) processFinalHtlcFail() error {
|
|||||||
|
|
||||||
// Send notification.
|
// Send notification.
|
||||||
h.ChainArbitratorConfig.HtlcNotifier.NotifyFinalHtlcEvent(
|
h.ChainArbitratorConfig.HtlcNotifier.NotifyFinalHtlcEvent(
|
||||||
channeldb.CircuitKey{
|
models.CircuitKey{
|
||||||
ChanID: h.ShortChanID,
|
ChanID: h.ShortChanID,
|
||||||
HtlcID: h.htlc.HtlcIndex,
|
HtlcID: h.htlc.HtlcIndex,
|
||||||
},
|
},
|
||||||
@@ -265,7 +266,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
// hop on-chain. If this HTLC indeed pays to an existing
|
// hop on-chain. If this HTLC indeed pays to an existing
|
||||||
// invoice, the invoice registry will tell us what to do with
|
// invoice, the invoice registry will tell us what to do with
|
||||||
// the HTLC. This is identical to HTLC resolution in the link.
|
// the HTLC. This is identical to HTLC resolution in the link.
|
||||||
circuitKey := channeldb.CircuitKey{
|
circuitKey := models.CircuitKey{
|
||||||
ChanID: h.ShortChanID,
|
ChanID: h.ShortChanID,
|
||||||
HtlcID: h.htlc.HtlcIndex,
|
HtlcID: h.htlc.HtlcIndex,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
@@ -27,7 +28,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
testResPreimage = lntypes.Preimage{1, 2, 3}
|
testResPreimage = lntypes.Preimage{1, 2, 3}
|
||||||
testResHash = testResPreimage.Hash()
|
testResHash = testResPreimage.Hash()
|
||||||
testResCircuitKey = channeldb.CircuitKey{}
|
testResCircuitKey = models.CircuitKey{}
|
||||||
testOnionBlob = []byte{4, 5, 6}
|
testOnionBlob = []byte{4, 5, 6}
|
||||||
testAcceptHeight int32 = 1234
|
testAcceptHeight int32 = 1234
|
||||||
testHtlcAmount = 2300
|
testHtlcAmount = 2300
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/labels"
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@@ -479,7 +480,7 @@ func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash,
|
|||||||
|
|
||||||
// Send notification.
|
// Send notification.
|
||||||
h.ChainArbitratorConfig.HtlcNotifier.NotifyFinalHtlcEvent(
|
h.ChainArbitratorConfig.HtlcNotifier.NotifyFinalHtlcEvent(
|
||||||
channeldb.CircuitKey{
|
models.CircuitKey{
|
||||||
ChanID: h.ShortChanID,
|
ChanID: h.ShortChanID,
|
||||||
HtlcID: h.htlc.HtlcIndex,
|
HtlcID: h.htlc.HtlcIndex,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
@@ -18,7 +19,7 @@ import (
|
|||||||
type Registry interface {
|
type Registry interface {
|
||||||
// LookupInvoice attempts to look up an invoice according to its 32
|
// LookupInvoice attempts to look up an invoice according to its 32
|
||||||
// byte payment hash.
|
// byte payment hash.
|
||||||
LookupInvoice(lntypes.Hash) (channeldb.Invoice, error)
|
LookupInvoice(lntypes.Hash) (invoices.Invoice, error)
|
||||||
|
|
||||||
// NotifyExitHopHtlc attempts to mark an invoice as settled. If the
|
// NotifyExitHopHtlc attempts to mark an invoice as settled. If the
|
||||||
// invoice is a debug invoice, then this method is a noop as debug
|
// invoice is a debug invoice, then this method is a noop as debug
|
||||||
@@ -27,7 +28,7 @@ type Registry interface {
|
|||||||
// the resolution is sent on the passed in hodlChan later.
|
// the resolution is sent on the passed in hodlChan later.
|
||||||
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
||||||
expiry uint32, currentHeight int32,
|
expiry uint32, currentHeight int32,
|
||||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
circuitKey models.CircuitKey, hodlChan chan<- interface{},
|
||||||
payload invoices.Payload) (invoices.HtlcResolution, error)
|
payload invoices.Payload) (invoices.HtlcResolution, error)
|
||||||
|
|
||||||
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
|
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
|
||||||
@@ -70,6 +71,6 @@ type UtxoSweeper interface {
|
|||||||
type HtlcNotifier interface {
|
type HtlcNotifier interface {
|
||||||
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome
|
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome
|
||||||
// for an htlc has been determined.
|
// for an htlc has been determined.
|
||||||
NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||||
info channeldb.FinalHtlcInfo)
|
info channeldb.FinalHtlcInfo)
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
import "github.com/lightningnetwork/lnd/channeldb"
|
import (
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
|
)
|
||||||
|
|
||||||
type mockHTLCNotifier struct {
|
type mockHTLCNotifier struct {
|
||||||
HtlcNotifier
|
HtlcNotifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHTLCNotifier) NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
func (m *mockHTLCNotifier) NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||||
info channeldb.FinalHtlcInfo) { //nolint:whitespace
|
info channeldb.FinalHtlcInfo) { //nolint:whitespace
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -23,7 +23,7 @@ type mockRegistry struct {
|
|||||||
|
|
||||||
func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
|
func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
|
||||||
paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
circuitKey models.CircuitKey, hodlChan chan<- interface{},
|
||||||
payload invoices.Payload) (invoices.HtlcResolution, error) {
|
payload invoices.Payload) (invoices.HtlcResolution, error) {
|
||||||
|
|
||||||
r.notifyChan <- notifyExitHopData{
|
r.notifyChan <- notifyExitHopData{
|
||||||
@@ -39,8 +39,8 @@ func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
|
|||||||
|
|
||||||
func (r *mockRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {}
|
func (r *mockRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {}
|
||||||
|
|
||||||
func (r *mockRegistry) LookupInvoice(lntypes.Hash) (channeldb.Invoice,
|
func (r *mockRegistry) LookupInvoice(lntypes.Hash) (invoices.Invoice,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
return channeldb.Invoice{}, channeldb.ErrInvoiceNotFound
|
return invoices.Invoice{}, invoices.ErrInvoiceNotFound
|
||||||
}
|
}
|
||||||
@@ -120,20 +120,26 @@ current gossip sync query status.
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
[The project has updated to Go
|
* [The project has updated to Go
|
||||||
1.19](https://github.com/lightningnetwork/lnd/pull/6795)! Go 1.18 is now the
|
1.19](https://github.com/lightningnetwork/lnd/pull/6795)! Go 1.18 is now the
|
||||||
minimum version needed to build the project.
|
minimum version needed to build the project.
|
||||||
|
|
||||||
[The minimum recommended version of the Go 1.19.x series is 1.19.2 because
|
* [The minimum recommended version of the Go 1.19.x series is 1.19.2 because
|
||||||
1.19.1 contained a bug that affected lnd and resulted in a
|
1.19.1 contained a bug that affected lnd and resulted in a
|
||||||
crash](https://github.com/lightningnetwork/lnd/pull/7019).
|
crash](https://github.com/lightningnetwork/lnd/pull/7019).
|
||||||
|
|
||||||
[Use Go's `runtime/debug` package to get information about the build](
|
* [Use Go's `runtime/debug` package to get information about the build](
|
||||||
https://github.com/lightningnetwork/lnd/pull/6963/)
|
https://github.com/lightningnetwork/lnd/pull/6963/)
|
||||||
|
|
||||||
[A wire parsing bug has been fixed that would cause lnd to be unable _decode_
|
* [A wire parsing bug has been fixed that would cause lnd to be unable _decode_
|
||||||
certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100).
|
certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100).
|
||||||
|
|
||||||
|
## Invoices
|
||||||
|
|
||||||
|
* Define a new [InvoiceDB](https://github.com/lightningnetwork/lnd/pull/7215)
|
||||||
|
interface to be used in all the packages that need to interact with invoice
|
||||||
|
data.
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
* [Fixed a bug where the Switch did not reforward settles or fails for
|
* [Fixed a bug where the Switch did not reforward settles or fails for
|
||||||
@@ -344,7 +350,7 @@ certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100).
|
|||||||
|
|
||||||
### Integration test
|
### Integration test
|
||||||
|
|
||||||
The `lntest` has been
|
* The `lntest` has been
|
||||||
[refactored](https://github.com/lightningnetwork/lnd/pull/6759) to provide a
|
[refactored](https://github.com/lightningnetwork/lnd/pull/6759) to provide a
|
||||||
better testing suite for writing integration tests. A new defined structure is
|
better testing suite for writing integration tests. A new defined structure is
|
||||||
implemented, please refer to
|
implemented, please refer to
|
||||||
@@ -379,7 +385,6 @@ refactor the itest for code health and maintenance.
|
|||||||
* lsunsi
|
* lsunsi
|
||||||
* Matt Morehouse
|
* Matt Morehouse
|
||||||
* Michael Street
|
* Michael Street
|
||||||
* Jordi Montes
|
|
||||||
* Olaoluwa Osuntokun
|
* Olaoluwa Osuntokun
|
||||||
* Oliver Gugger
|
* Oliver Gugger
|
||||||
* Priyansh Rastogi
|
* Priyansh Rastogi
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
@@ -19,7 +20,7 @@ var EmptyCircuitKey CircuitKey
|
|||||||
// HTLCs in a circuit. Circuits are identified primarily by the circuit key of
|
// HTLCs in a circuit. Circuits are identified primarily by the circuit key of
|
||||||
// the incoming HTLC. However, a circuit may also be referenced by its outgoing
|
// the incoming HTLC. However, a circuit may also be referenced by its outgoing
|
||||||
// circuit key after the HTLC has been forwarded via the outgoing link.
|
// circuit key after the HTLC has been forwarded via the outgoing link.
|
||||||
type CircuitKey = channeldb.CircuitKey
|
type CircuitKey = models.CircuitKey
|
||||||
|
|
||||||
// PaymentCircuit is used by the switch as placeholder between when the
|
// PaymentCircuit is used by the switch as placeholder between when the
|
||||||
// switch makes a forwarding decision and the outgoing link determines the
|
// switch makes a forwarding decision and the outgoing link determines the
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// heldHtlcSet keeps track of outstanding intercepted forwards. It exposes
|
// heldHtlcSet keeps track of outstanding intercepted forwards. It exposes
|
||||||
// several methods to manipulate the underlying map structure in a consistent
|
// several methods to manipulate the underlying map structure in a consistent
|
||||||
// way.
|
// way.
|
||||||
type heldHtlcSet struct {
|
type heldHtlcSet struct {
|
||||||
set map[channeldb.CircuitKey]InterceptedForward
|
set map[models.CircuitKey]InterceptedForward
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHeldHtlcSet() *heldHtlcSet {
|
func newHeldHtlcSet() *heldHtlcSet {
|
||||||
return &heldHtlcSet{
|
return &heldHtlcSet{
|
||||||
set: make(map[channeldb.CircuitKey]InterceptedForward),
|
set: make(map[models.CircuitKey]InterceptedForward),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func (h *heldHtlcSet) popAll(cb func(InterceptedForward)) {
|
|||||||
cb(fwd)
|
cb(fwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
h.set = make(map[channeldb.CircuitKey]InterceptedForward)
|
h.set = make(map[models.CircuitKey]InterceptedForward)
|
||||||
}
|
}
|
||||||
|
|
||||||
// popAutoFails calls the callback for each forward that has an auto-fail height
|
// popAutoFails calls the callback for each forward that has an auto-fail height
|
||||||
@@ -52,7 +52,7 @@ func (h *heldHtlcSet) popAutoFails(height uint32, cb func(InterceptedForward)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pop returns the specified forward and removes it from the set.
|
// pop returns the specified forward and removes it from the set.
|
||||||
func (h *heldHtlcSet) pop(key channeldb.CircuitKey) (InterceptedForward, error) {
|
func (h *heldHtlcSet) pop(key models.CircuitKey) (InterceptedForward, error) {
|
||||||
intercepted, ok := h.set[key]
|
intercepted, ok := h.set[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("fwd %v not found", key)
|
return nil, fmt.Errorf("fwd %v not found", key)
|
||||||
@@ -64,7 +64,7 @@ func (h *heldHtlcSet) pop(key channeldb.CircuitKey) (InterceptedForward, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// exists tests whether the specified forward is part of the set.
|
// exists tests whether the specified forward is part of the set.
|
||||||
func (h *heldHtlcSet) exists(key channeldb.CircuitKey) bool {
|
func (h *heldHtlcSet) exists(key models.CircuitKey) bool {
|
||||||
_, ok := h.set[key]
|
_, ok := h.set[key]
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
@@ -72,7 +72,7 @@ func (h *heldHtlcSet) exists(key channeldb.CircuitKey) bool {
|
|||||||
|
|
||||||
// push adds the specified forward to the set. An error is returned if the
|
// push adds the specified forward to the set. An error is returned if the
|
||||||
// forward exists already.
|
// forward exists already.
|
||||||
func (h *heldHtlcSet) push(key channeldb.CircuitKey,
|
func (h *heldHtlcSet) push(key models.CircuitKey,
|
||||||
fwd InterceptedForward) error {
|
fwd InterceptedForward) error {
|
||||||
|
|
||||||
if fwd == nil {
|
if fwd == nil {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package htlcswitch
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -12,9 +12,9 @@ func TestHeldHtlcSetEmpty(t *testing.T) {
|
|||||||
set := newHeldHtlcSet()
|
set := newHeldHtlcSet()
|
||||||
|
|
||||||
// Test operations on an empty set.
|
// Test operations on an empty set.
|
||||||
require.False(t, set.exists(channeldb.CircuitKey{}))
|
require.False(t, set.exists(models.CircuitKey{}))
|
||||||
|
|
||||||
_, err := set.pop(channeldb.CircuitKey{})
|
_, err := set.pop(models.CircuitKey{})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
set.popAll(
|
set.popAll(
|
||||||
@@ -27,7 +27,7 @@ func TestHeldHtlcSetEmpty(t *testing.T) {
|
|||||||
func TestHeldHtlcSet(t *testing.T) {
|
func TestHeldHtlcSet(t *testing.T) {
|
||||||
set := newHeldHtlcSet()
|
set := newHeldHtlcSet()
|
||||||
|
|
||||||
key := channeldb.CircuitKey{
|
key := models.CircuitKey{
|
||||||
ChanID: lnwire.NewShortChanIDFromInt(1),
|
ChanID: lnwire.NewShortChanIDFromInt(1),
|
||||||
HtlcID: 2,
|
HtlcID: 2,
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ func TestHeldHtlcSet(t *testing.T) {
|
|||||||
func TestHeldHtlcSetAutoFails(t *testing.T) {
|
func TestHeldHtlcSetAutoFails(t *testing.T) {
|
||||||
set := newHeldHtlcSet()
|
set := newHeldHtlcSet()
|
||||||
|
|
||||||
key := channeldb.CircuitKey{
|
key := models.CircuitKey{
|
||||||
ChanID: lnwire.NewShortChanIDFromInt(1),
|
ChanID: lnwire.NewShortChanIDFromInt(1),
|
||||||
HtlcID: 2,
|
HtlcID: 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -108,10 +109,10 @@ func (h *HtlcNotifier) SubscribeHtlcEvents() (*subscribe.Client, error) {
|
|||||||
// HtlcKey uniquely identifies the htlc.
|
// HtlcKey uniquely identifies the htlc.
|
||||||
type HtlcKey struct {
|
type HtlcKey struct {
|
||||||
// IncomingCircuit is the channel an htlc id of the incoming htlc.
|
// IncomingCircuit is the channel an htlc id of the incoming htlc.
|
||||||
IncomingCircuit channeldb.CircuitKey
|
IncomingCircuit models.CircuitKey
|
||||||
|
|
||||||
// OutgoingCircuit is the channel and htlc id of the outgoing htlc.
|
// OutgoingCircuit is the channel and htlc id of the outgoing htlc.
|
||||||
OutgoingCircuit channeldb.CircuitKey
|
OutgoingCircuit models.CircuitKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a string representation of a htlc key.
|
// String returns a string representation of a htlc key.
|
||||||
@@ -398,7 +399,7 @@ func (h *HtlcNotifier) NotifySettleEvent(key HtlcKey,
|
|||||||
// htlc has been determined.
|
// htlc has been determined.
|
||||||
//
|
//
|
||||||
// Note this is part of the htlcNotifier interface.
|
// Note this is part of the htlcNotifier interface.
|
||||||
func (h *HtlcNotifier) NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
func (h *HtlcNotifier) NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||||
info channeldb.FinalHtlcInfo) {
|
info channeldb.FinalHtlcInfo) {
|
||||||
|
|
||||||
event := &FinalHtlcEvent{
|
event := &FinalHtlcEvent{
|
||||||
@@ -423,7 +424,7 @@ func (h *HtlcNotifier) NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
|||||||
// originate at our node.
|
// originate at our node.
|
||||||
func newHtlcKey(pkt *htlcPacket) HtlcKey {
|
func newHtlcKey(pkt *htlcPacket) HtlcKey {
|
||||||
htlcKey := HtlcKey{
|
htlcKey := HtlcKey{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: pkt.incomingChanID,
|
ChanID: pkt.incomingChanID,
|
||||||
HtlcID: pkt.incomingHTLCID,
|
HtlcID: pkt.incomingHTLCID,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -110,7 +110,7 @@ const (
|
|||||||
// FwdResolution defines the action to be taken on an intercepted packet.
|
// FwdResolution defines the action to be taken on an intercepted packet.
|
||||||
type FwdResolution struct {
|
type FwdResolution struct {
|
||||||
// Key is the incoming circuit key of the htlc.
|
// Key is the incoming circuit key of the htlc.
|
||||||
Key channeldb.CircuitKey
|
Key models.CircuitKey
|
||||||
|
|
||||||
// Action is the action to take on the intercepted htlc.
|
// Action is the action to take on the intercepted htlc.
|
||||||
Action FwdAction
|
Action FwdAction
|
||||||
@@ -592,7 +592,7 @@ type interceptedForward struct {
|
|||||||
// Packet returns the intercepted htlc packet.
|
// Packet returns the intercepted htlc packet.
|
||||||
func (f *interceptedForward) Packet() InterceptedPacket {
|
func (f *interceptedForward) Packet() InterceptedPacket {
|
||||||
return InterceptedPacket{
|
return InterceptedPacket{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: f.packet.incomingChanID,
|
ChanID: f.packet.incomingChanID,
|
||||||
HtlcID: f.packet.incomingHTLCID,
|
HtlcID: f.packet.incomingHTLCID,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package htlcswitch
|
|||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lnpeer"
|
"github.com/lightningnetwork/lnd/lnpeer"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
@@ -17,7 +18,7 @@ import (
|
|||||||
type InvoiceDatabase interface {
|
type InvoiceDatabase interface {
|
||||||
// LookupInvoice attempts to look up an invoice according to its 32
|
// LookupInvoice attempts to look up an invoice according to its 32
|
||||||
// byte payment hash.
|
// byte payment hash.
|
||||||
LookupInvoice(lntypes.Hash) (channeldb.Invoice, error)
|
LookupInvoice(lntypes.Hash) (invoices.Invoice, error)
|
||||||
|
|
||||||
// NotifyExitHopHtlc attempts to mark an invoice as settled. If the
|
// NotifyExitHopHtlc attempts to mark an invoice as settled. If the
|
||||||
// invoice is a debug invoice, then this method is a noop as debug
|
// invoice is a debug invoice, then this method is a noop as debug
|
||||||
@@ -28,7 +29,7 @@ type InvoiceDatabase interface {
|
|||||||
// for decoding purposes.
|
// for decoding purposes.
|
||||||
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
||||||
expiry uint32, currentHeight int32,
|
expiry uint32, currentHeight int32,
|
||||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
circuitKey models.CircuitKey, hodlChan chan<- interface{},
|
||||||
payload invoices.Payload) (invoices.HtlcResolution, error)
|
payload invoices.Payload) (invoices.HtlcResolution, error)
|
||||||
|
|
||||||
// CancelInvoice attempts to cancel the invoice corresponding to the
|
// CancelInvoice attempts to cancel the invoice corresponding to the
|
||||||
@@ -284,7 +285,7 @@ type ForwardInterceptor func(InterceptedPacket) error
|
|||||||
type InterceptedPacket struct {
|
type InterceptedPacket struct {
|
||||||
// IncomingCircuit contains the incoming channel and htlc id of the
|
// IncomingCircuit contains the incoming channel and htlc id of the
|
||||||
// packet.
|
// packet.
|
||||||
IncomingCircuit channeldb.CircuitKey
|
IncomingCircuit models.CircuitKey
|
||||||
|
|
||||||
// OutgoingChanID is the destination channel for this packet.
|
// OutgoingChanID is the destination channel for this packet.
|
||||||
OutgoingChanID lnwire.ShortChannelID
|
OutgoingChanID lnwire.ShortChannelID
|
||||||
@@ -373,6 +374,6 @@ type htlcNotifier interface {
|
|||||||
|
|
||||||
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome
|
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome
|
||||||
// for an htlc has been determined.
|
// for an htlc has been determined.
|
||||||
NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||||
info channeldb.FinalHtlcInfo)
|
info channeldb.FinalHtlcInfo)
|
||||||
}
|
}
|
||||||
+7
-6
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
@@ -395,7 +396,7 @@ type channelLink struct {
|
|||||||
|
|
||||||
// hodlMap stores related htlc data for a circuit key. It allows
|
// hodlMap stores related htlc data for a circuit key. It allows
|
||||||
// resolving those htlcs when we receive a message on hodlQueue.
|
// resolving those htlcs when we receive a message on hodlQueue.
|
||||||
hodlMap map[channeldb.CircuitKey]hodlHtlc
|
hodlMap map[models.CircuitKey]hodlHtlc
|
||||||
|
|
||||||
// log is a link-specific logging instance.
|
// log is a link-specific logging instance.
|
||||||
log btclog.Logger
|
log btclog.Logger
|
||||||
@@ -422,7 +423,7 @@ func NewChannelLink(cfg ChannelLinkConfig,
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
shortChanID: channel.ShortChanID(),
|
shortChanID: channel.ShortChanID(),
|
||||||
shutdownRequest: make(chan *shutdownReq),
|
shutdownRequest: make(chan *shutdownReq),
|
||||||
hodlMap: make(map[channeldb.CircuitKey]hodlHtlc),
|
hodlMap: make(map[models.CircuitKey]hodlHtlc),
|
||||||
hodlQueue: queue.NewConcurrentQueue(10),
|
hodlQueue: queue.NewConcurrentQueue(10),
|
||||||
log: build.NewPrefixLog(logPrefix, log),
|
log: build.NewPrefixLog(logPrefix, log),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
@@ -1962,7 +1963,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
|
|||||||
// locked in.
|
// locked in.
|
||||||
for id, settled := range finalHTLCs {
|
for id, settled := range finalHTLCs {
|
||||||
l.cfg.HtlcNotifier.NotifyFinalHtlcEvent(
|
l.cfg.HtlcNotifier.NotifyFinalHtlcEvent(
|
||||||
channeldb.CircuitKey{
|
models.CircuitKey{
|
||||||
ChanID: l.shortChanID,
|
ChanID: l.shortChanID,
|
||||||
HtlcID: id,
|
HtlcID: id,
|
||||||
},
|
},
|
||||||
@@ -3260,7 +3261,7 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
|
|||||||
// receive back a resolution event.
|
// receive back a resolution event.
|
||||||
invoiceHash := lntypes.Hash(pd.RHash)
|
invoiceHash := lntypes.Hash(pd.RHash)
|
||||||
|
|
||||||
circuitKey := channeldb.CircuitKey{
|
circuitKey := models.CircuitKey{
|
||||||
ChanID: l.ShortChanID(),
|
ChanID: l.ShortChanID(),
|
||||||
HtlcID: pd.HtlcIndex,
|
HtlcID: pd.HtlcIndex,
|
||||||
}
|
}
|
||||||
@@ -3324,7 +3325,7 @@ func (l *channelLink) settleHTLC(preimage lntypes.Preimage,
|
|||||||
// Once we have successfully settled the htlc, notify a settle event.
|
// Once we have successfully settled the htlc, notify a settle event.
|
||||||
l.cfg.HtlcNotifier.NotifySettleEvent(
|
l.cfg.HtlcNotifier.NotifySettleEvent(
|
||||||
HtlcKey{
|
HtlcKey{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: l.ShortChanID(),
|
ChanID: l.ShortChanID(),
|
||||||
HtlcID: pd.HtlcIndex,
|
HtlcID: pd.HtlcIndex,
|
||||||
},
|
},
|
||||||
@@ -3394,7 +3395,7 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor,
|
|||||||
|
|
||||||
l.cfg.HtlcNotifier.NotifyLinkFailEvent(
|
l.cfg.HtlcNotifier.NotifyLinkFailEvent(
|
||||||
HtlcKey{
|
HtlcKey{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: l.ShortChanID(),
|
ChanID: l.ShortChanID(),
|
||||||
HtlcID: pd.HtlcIndex,
|
HtlcID: pd.HtlcIndex,
|
||||||
},
|
},
|
||||||
|
|||||||
+12
-11
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
invpkg "github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lnpeer"
|
"github.com/lightningnetwork/lnd/lnpeer"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
@@ -479,7 +480,7 @@ func TestChannelLinkSingleHopPayment(t *testing.T) {
|
|||||||
// links was changed.
|
// links was changed.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State != channeldb.ContractSettled {
|
if invoice.State != invpkg.ContractSettled {
|
||||||
t.Fatal("alice invoice wasn't settled")
|
t.Fatal("alice invoice wasn't settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +598,7 @@ func testChannelLinkMultiHopPayment(t *testing.T,
|
|||||||
// links were changed.
|
// links were changed.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State != channeldb.ContractSettled {
|
if invoice.State != invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice haven't been settled")
|
t.Fatal("carol invoice haven't been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,7 +1081,7 @@ func TestUpdateForwardingPolicy(t *testing.T) {
|
|||||||
// succeeded.
|
// succeeded.
|
||||||
invoice, err := n.carolServer.registry.LookupInvoice(payResp)
|
invoice, err := n.carolServer.registry.LookupInvoice(payResp)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State != channeldb.ContractSettled {
|
if invoice.State != invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice haven't been settled")
|
t.Fatal("carol invoice haven't been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1234,7 +1235,7 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) {
|
|||||||
// links hasn't been changed.
|
// links hasn't been changed.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State == channeldb.ContractSettled {
|
if invoice.State == invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice have been settled")
|
t.Fatal("carol invoice have been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1412,7 +1413,7 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) {
|
|||||||
// links hasn't been changed.
|
// links hasn't been changed.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State == channeldb.ContractSettled {
|
if invoice.State == invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice have been settled")
|
t.Fatal("carol invoice have been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1520,7 +1521,7 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
|
|||||||
// links hasn't been changed.
|
// links hasn't been changed.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State == channeldb.ContractSettled {
|
if invoice.State == invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice have been settled")
|
t.Fatal("carol invoice have been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3498,7 +3499,7 @@ func TestChannelRetransmission(t *testing.T) {
|
|||||||
// TODO(andrew.shvv) Will be removed if we move the notification center
|
// TODO(andrew.shvv) Will be removed if we move the notification center
|
||||||
// to the channel link itself.
|
// to the channel link itself.
|
||||||
|
|
||||||
var invoice channeldb.Invoice
|
var invoice invpkg.Invoice
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Millisecond * 200):
|
case <-time.After(time.Millisecond * 200):
|
||||||
@@ -3513,7 +3514,7 @@ func TestChannelRetransmission(t *testing.T) {
|
|||||||
err = errors.Errorf("unable to get invoice: %v", err)
|
err = errors.Errorf("unable to get invoice: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if invoice.State != channeldb.ContractSettled {
|
if invoice.State != invpkg.ContractSettled {
|
||||||
err = errors.Errorf("alice invoice haven't been settled")
|
err = errors.Errorf("alice invoice haven't been settled")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -4059,7 +4060,7 @@ func TestChannelLinkAcceptOverpay(t *testing.T) {
|
|||||||
// accepted the payment and marked it as settled.
|
// accepted the payment and marked it as settled.
|
||||||
invoice, err := receiver.registry.LookupInvoice(rhash)
|
invoice, err := receiver.registry.LookupInvoice(rhash)
|
||||||
require.NoError(t, err, "unable to get invoice")
|
require.NoError(t, err, "unable to get invoice")
|
||||||
if invoice.State != channeldb.ContractSettled {
|
if invoice.State != invpkg.ContractSettled {
|
||||||
t.Fatal("carol invoice haven't been settled")
|
t.Fatal("carol invoice haven't been settled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4383,7 +4384,7 @@ func generateHtlc(t *testing.T, coreLink *channelLink,
|
|||||||
// generateHtlcAndInvoice generates an invoice and a single hop htlc to send to
|
// generateHtlcAndInvoice generates an invoice and a single hop htlc to send to
|
||||||
// the receiver.
|
// the receiver.
|
||||||
func generateHtlcAndInvoice(t *testing.T,
|
func generateHtlcAndInvoice(t *testing.T,
|
||||||
id uint64) (*lnwire.UpdateAddHTLC, *channeldb.Invoice) {
|
id uint64) (*lnwire.UpdateAddHTLC, *invpkg.Invoice) {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
@@ -4778,7 +4779,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
|
|||||||
// We will send 10 HTLCs in total, from Bob to Alice.
|
// We will send 10 HTLCs in total, from Bob to Alice.
|
||||||
numHtlcs := 10
|
numHtlcs := 10
|
||||||
var htlcs []*lnwire.UpdateAddHTLC
|
var htlcs []*lnwire.UpdateAddHTLC
|
||||||
var invoices []*channeldb.Invoice
|
var invoices []*invpkg.Invoice
|
||||||
for i := 0; i < numHtlcs; i++ {
|
for i := 0; i < numHtlcs; i++ {
|
||||||
htlc, invoice := generateHtlcAndInvoice(t, uint64(i))
|
htlc, invoice := generateHtlcAndInvoice(t, uint64(i))
|
||||||
htlcs = append(htlcs, htlc)
|
htlcs = append(htlcs, htlc)
|
||||||
|
|||||||
+18
-11
@@ -23,6 +23,7 @@ import (
|
|||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
@@ -845,7 +846,9 @@ func (f *mockChannelLink) CheckHtlcTransit(payHash [32]byte,
|
|||||||
return f.checkHtlcTransitResult
|
return f.checkHtlcTransitResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *mockChannelLink) Stats() (uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
|
func (f *mockChannelLink) Stats() (
|
||||||
|
uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
|
||||||
|
|
||||||
return 0, 0, 0
|
return 0, 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,18 +980,20 @@ func newMockRegistry(minDelta uint32) *mockInvoiceRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (
|
func (i *mockInvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (
|
||||||
channeldb.Invoice, error) {
|
invoices.Invoice, error) {
|
||||||
|
|
||||||
return i.registry.LookupInvoice(rHash)
|
return i.registry.LookupInvoice(rHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
func (i *mockInvoiceRegistry) SettleHodlInvoice(
|
||||||
|
preimage lntypes.Preimage) error {
|
||||||
|
|
||||||
return i.registry.SettleHodlInvoice(preimage)
|
return i.registry.SettleHodlInvoice(preimage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
|
func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
|
||||||
amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
circuitKey models.CircuitKey, hodlChan chan<- interface{},
|
||||||
payload invoices.Payload) (invoices.HtlcResolution, error) {
|
payload invoices.Payload) (invoices.HtlcResolution, error) {
|
||||||
|
|
||||||
event, err := i.registry.NotifyExitHopHtlc(
|
event, err := i.registry.NotifyExitHopHtlc(
|
||||||
@@ -1009,14 +1014,16 @@ func (i *mockInvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
|
|||||||
return i.registry.CancelInvoice(payHash)
|
return i.registry.CancelInvoice(payHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) AddInvoice(invoice channeldb.Invoice,
|
func (i *mockInvoiceRegistry) AddInvoice(invoice invoices.Invoice,
|
||||||
paymentHash lntypes.Hash) error {
|
paymentHash lntypes.Hash) error {
|
||||||
|
|
||||||
_, err := i.registry.AddInvoice(&invoice, paymentHash)
|
_, err := i.registry.AddInvoice(&invoice, paymentHash)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
|
func (i *mockInvoiceRegistry) HodlUnsubscribeAll(
|
||||||
|
subscriber chan<- interface{}) {
|
||||||
|
|
||||||
i.registry.HodlUnsubscribeAll(subscriber)
|
i.registry.HodlUnsubscribeAll(subscriber)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,22 +1106,22 @@ type mockHTLCNotifier struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockHTLCNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
|
func (h *mockHTLCNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
|
||||||
eventType HtlcEventType) { // nolint:whitespace
|
eventType HtlcEventType) { //nolint:whitespace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockHTLCNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
|
func (h *mockHTLCNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
|
||||||
eventType HtlcEventType, linkErr *LinkError,
|
eventType HtlcEventType, linkErr *LinkError,
|
||||||
incoming bool) { // nolint:whitespace
|
incoming bool) { //nolint:whitespace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockHTLCNotifier) NotifyForwardingFailEvent(key HtlcKey,
|
func (h *mockHTLCNotifier) NotifyForwardingFailEvent(key HtlcKey,
|
||||||
eventType HtlcEventType) { // nolint:whitespace
|
eventType HtlcEventType) { //nolint:whitespace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockHTLCNotifier) NotifySettleEvent(key HtlcKey,
|
func (h *mockHTLCNotifier) NotifySettleEvent(key HtlcKey,
|
||||||
preimage lntypes.Preimage, eventType HtlcEventType) { // nolint:whitespace
|
preimage lntypes.Preimage, eventType HtlcEventType) { //nolint:whitespace,lll
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockHTLCNotifier) NotifyFinalHtlcEvent(key channeldb.CircuitKey,
|
func (h *mockHTLCNotifier) NotifyFinalHtlcEvent(key models.CircuitKey,
|
||||||
info channeldb.FinalHtlcInfo) { //nolint:whitespace
|
info channeldb.FinalHtlcInfo) { //nolint:whitespace
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
@@ -640,7 +641,7 @@ func (s *Switch) UpdateForwardingPolicies(
|
|||||||
func (s *Switch) IsForwardedHTLC(chanID lnwire.ShortChannelID,
|
func (s *Switch) IsForwardedHTLC(chanID lnwire.ShortChannelID,
|
||||||
htlcIndex uint64) bool {
|
htlcIndex uint64) bool {
|
||||||
|
|
||||||
circuit := s.circuits.LookupOpenCircuit(channeldb.CircuitKey{
|
circuit := s.circuits.LookupOpenCircuit(models.CircuitKey{
|
||||||
ChanID: chanID,
|
ChanID: chanID,
|
||||||
HtlcID: htlcIndex,
|
HtlcID: htlcIndex,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
@@ -25,7 +26,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var zeroCircuit = channeldb.CircuitKey{}
|
var zeroCircuit = models.CircuitKey{}
|
||||||
var emptyScid = lnwire.ShortChannelID{}
|
var emptyScid = lnwire.ShortChannelID{}
|
||||||
|
|
||||||
func genPreimage() ([32]byte, error) {
|
func genPreimage() ([32]byte, error) {
|
||||||
@@ -3585,7 +3586,7 @@ func getThreeHopEvents(channels *clusterChannels, htlcID uint64,
|
|||||||
|
|
||||||
aliceKey := HtlcKey{
|
aliceKey := HtlcKey{
|
||||||
IncomingCircuit: zeroCircuit,
|
IncomingCircuit: zeroCircuit,
|
||||||
OutgoingCircuit: channeldb.CircuitKey{
|
OutgoingCircuit: models.CircuitKey{
|
||||||
ChanID: channels.aliceToBob.ShortChanID(),
|
ChanID: channels.aliceToBob.ShortChanID(),
|
||||||
HtlcID: htlcID,
|
HtlcID: htlcID,
|
||||||
},
|
},
|
||||||
@@ -3606,11 +3607,11 @@ func getThreeHopEvents(channels *clusterChannels, htlcID uint64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bobKey := HtlcKey{
|
bobKey := HtlcKey{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: channels.bobToAlice.ShortChanID(),
|
ChanID: channels.bobToAlice.ShortChanID(),
|
||||||
HtlcID: htlcID,
|
HtlcID: htlcID,
|
||||||
},
|
},
|
||||||
OutgoingCircuit: channeldb.CircuitKey{
|
OutgoingCircuit: models.CircuitKey{
|
||||||
ChanID: channels.bobToCarol.ShortChanID(),
|
ChanID: channels.bobToCarol.ShortChanID(),
|
||||||
HtlcID: htlcID,
|
HtlcID: htlcID,
|
||||||
},
|
},
|
||||||
@@ -3691,7 +3692,7 @@ func getThreeHopEvents(channels *clusterChannels, htlcID uint64,
|
|||||||
carolEvents := []interface{}{
|
carolEvents := []interface{}{
|
||||||
&SettleEvent{
|
&SettleEvent{
|
||||||
HtlcKey: HtlcKey{
|
HtlcKey: HtlcKey{
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: channels.carolToBob.ShortChanID(),
|
ChanID: channels.carolToBob.ShortChanID(),
|
||||||
HtlcID: htlcID,
|
HtlcID: htlcID,
|
||||||
},
|
},
|
||||||
@@ -3701,7 +3702,7 @@ func getThreeHopEvents(channels *clusterChannels, htlcID uint64,
|
|||||||
HtlcEventType: HtlcEventTypeReceive,
|
HtlcEventType: HtlcEventTypeReceive,
|
||||||
Timestamp: ts,
|
Timestamp: ts,
|
||||||
}, &FinalHtlcEvent{
|
}, &FinalHtlcEvent{
|
||||||
CircuitKey: channeldb.CircuitKey{
|
CircuitKey: models.CircuitKey{
|
||||||
ChanID: channels.carolToBob.ShortChanID(),
|
ChanID: channels.carolToBob.ShortChanID(),
|
||||||
HtlcID: htlcID,
|
HtlcID: htlcID,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lnpeer"
|
"github.com/lightningnetwork/lnd/lnpeer"
|
||||||
@@ -510,7 +511,7 @@ func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
|
|||||||
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 *lntypes.Preimage, rhash, payAddr [32]byte) (
|
preimage *lntypes.Preimage, rhash, payAddr [32]byte) (
|
||||||
*channeldb.Invoice, *lnwire.UpdateAddHTLC, uint64, error) {
|
*invoices.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.
|
||||||
@@ -519,9 +520,9 @@ func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|||||||
// 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 := &invoices.Invoice{
|
||||||
CreationDate: time.Now(),
|
CreationDate: time.Now(),
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invoices.ContractTerm{
|
||||||
FinalCltvDelta: testInvoiceCltvExpiry,
|
FinalCltvDelta: testInvoiceCltvExpiry,
|
||||||
Value: invoiceAmt,
|
Value: invoiceAmt,
|
||||||
PaymentPreimage: preimage,
|
PaymentPreimage: preimage,
|
||||||
@@ -552,7 +553,7 @@ func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
|||||||
// generatePayment generates the htlc add request by given path blob and
|
// generatePayment generates the htlc add request by given path blob and
|
||||||
// invoice which should be added by destination peer.
|
// invoice which should be added by destination peer.
|
||||||
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
||||||
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice,
|
blob [lnwire.OnionPacketSize]byte) (*invoices.Invoice,
|
||||||
*lnwire.UpdateAddHTLC, uint64, error) {
|
*lnwire.UpdateAddHTLC, uint64, error) {
|
||||||
|
|
||||||
var preimage lntypes.Preimage
|
var preimage lntypes.Preimage
|
||||||
@@ -753,7 +754,7 @@ func makePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
|||||||
func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
||||||
firstHop lnwire.ShortChannelID, hops []*hop.Payload,
|
firstHop lnwire.ShortChannelID, hops []*hop.Payload,
|
||||||
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
||||||
timelock uint32) (*channeldb.Invoice, func() error, error) {
|
timelock uint32) (*invoices.Invoice, func() error, error) {
|
||||||
|
|
||||||
sender := sendingPeer.(*mockServer)
|
sender := sendingPeer.(*mockServer)
|
||||||
receiver := receivingPeer.(*mockServer)
|
receiver := receivingPeer.(*mockServer)
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package invoices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvoiceAlreadySettled is returned when the invoice is already
|
||||||
|
// settled.
|
||||||
|
ErrInvoiceAlreadySettled = errors.New("invoice already settled")
|
||||||
|
|
||||||
|
// ErrInvoiceAlreadyCanceled is returned when the invoice is already
|
||||||
|
// canceled.
|
||||||
|
ErrInvoiceAlreadyCanceled = errors.New("invoice already canceled")
|
||||||
|
|
||||||
|
// ErrInvoiceAlreadyAccepted is returned when the invoice is already
|
||||||
|
// accepted.
|
||||||
|
ErrInvoiceAlreadyAccepted = errors.New("invoice already accepted")
|
||||||
|
|
||||||
|
// ErrInvoiceStillOpen is returned when the invoice is still open.
|
||||||
|
ErrInvoiceStillOpen = errors.New("invoice still open")
|
||||||
|
|
||||||
|
// ErrInvoiceCannotOpen is returned when an attempt is made to move an
|
||||||
|
// invoice to the open state.
|
||||||
|
ErrInvoiceCannotOpen = errors.New("cannot move invoice to open")
|
||||||
|
|
||||||
|
// ErrInvoiceCannotAccept is returned when an attempt is made to accept
|
||||||
|
// an invoice while the invoice is not in the open state.
|
||||||
|
ErrInvoiceCannotAccept = errors.New("cannot accept invoice")
|
||||||
|
|
||||||
|
// ErrInvoicePreimageMismatch is returned when the preimage doesn't
|
||||||
|
// match the invoice hash.
|
||||||
|
ErrInvoicePreimageMismatch = errors.New("preimage does not match")
|
||||||
|
|
||||||
|
// ErrHTLCPreimageMissing is returned when trying to accept/settle an
|
||||||
|
// AMP HTLC but the HTLC-level preimage has not been set.
|
||||||
|
ErrHTLCPreimageMissing = errors.New("AMP htlc missing preimage")
|
||||||
|
|
||||||
|
// ErrHTLCPreimageMismatch is returned when trying to accept/settle an
|
||||||
|
// AMP HTLC but the HTLC-level preimage does not satisfying the
|
||||||
|
// HTLC-level payment hash.
|
||||||
|
ErrHTLCPreimageMismatch = errors.New("htlc preimage mismatch")
|
||||||
|
|
||||||
|
// ErrHTLCAlreadySettled is returned when trying to settle an invoice
|
||||||
|
// but HTLC already exists in the settled state.
|
||||||
|
ErrHTLCAlreadySettled = errors.New("htlc already settled")
|
||||||
|
|
||||||
|
// ErrInvoiceHasHtlcs is returned when attempting to insert an invoice
|
||||||
|
// that already has HTLCs.
|
||||||
|
ErrInvoiceHasHtlcs = errors.New("cannot add invoice with htlcs")
|
||||||
|
|
||||||
|
// ErrEmptyHTLCSet is returned when attempting to accept or settle and
|
||||||
|
// HTLC set that has no HTLCs.
|
||||||
|
ErrEmptyHTLCSet = errors.New("cannot settle/accept empty HTLC set")
|
||||||
|
|
||||||
|
// ErrUnexpectedInvoicePreimage is returned when an invoice-level
|
||||||
|
// preimage is provided when trying to settle an invoice that shouldn't
|
||||||
|
// have one, e.g. an AMP invoice.
|
||||||
|
ErrUnexpectedInvoicePreimage = errors.New(
|
||||||
|
"unexpected invoice preimage provided on settle",
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrHTLCPreimageAlreadyExists is returned when trying to set an
|
||||||
|
// htlc-level preimage but one is already known.
|
||||||
|
ErrHTLCPreimageAlreadyExists = errors.New(
|
||||||
|
"htlc-level preimage already exists",
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrInvoiceNotFound is returned when a targeted invoice can't be
|
||||||
|
// found.
|
||||||
|
ErrInvoiceNotFound = errors.New("unable to locate invoice")
|
||||||
|
|
||||||
|
// ErrNoInvoicesCreated is returned when we don't have invoices in
|
||||||
|
// our database to return.
|
||||||
|
ErrNoInvoicesCreated = errors.New("there are no existing invoices")
|
||||||
|
|
||||||
|
// ErrDuplicateInvoice is returned when an invoice with the target
|
||||||
|
// payment hash already exists.
|
||||||
|
ErrDuplicateInvoice = errors.New(
|
||||||
|
"invoice with payment hash already exists",
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrDuplicatePayAddr is returned when an invoice with the target
|
||||||
|
// payment addr already exists.
|
||||||
|
ErrDuplicatePayAddr = errors.New(
|
||||||
|
"invoice with payemnt addr already exists",
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrInvRefEquivocation is returned when an InvoiceRef targets
|
||||||
|
// multiple, distinct invoices.
|
||||||
|
ErrInvRefEquivocation = errors.New("inv ref matches multiple invoices")
|
||||||
|
|
||||||
|
// ErrNoPaymentsCreated is returned when bucket of payments hasn't been
|
||||||
|
// created.
|
||||||
|
ErrNoPaymentsCreated = errors.New("there are no existing payments")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrDuplicateSetID is an error returned when attempting to adding an AMP HTLC
|
||||||
|
// to an invoice, but another invoice is already indexed by the same set id.
|
||||||
|
type ErrDuplicateSetID struct {
|
||||||
|
SetID [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a human-readable description of ErrDuplicateSetID.
|
||||||
|
func (e ErrDuplicateSetID) Error() string {
|
||||||
|
return fmt.Sprintf("invoice with set_id=%x already exists", e.SetID)
|
||||||
|
}
|
||||||
@@ -1,9 +1,91 @@
|
|||||||
package invoices
|
package invoices
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// InvScanFunc is a helper type used to specify the type used in the
|
||||||
|
// ScanInvoices methods (part of the InvoiceDB interface).
|
||||||
|
type InvScanFunc func(lntypes.Hash, *Invoice) error
|
||||||
|
|
||||||
|
// InvoiceDB is the database that stores the information about invoices.
|
||||||
|
type InvoiceDB interface {
|
||||||
|
// AddInvoice inserts the targeted invoice into the database.
|
||||||
|
// If the invoice has *any* payment hashes which already exists within
|
||||||
|
// the database, then the insertion will be aborted and rejected due to
|
||||||
|
// the strict policy banning any duplicate payment hashes.
|
||||||
|
//
|
||||||
|
// NOTE: A side effect of this function is that it sets AddIndex on
|
||||||
|
// newInvoice.
|
||||||
|
AddInvoice(invoice *Invoice, paymentHash lntypes.Hash) (uint64, error)
|
||||||
|
|
||||||
|
// InvoicesAddedSince can be used by callers to seek into the event
|
||||||
|
// time series of all the invoices added in the database. The specified
|
||||||
|
// sinceAddIndex should be the highest add index that the caller knows
|
||||||
|
// of. This method will return all invoices with an add index greater
|
||||||
|
// than the specified sinceAddIndex.
|
||||||
|
//
|
||||||
|
// NOTE: The index starts from 1, as a result. We enforce that
|
||||||
|
// specifying a value below the starting index value is a noop.
|
||||||
|
InvoicesAddedSince(sinceAddIndex uint64) ([]Invoice, error)
|
||||||
|
|
||||||
|
// LookupInvoice attempts to look up an invoice according to its 32 byte
|
||||||
|
// payment hash. If an invoice which can settle the HTLC identified by
|
||||||
|
// the passed payment hash isn't found, then an error is returned.
|
||||||
|
// Otherwise, the full invoice is returned.
|
||||||
|
// Before setting the incoming HTLC, the values SHOULD be checked to
|
||||||
|
// ensure the payer meets the agreed upon contractual terms of the
|
||||||
|
// payment.
|
||||||
|
LookupInvoice(ref InvoiceRef) (Invoice, error)
|
||||||
|
|
||||||
|
// ScanInvoices scans through all invoices and calls the passed scanFunc
|
||||||
|
// for each invoice with its respective payment hash. Additionally a
|
||||||
|
// reset() closure is passed which is used to reset/initialize partial
|
||||||
|
// results and also to signal if the kvdb.View transaction has been
|
||||||
|
// retried.
|
||||||
|
//
|
||||||
|
// TODO(positiveblue): abstract this functionality so it makes sense for
|
||||||
|
// other backends like sql.
|
||||||
|
ScanInvoices(scanFunc InvScanFunc, reset func()) error
|
||||||
|
|
||||||
|
// QueryInvoices allows a caller to query the invoice database for
|
||||||
|
// invoices within the specified add index range.
|
||||||
|
QueryInvoices(q InvoiceQuery) (InvoiceSlice, error)
|
||||||
|
|
||||||
|
// UpdateInvoice attempts to update an invoice corresponding to the
|
||||||
|
// passed payment hash. If an invoice matching the passed payment hash
|
||||||
|
// doesn't exist within the database, then the action will fail with a
|
||||||
|
// "not found" error.
|
||||||
|
//
|
||||||
|
// The update is performed inside the same database transaction that
|
||||||
|
// fetches the invoice and is therefore atomic. The fields to update
|
||||||
|
// are controlled by the supplied callback.
|
||||||
|
//
|
||||||
|
// TODO(positiveblue): abstract this functionality so it makes sense for
|
||||||
|
// other backends like sql.
|
||||||
|
UpdateInvoice(ref InvoiceRef, setIDHint *SetID,
|
||||||
|
callback InvoiceUpdateCallback) (*Invoice, error)
|
||||||
|
|
||||||
|
// InvoicesSettledSince can be used by callers to catch up any settled
|
||||||
|
// invoices they missed within the settled invoice time series. We'll
|
||||||
|
// return all known settled invoice that have a settle index higher than
|
||||||
|
// the passed sinceSettleIndex.
|
||||||
|
//
|
||||||
|
// NOTE: The index starts from 1, as a result. We enforce that
|
||||||
|
// specifying a value below the starting index value is a noop.
|
||||||
|
InvoicesSettledSince(sinceSettleIndex uint64) ([]Invoice, error)
|
||||||
|
|
||||||
|
// DeleteInvoice attempts to delete the passed invoices from the
|
||||||
|
// database in one transaction. The passed delete references hold all
|
||||||
|
// keys required to delete the invoices without also needing to
|
||||||
|
// deserialze them.
|
||||||
|
DeleteInvoice(invoicesToDelete []InvoiceDeleteRef) error
|
||||||
|
}
|
||||||
|
|
||||||
// Payload abstracts access to any additional fields provided in the final hop's
|
// Payload abstracts access to any additional fields provided in the final hop's
|
||||||
// TLV onion payload.
|
// TLV onion payload.
|
||||||
type Payload interface {
|
type Payload interface {
|
||||||
@@ -23,3 +105,61 @@ type Payload interface {
|
|||||||
// payment to the payee.
|
// payment to the payee.
|
||||||
Metadata() []byte
|
Metadata() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvoiceQuery represents a query to the invoice database. The query allows a
|
||||||
|
// caller to retrieve all invoices starting from a particular add index and
|
||||||
|
// limit the number of results returned.
|
||||||
|
type InvoiceQuery struct {
|
||||||
|
// IndexOffset is the offset within the add indices to start at. This
|
||||||
|
// can be used to start the response at a particular invoice.
|
||||||
|
IndexOffset uint64
|
||||||
|
|
||||||
|
// NumMaxInvoices is the maximum number of invoices that should be
|
||||||
|
// starting from the add index.
|
||||||
|
NumMaxInvoices uint64
|
||||||
|
|
||||||
|
// PendingOnly, if set, returns unsettled invoices starting from the
|
||||||
|
// add index.
|
||||||
|
PendingOnly bool
|
||||||
|
|
||||||
|
// Reversed, if set, indicates that the invoices returned should start
|
||||||
|
// from the IndexOffset and go backwards.
|
||||||
|
Reversed bool
|
||||||
|
|
||||||
|
// CreationDateStart, if set, filters out all invoices with a creation
|
||||||
|
// date greater than or euqal to it.
|
||||||
|
CreationDateStart time.Time
|
||||||
|
|
||||||
|
// CreationDateEnd, if set, filters out all invoices with a creation
|
||||||
|
// date less than or euqal to it.
|
||||||
|
CreationDateEnd time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceSlice is the response to a invoice query. It includes the original
|
||||||
|
// query, the set of invoices that match the query, and an integer which
|
||||||
|
// represents the offset index of the last item in the set of returned invoices.
|
||||||
|
// This integer allows callers to resume their query using this offset in the
|
||||||
|
// event that the query's response exceeds the maximum number of returnable
|
||||||
|
// invoices.
|
||||||
|
type InvoiceSlice struct {
|
||||||
|
InvoiceQuery
|
||||||
|
|
||||||
|
// Invoices is the set of invoices that matched the query above.
|
||||||
|
Invoices []Invoice
|
||||||
|
|
||||||
|
// FirstIndexOffset is the index of the first element in the set of
|
||||||
|
// returned Invoices above. Callers can use this to resume their query
|
||||||
|
// in the event that the slice has too many events to fit into a single
|
||||||
|
// response.
|
||||||
|
FirstIndexOffset uint64
|
||||||
|
|
||||||
|
// LastIndexOffset is the index of the last element in the set of
|
||||||
|
// returned Invoices above. Callers can use this to resume their query
|
||||||
|
// in the event that the slice has too many events to fit into a single
|
||||||
|
// response.
|
||||||
|
LastIndexOffset uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// CircuitKey is a tuple of channel ID and HTLC ID, used to uniquely identify
|
||||||
|
// HTLCs in a circuit.
|
||||||
|
type CircuitKey = models.CircuitKey
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/queue"
|
"github.com/lightningnetwork/lnd/queue"
|
||||||
@@ -178,18 +177,18 @@ func (ew *InvoiceExpiryWatcher) Stop() {
|
|||||||
// makeInvoiceExpiry checks if the passed invoice may be canceled and calculates
|
// makeInvoiceExpiry checks if the passed invoice may be canceled and calculates
|
||||||
// the expiry time and creates a slimmer invoiceExpiry implementation.
|
// the expiry time and creates a slimmer invoiceExpiry implementation.
|
||||||
func makeInvoiceExpiry(paymentHash lntypes.Hash,
|
func makeInvoiceExpiry(paymentHash lntypes.Hash,
|
||||||
invoice *channeldb.Invoice) invoiceExpiry {
|
invoice *Invoice) invoiceExpiry {
|
||||||
|
|
||||||
switch invoice.State {
|
switch invoice.State {
|
||||||
// If we have an open invoice with no htlcs, we want to expire the
|
// If we have an open invoice with no htlcs, we want to expire the
|
||||||
// invoice based on timestamp
|
// invoice based on timestamp
|
||||||
case channeldb.ContractOpen:
|
case ContractOpen:
|
||||||
return makeTimestampExpiry(paymentHash, invoice)
|
return makeTimestampExpiry(paymentHash, invoice)
|
||||||
|
|
||||||
// If an invoice has active htlcs, we want to expire it based on block
|
// If an invoice has active htlcs, we want to expire it based on block
|
||||||
// height. We only do this for hodl invoices, since regular invoices
|
// height. We only do this for hodl invoices, since regular invoices
|
||||||
// should resolve themselves automatically.
|
// should resolve themselves automatically.
|
||||||
case channeldb.ContractAccepted:
|
case ContractAccepted:
|
||||||
if !invoice.HodlInvoice {
|
if !invoice.HodlInvoice {
|
||||||
log.Debugf("Invoice in accepted state not added to "+
|
log.Debugf("Invoice in accepted state not added to "+
|
||||||
"expiry watcher: %v", paymentHash)
|
"expiry watcher: %v", paymentHash)
|
||||||
@@ -201,7 +200,7 @@ func makeInvoiceExpiry(paymentHash lntypes.Hash,
|
|||||||
for _, htlc := range invoice.Htlcs {
|
for _, htlc := range invoice.Htlcs {
|
||||||
// We only care about accepted htlcs, since they will
|
// We only care about accepted htlcs, since they will
|
||||||
// trigger force-closes.
|
// trigger force-closes.
|
||||||
if htlc.State != channeldb.HtlcStateAccepted {
|
if htlc.State != HtlcStateAccepted {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,9 +221,9 @@ func makeInvoiceExpiry(paymentHash lntypes.Hash,
|
|||||||
|
|
||||||
// makeTimestampExpiry creates a timestamp-based expiry entry.
|
// makeTimestampExpiry creates a timestamp-based expiry entry.
|
||||||
func makeTimestampExpiry(paymentHash lntypes.Hash,
|
func makeTimestampExpiry(paymentHash lntypes.Hash,
|
||||||
invoice *channeldb.Invoice) *invoiceExpiryTs {
|
invoice *Invoice) *invoiceExpiryTs {
|
||||||
|
|
||||||
if invoice.State != channeldb.ContractOpen {
|
if invoice.State != ContractOpen {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,11 +348,11 @@ func (ew *InvoiceExpiryWatcher) expireInvoice(hash lntypes.Hash, force bool) {
|
|||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
|
|
||||||
case channeldb.ErrInvoiceAlreadyCanceled:
|
case ErrInvoiceAlreadyCanceled:
|
||||||
|
|
||||||
case channeldb.ErrInvoiceAlreadySettled:
|
case ErrInvoiceAlreadySettled:
|
||||||
|
|
||||||
case channeldb.ErrInvoiceNotFound:
|
case ErrInvoiceNotFound:
|
||||||
// It's possible that the user has manually canceled the invoice
|
// It's possible that the user has manually canceled the invoice
|
||||||
// which will then be deleted by the garbage collector resulting
|
// which will then be deleted by the garbage collector resulting
|
||||||
// in an ErrInvoiceNotFound error.
|
// in an ErrInvoiceNotFound error.
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -22,29 +20,6 @@ type invoiceExpiryWatcherTest struct {
|
|||||||
canceledInvoices []lntypes.Hash
|
canceledInvoices []lntypes.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockChainNotifier struct {
|
|
||||||
chainntnfs.ChainNotifier
|
|
||||||
|
|
||||||
blockChan chan *chainntnfs.BlockEpoch
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMockNotifier() *mockChainNotifier {
|
|
||||||
return &mockChainNotifier{
|
|
||||||
blockChan: make(chan *chainntnfs.BlockEpoch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterBlockEpochNtfn mocks a block epoch notification, using the mock's
|
|
||||||
// block channel to deliver blocks to the client.
|
|
||||||
func (m *mockChainNotifier) RegisterBlockEpochNtfn(*chainntnfs.BlockEpoch) (
|
|
||||||
*chainntnfs.BlockEpochEvent, error) {
|
|
||||||
|
|
||||||
return &chainntnfs.BlockEpochEvent{
|
|
||||||
Epochs: m.blockChan,
|
|
||||||
Cancel: func() {},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newInvoiceExpiryWatcherTest creates a new InvoiceExpiryWatcher test fixture
|
// newInvoiceExpiryWatcherTest creates a new InvoiceExpiryWatcher test fixture
|
||||||
// and sets up the test environment.
|
// and sets up the test environment.
|
||||||
func newInvoiceExpiryWatcherTest(t *testing.T, now time.Time,
|
func newInvoiceExpiryWatcherTest(t *testing.T, now time.Time,
|
||||||
@@ -213,7 +188,7 @@ func TestExpiredHodlInv(t *testing.T) {
|
|||||||
expiry := time.Hour
|
expiry := time.Hour
|
||||||
|
|
||||||
test := setupHodlExpiry(
|
test := setupHodlExpiry(
|
||||||
t, creationDate, expiry, 0, channeldb.ContractOpen, nil,
|
t, creationDate, expiry, 0, ContractOpen, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
test.assertCanceled(t, test.hash)
|
test.assertCanceled(t, test.hash)
|
||||||
@@ -231,7 +206,7 @@ func TestAcceptedHodlNotExpired(t *testing.T) {
|
|||||||
expiry := time.Hour
|
expiry := time.Hour
|
||||||
|
|
||||||
test := setupHodlExpiry(
|
test := setupHodlExpiry(
|
||||||
t, creationDate, expiry, 0, channeldb.ContractAccepted, nil,
|
t, creationDate, expiry, 0, ContractAccepted, nil,
|
||||||
)
|
)
|
||||||
defer test.watcher.Stop()
|
defer test.watcher.Stop()
|
||||||
|
|
||||||
@@ -255,15 +230,15 @@ func TestAcceptedHodlNotExpired(t *testing.T) {
|
|||||||
func TestHeightAlreadyExpired(t *testing.T) {
|
func TestHeightAlreadyExpired(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
expiredHtlc := []*channeldb.InvoiceHTLC{
|
expiredHtlc := []*InvoiceHTLC{
|
||||||
{
|
{
|
||||||
State: channeldb.HtlcStateAccepted,
|
State: HtlcStateAccepted,
|
||||||
Expiry: uint32(testCurrentHeight),
|
Expiry: uint32(testCurrentHeight),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
test := setupHodlExpiry(
|
test := setupHodlExpiry(
|
||||||
t, testTime, time.Hour, 0, channeldb.ContractAccepted,
|
t, testTime, time.Hour, 0, ContractAccepted,
|
||||||
expiredHtlc,
|
expiredHtlc,
|
||||||
)
|
)
|
||||||
defer test.watcher.Stop()
|
defer test.watcher.Stop()
|
||||||
@@ -284,7 +259,7 @@ func TestExpiryHeightArrives(t *testing.T) {
|
|||||||
|
|
||||||
// Start out with a hodl invoice that is open, and has no htlcs.
|
// Start out with a hodl invoice that is open, and has no htlcs.
|
||||||
test := setupHodlExpiry(
|
test := setupHodlExpiry(
|
||||||
t, creationDate, expiry, delta, channeldb.ContractOpen, nil,
|
t, creationDate, expiry, delta, ContractOpen, nil,
|
||||||
)
|
)
|
||||||
defer test.watcher.Stop()
|
defer test.watcher.Stop()
|
||||||
|
|
||||||
@@ -293,7 +268,7 @@ func TestExpiryHeightArrives(t *testing.T) {
|
|||||||
|
|
||||||
// Add htlcs to our invoice and progress its state to accepted.
|
// Add htlcs to our invoice and progress its state to accepted.
|
||||||
test.watcher.AddInvoices(expiry1)
|
test.watcher.AddInvoices(expiry1)
|
||||||
test.setState(channeldb.ContractAccepted)
|
test.setState(ContractAccepted)
|
||||||
|
|
||||||
// Progress time so that our expiry has elapsed. We no longer expect
|
// Progress time so that our expiry has elapsed. We no longer expect
|
||||||
// this invoice to be canceled because it has been accepted.
|
// this invoice to be canceled because it has been accepted.
|
||||||
|
|||||||
+166
-154
@@ -7,7 +7,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -16,13 +15,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrInvoiceExpiryTooSoon is returned when an invoice is attempted to be
|
// ErrInvoiceExpiryTooSoon is returned when an invoice is attempted to
|
||||||
// accepted or settled with not enough blocks remaining.
|
// be accepted or settled with not enough blocks remaining.
|
||||||
ErrInvoiceExpiryTooSoon = errors.New("invoice expiry too soon")
|
ErrInvoiceExpiryTooSoon = errors.New("invoice expiry too soon")
|
||||||
|
|
||||||
// ErrInvoiceAmountTooLow is returned when an invoice is attempted to be
|
// ErrInvoiceAmountTooLow is returned when an invoice is attempted to
|
||||||
// accepted or settled with an amount that is too low.
|
// be accepted or settled with an amount that is too low.
|
||||||
ErrInvoiceAmountTooLow = errors.New("paid amount less than invoice amount")
|
ErrInvoiceAmountTooLow = errors.New(
|
||||||
|
"paid amount less than invoice amount",
|
||||||
|
)
|
||||||
|
|
||||||
// ErrShuttingDown is returned when an operation failed because the
|
// ErrShuttingDown is returned when an operation failed because the
|
||||||
// invoice registry is shutting down.
|
// invoice registry is shutting down.
|
||||||
@@ -78,10 +79,10 @@ type RegistryConfig struct {
|
|||||||
// mpp htlcs for which the complete set didn't arrive in time.
|
// mpp htlcs for which the complete set didn't arrive in time.
|
||||||
type htlcReleaseEvent struct {
|
type htlcReleaseEvent struct {
|
||||||
// invoiceRef identifiers the invoice this htlc belongs to.
|
// invoiceRef identifiers the invoice this htlc belongs to.
|
||||||
invoiceRef channeldb.InvoiceRef
|
invoiceRef InvoiceRef
|
||||||
|
|
||||||
// key is the circuit key of the htlc to release.
|
// key is the circuit key of the htlc to release.
|
||||||
key channeldb.CircuitKey
|
key CircuitKey
|
||||||
|
|
||||||
// releaseTime is the time at which to release the htlc.
|
// releaseTime is the time at which to release the htlc.
|
||||||
releaseTime time.Time
|
releaseTime time.Time
|
||||||
@@ -103,7 +104,7 @@ type InvoiceRegistry struct {
|
|||||||
|
|
||||||
nextClientID uint32 // must be used atomically
|
nextClientID uint32 // must be used atomically
|
||||||
|
|
||||||
cdb *channeldb.DB
|
idb InvoiceDB
|
||||||
|
|
||||||
// cfg contains the registry's configuration parameters.
|
// cfg contains the registry's configuration parameters.
|
||||||
cfg *RegistryConfig
|
cfg *RegistryConfig
|
||||||
@@ -129,13 +130,14 @@ type InvoiceRegistry struct {
|
|||||||
// necessary to avoid deadlocks in the registry when processing invoice
|
// necessary to avoid deadlocks in the registry when processing invoice
|
||||||
// events.
|
// events.
|
||||||
hodlSubscriptionsMux sync.RWMutex
|
hodlSubscriptionsMux sync.RWMutex
|
||||||
// subscriptions is a map from a circuit key to a list of subscribers.
|
|
||||||
// It is used for efficient notification of links.
|
// hodlSubscriptions is a map from a circuit key to a list of
|
||||||
hodlSubscriptions map[channeldb.CircuitKey]map[chan<- interface{}]struct{}
|
// subscribers. It is used for efficient notification of links.
|
||||||
|
hodlSubscriptions map[CircuitKey]map[chan<- interface{}]struct{}
|
||||||
|
|
||||||
// reverseSubscriptions tracks circuit keys subscribed to per
|
// reverseSubscriptions tracks circuit keys subscribed to per
|
||||||
// subscriber. This is used to unsubscribe from all hashes efficiently.
|
// subscriber. This is used to unsubscribe from all hashes efficiently.
|
||||||
hodlReverseSubscriptions map[chan<- interface{}]map[channeldb.CircuitKey]struct{}
|
hodlReverseSubscriptions map[chan<- interface{}]map[CircuitKey]struct{}
|
||||||
|
|
||||||
// htlcAutoReleaseChan contains the new htlcs that need to be
|
// htlcAutoReleaseChan contains the new htlcs that need to be
|
||||||
// auto-released.
|
// auto-released.
|
||||||
@@ -151,16 +153,22 @@ type InvoiceRegistry struct {
|
|||||||
// wraps the persistent on-disk invoice storage with an additional in-memory
|
// wraps the persistent on-disk invoice storage with an additional in-memory
|
||||||
// layer. The in-memory layer is in place such that debug invoices can be added
|
// layer. The in-memory layer is in place such that debug invoices can be added
|
||||||
// which are volatile yet available system wide within the daemon.
|
// which are volatile yet available system wide within the daemon.
|
||||||
func NewRegistry(cdb *channeldb.DB, expiryWatcher *InvoiceExpiryWatcher,
|
func NewRegistry(idb InvoiceDB, expiryWatcher *InvoiceExpiryWatcher,
|
||||||
cfg *RegistryConfig) *InvoiceRegistry {
|
cfg *RegistryConfig) *InvoiceRegistry {
|
||||||
|
|
||||||
|
notificationClients := make(map[uint32]*InvoiceSubscription)
|
||||||
|
singleNotificationClients := make(map[uint32]*SingleInvoiceSubscription)
|
||||||
return &InvoiceRegistry{
|
return &InvoiceRegistry{
|
||||||
cdb: cdb,
|
idb: idb,
|
||||||
notificationClients: make(map[uint32]*InvoiceSubscription),
|
notificationClients: notificationClients,
|
||||||
singleNotificationClients: make(map[uint32]*SingleInvoiceSubscription),
|
singleNotificationClients: singleNotificationClients,
|
||||||
invoiceEvents: make(chan *invoiceEvent, 100),
|
invoiceEvents: make(chan *invoiceEvent, 100),
|
||||||
hodlSubscriptions: make(map[channeldb.CircuitKey]map[chan<- interface{}]struct{}),
|
hodlSubscriptions: make(
|
||||||
hodlReverseSubscriptions: make(map[chan<- interface{}]map[channeldb.CircuitKey]struct{}),
|
map[CircuitKey]map[chan<- interface{}]struct{},
|
||||||
|
),
|
||||||
|
hodlReverseSubscriptions: make(
|
||||||
|
map[chan<- interface{}]map[CircuitKey]struct{},
|
||||||
|
),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
htlcAutoReleaseChan: make(chan *htlcReleaseEvent),
|
htlcAutoReleaseChan: make(chan *htlcReleaseEvent),
|
||||||
expiryWatcher: expiryWatcher,
|
expiryWatcher: expiryWatcher,
|
||||||
@@ -174,7 +182,7 @@ func NewRegistry(cdb *channeldb.DB, expiryWatcher *InvoiceExpiryWatcher,
|
|||||||
func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
||||||
var (
|
var (
|
||||||
pending []invoiceExpiry
|
pending []invoiceExpiry
|
||||||
removable []channeldb.InvoiceDeleteRef
|
removable []InvoiceDeleteRef
|
||||||
)
|
)
|
||||||
|
|
||||||
reset := func() {
|
reset := func() {
|
||||||
@@ -184,11 +192,11 @@ func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
|||||||
// using the etcd driver, where all transactions are allowed
|
// using the etcd driver, where all transactions are allowed
|
||||||
// to retry for serializability).
|
// to retry for serializability).
|
||||||
pending = nil
|
pending = nil
|
||||||
removable = make([]channeldb.InvoiceDeleteRef, 0)
|
removable = make([]InvoiceDeleteRef, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
scanFunc := func(paymentHash lntypes.Hash,
|
scanFunc := func(paymentHash lntypes.Hash,
|
||||||
invoice *channeldb.Invoice) error {
|
invoice *Invoice) error {
|
||||||
|
|
||||||
if invoice.IsPending() {
|
if invoice.IsPending() {
|
||||||
expiryRef := makeInvoiceExpiry(paymentHash, invoice)
|
expiryRef := makeInvoiceExpiry(paymentHash, invoice)
|
||||||
@@ -196,19 +204,19 @@ func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
|||||||
pending = append(pending, expiryRef)
|
pending = append(pending, expiryRef)
|
||||||
}
|
}
|
||||||
} else if i.cfg.GcCanceledInvoicesOnStartup &&
|
} else if i.cfg.GcCanceledInvoicesOnStartup &&
|
||||||
invoice.State == channeldb.ContractCanceled {
|
invoice.State == ContractCanceled {
|
||||||
|
|
||||||
// Consider invoice for removal if it is already
|
// Consider invoice for removal if it is already
|
||||||
// canceled. Invoices that are expired but not yet
|
// canceled. Invoices that are expired but not yet
|
||||||
// canceled, will be queued up for cancellation after
|
// canceled, will be queued up for cancellation after
|
||||||
// startup and will be deleted afterwards.
|
// startup and will be deleted afterwards.
|
||||||
ref := channeldb.InvoiceDeleteRef{
|
ref := InvoiceDeleteRef{
|
||||||
PayHash: paymentHash,
|
PayHash: paymentHash,
|
||||||
AddIndex: invoice.AddIndex,
|
AddIndex: invoice.AddIndex,
|
||||||
SettleIndex: invoice.SettleIndex,
|
SettleIndex: invoice.SettleIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
if invoice.Terms.PaymentAddr != channeldb.BlankPayAddr {
|
if invoice.Terms.PaymentAddr != BlankPayAddr {
|
||||||
ref.PayAddr = &invoice.Terms.PaymentAddr
|
ref.PayAddr = &invoice.Terms.PaymentAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +225,7 @@ func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := i.cdb.ScanInvoices(scanFunc, reset)
|
err := i.idb.ScanInvoices(scanFunc, reset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -229,7 +237,7 @@ func (i *InvoiceRegistry) scanInvoicesOnStart() error {
|
|||||||
if len(removable) > 0 {
|
if len(removable) > 0 {
|
||||||
log.Infof("Attempting to delete %v canceled invoices",
|
log.Infof("Attempting to delete %v canceled invoices",
|
||||||
len(removable))
|
len(removable))
|
||||||
if err := i.cdb.DeleteInvoice(removable); err != nil {
|
if err := i.idb.DeleteInvoice(removable); err != nil {
|
||||||
log.Warnf("Deleting canceled invoices failed: %v", err)
|
log.Warnf("Deleting canceled invoices failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Deleted %v canceled invoices",
|
log.Infof("Deleted %v canceled invoices",
|
||||||
@@ -282,7 +290,7 @@ func (i *InvoiceRegistry) Stop() error {
|
|||||||
// instance where invoices are settled.
|
// instance where invoices are settled.
|
||||||
type invoiceEvent struct {
|
type invoiceEvent struct {
|
||||||
hash lntypes.Hash
|
hash lntypes.Hash
|
||||||
invoice *channeldb.Invoice
|
invoice *Invoice
|
||||||
setID *[32]byte
|
setID *[32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,8 +326,8 @@ func (i *InvoiceRegistry) invoiceEventLoop() {
|
|||||||
// For backwards compatibility, do not notify all
|
// For backwards compatibility, do not notify all
|
||||||
// invoice subscribers of cancel and accept events.
|
// invoice subscribers of cancel and accept events.
|
||||||
state := event.invoice.State
|
state := event.invoice.State
|
||||||
if state != channeldb.ContractCanceled &&
|
if state != ContractCanceled &&
|
||||||
state != channeldb.ContractAccepted {
|
state != ContractAccepted {
|
||||||
|
|
||||||
i.dispatchToClients(event)
|
i.dispatchToClients(event)
|
||||||
}
|
}
|
||||||
@@ -396,7 +404,7 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
switch {
|
switch {
|
||||||
// If we've already sent this settle event to
|
// If we've already sent this settle event to
|
||||||
// the client, then we can skip this.
|
// the client, then we can skip this.
|
||||||
case state == channeldb.ContractSettled &&
|
case state == ContractSettled &&
|
||||||
client.settleIndex >= invoice.SettleIndex:
|
client.settleIndex >= invoice.SettleIndex:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -404,14 +412,14 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
// the client then we can skip this one, but only if this isn't
|
// the client then we can skip this one, but only if this isn't
|
||||||
// an AMP invoice. AMP invoices always remain in the settle
|
// an AMP invoice. AMP invoices always remain in the settle
|
||||||
// state as a base invoice.
|
// state as a base invoice.
|
||||||
case event.setID == nil && state == channeldb.ContractOpen &&
|
case event.setID == nil && state == ContractOpen &&
|
||||||
client.addIndex >= invoice.AddIndex:
|
client.addIndex >= invoice.AddIndex:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// These two states should never happen, but we
|
// These two states should never happen, but we
|
||||||
// log them just in case so we can detect this
|
// log them just in case so we can detect this
|
||||||
// instance.
|
// instance.
|
||||||
case state == channeldb.ContractOpen &&
|
case state == ContractOpen &&
|
||||||
client.addIndex+1 != invoice.AddIndex:
|
client.addIndex+1 != invoice.AddIndex:
|
||||||
log.Warnf("client=%v for invoice "+
|
log.Warnf("client=%v for invoice "+
|
||||||
"notifications missed an update, "+
|
"notifications missed an update, "+
|
||||||
@@ -419,7 +427,7 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
clientID, client.addIndex,
|
clientID, client.addIndex,
|
||||||
invoice.AddIndex)
|
invoice.AddIndex)
|
||||||
|
|
||||||
case state == channeldb.ContractSettled &&
|
case state == ContractSettled &&
|
||||||
client.settleIndex+1 != invoice.SettleIndex:
|
client.settleIndex+1 != invoice.SettleIndex:
|
||||||
log.Warnf("client=%v for invoice "+
|
log.Warnf("client=%v for invoice "+
|
||||||
"notifications missed an update, "+
|
"notifications missed an update, "+
|
||||||
@@ -451,10 +459,10 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
// event is added while we're catching up a new client.
|
// event is added while we're catching up a new client.
|
||||||
invState := event.invoice.State
|
invState := event.invoice.State
|
||||||
switch {
|
switch {
|
||||||
case invState == channeldb.ContractSettled:
|
case invState == ContractSettled:
|
||||||
client.settleIndex = invoice.SettleIndex
|
client.settleIndex = invoice.SettleIndex
|
||||||
|
|
||||||
case invState == channeldb.ContractOpen && event.setID == nil:
|
case invState == ContractOpen && event.setID == nil:
|
||||||
client.addIndex = invoice.AddIndex
|
client.addIndex = invoice.AddIndex
|
||||||
|
|
||||||
// If this is an AMP invoice, then we'll need to use the set ID
|
// If this is an AMP invoice, then we'll need to use the set ID
|
||||||
@@ -462,7 +470,7 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
// invoices never go to the open state, but if a setID is
|
// invoices never go to the open state, but if a setID is
|
||||||
// passed, then we know it was just settled and will track the
|
// passed, then we know it was just settled and will track the
|
||||||
// highest settle index so far.
|
// highest settle index so far.
|
||||||
case invState == channeldb.ContractOpen && event.setID != nil:
|
case invState == ContractOpen && event.setID != nil:
|
||||||
setID := *event.setID
|
setID := *event.setID
|
||||||
client.settleIndex = invoice.AMPState[setID].SettleIndex
|
client.settleIndex = invoice.AMPState[setID].SettleIndex
|
||||||
|
|
||||||
@@ -475,13 +483,15 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
|
|
||||||
// deliverBacklogEvents will attempts to query the invoice database for any
|
// deliverBacklogEvents will attempts to query the invoice database for any
|
||||||
// notifications that the client has missed since it reconnected last.
|
// notifications that the client has missed since it reconnected last.
|
||||||
func (i *InvoiceRegistry) deliverBacklogEvents(client *InvoiceSubscription) error {
|
func (i *InvoiceRegistry) deliverBacklogEvents(
|
||||||
addEvents, err := i.cdb.InvoicesAddedSince(client.addIndex)
|
client *InvoiceSubscription) error {
|
||||||
|
|
||||||
|
addEvents, err := i.idb.InvoicesAddedSince(client.addIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settleEvents, err := i.cdb.InvoicesSettledSince(client.settleIndex)
|
settleEvents, err := i.idb.InvoicesSettledSince(client.settleIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -528,13 +538,13 @@ func (i *InvoiceRegistry) deliverBacklogEvents(client *InvoiceSubscription) erro
|
|||||||
func (i *InvoiceRegistry) deliverSingleBacklogEvents(
|
func (i *InvoiceRegistry) deliverSingleBacklogEvents(
|
||||||
client *SingleInvoiceSubscription) error {
|
client *SingleInvoiceSubscription) error {
|
||||||
|
|
||||||
invoice, err := i.cdb.LookupInvoice(client.invoiceRef)
|
invoice, err := i.idb.LookupInvoice(client.invoiceRef)
|
||||||
|
|
||||||
// It is possible that the invoice does not exist yet, but the client is
|
// It is possible that the invoice does not exist yet, but the client is
|
||||||
// already watching it in anticipation.
|
// already watching it in anticipation.
|
||||||
if err == channeldb.ErrInvoiceNotFound ||
|
isNotFound := errors.Is(err, ErrInvoiceNotFound)
|
||||||
err == channeldb.ErrNoInvoicesCreated {
|
isNotCreated := errors.Is(err, ErrNoInvoicesCreated)
|
||||||
|
if isNotFound || isNotCreated {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -568,15 +578,15 @@ func (i *InvoiceRegistry) deliverSingleBacklogEvents(
|
|||||||
// addIndex of the newly created invoice which monotonically increases for each
|
// addIndex of the newly created invoice which monotonically increases for each
|
||||||
// new invoice added. A side effect of this function is that it also sets
|
// new invoice added. A side effect of this function is that it also sets
|
||||||
// AddIndex on the invoice argument.
|
// AddIndex on the invoice argument.
|
||||||
func (i *InvoiceRegistry) AddInvoice(invoice *channeldb.Invoice,
|
func (i *InvoiceRegistry) AddInvoice(invoice *Invoice,
|
||||||
paymentHash lntypes.Hash) (uint64, error) {
|
paymentHash lntypes.Hash) (uint64, error) {
|
||||||
|
|
||||||
i.Lock()
|
i.Lock()
|
||||||
|
|
||||||
ref := channeldb.InvoiceRefByHash(paymentHash)
|
ref := InvoiceRefByHash(paymentHash)
|
||||||
log.Debugf("Invoice%v: added with terms %v", ref, invoice.Terms)
|
log.Debugf("Invoice%v: added with terms %v", ref, invoice.Terms)
|
||||||
|
|
||||||
addIndex, err := i.cdb.AddInvoice(invoice, paymentHash)
|
addIndex, err := i.idb.AddInvoice(invoice, paymentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.Unlock()
|
i.Unlock()
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -602,27 +612,23 @@ func (i *InvoiceRegistry) AddInvoice(invoice *channeldb.Invoice,
|
|||||||
// then we're able to pull the funds pending within an HTLC.
|
// then we're able to pull the funds pending within an HTLC.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): ignore if settled?
|
// TODO(roasbeef): ignore if settled?
|
||||||
func (i *InvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (channeldb.Invoice,
|
func (i *InvoiceRegistry) LookupInvoice(rHash lntypes.Hash) (Invoice, error) {
|
||||||
error) {
|
|
||||||
|
|
||||||
// We'll check the database to see if there's an existing matching
|
// We'll check the database to see if there's an existing matching
|
||||||
// invoice.
|
// invoice.
|
||||||
ref := channeldb.InvoiceRefByHash(rHash)
|
ref := InvoiceRefByHash(rHash)
|
||||||
return i.cdb.LookupInvoice(ref)
|
return i.idb.LookupInvoice(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupInvoiceByRef looks up an invoice by the given reference, if found
|
// LookupInvoiceByRef looks up an invoice by the given reference, if found
|
||||||
// then we're able to pull the funds pending within an HTLC.
|
// then we're able to pull the funds pending within an HTLC.
|
||||||
func (i *InvoiceRegistry) LookupInvoiceByRef(
|
func (i *InvoiceRegistry) LookupInvoiceByRef(ref InvoiceRef) (Invoice, error) {
|
||||||
ref channeldb.InvoiceRef) (channeldb.Invoice, error) {
|
return i.idb.LookupInvoice(ref)
|
||||||
|
|
||||||
return i.cdb.LookupInvoice(ref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// startHtlcTimer starts a new timer via the invoice registry main loop that
|
// startHtlcTimer starts a new timer via the invoice registry main loop that
|
||||||
// cancels a single htlc on an invoice when the htlc hold duration has passed.
|
// cancels a single htlc on an invoice when the htlc hold duration has passed.
|
||||||
func (i *InvoiceRegistry) startHtlcTimer(invoiceRef channeldb.InvoiceRef,
|
func (i *InvoiceRegistry) startHtlcTimer(invoiceRef InvoiceRef,
|
||||||
key channeldb.CircuitKey, acceptTime time.Time) error {
|
key CircuitKey, acceptTime time.Time) error {
|
||||||
|
|
||||||
releaseTime := acceptTime.Add(i.cfg.HtlcHoldDuration)
|
releaseTime := acceptTime.Add(i.cfg.HtlcHoldDuration)
|
||||||
event := &htlcReleaseEvent{
|
event := &htlcReleaseEvent{
|
||||||
@@ -643,14 +649,12 @@ func (i *InvoiceRegistry) startHtlcTimer(invoiceRef channeldb.InvoiceRef,
|
|||||||
// cancelSingleHtlc cancels a single accepted htlc on an invoice. It takes
|
// cancelSingleHtlc cancels a single accepted htlc on an invoice. It takes
|
||||||
// a resolution result which will be used to notify subscribed links and
|
// a resolution result which will be used to notify subscribed links and
|
||||||
// resolvers of the details of the htlc cancellation.
|
// resolvers of the details of the htlc cancellation.
|
||||||
func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef InvoiceRef,
|
||||||
key channeldb.CircuitKey, result FailResolutionResult) error {
|
key CircuitKey, result FailResolutionResult) error {
|
||||||
|
|
||||||
updateInvoice := func(invoice *channeldb.Invoice) (
|
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
|
||||||
|
|
||||||
|
updateInvoice := func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
||||||
// Only allow individual htlc cancellation on open invoices.
|
// Only allow individual htlc cancellation on open invoices.
|
||||||
if invoice.State != channeldb.ContractOpen {
|
if invoice.State != ContractOpen {
|
||||||
log.Debugf("cancelSingleHtlc: invoice %v no longer "+
|
log.Debugf("cancelSingleHtlc: invoice %v no longer "+
|
||||||
"open", invoiceRef)
|
"open", invoiceRef)
|
||||||
|
|
||||||
@@ -659,8 +663,8 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
|||||||
|
|
||||||
// Lookup the current status of the htlc in the database.
|
// Lookup the current status of the htlc in the database.
|
||||||
var (
|
var (
|
||||||
htlcState channeldb.HtlcState
|
htlcState HtlcState
|
||||||
setID *channeldb.SetID
|
setID *SetID
|
||||||
)
|
)
|
||||||
htlc, ok := invoice.Htlcs[key]
|
htlc, ok := invoice.Htlcs[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -690,7 +694,7 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
|||||||
|
|
||||||
// Cancellation is only possible if the htlc wasn't already
|
// Cancellation is only possible if the htlc wasn't already
|
||||||
// resolved.
|
// resolved.
|
||||||
if htlcState != channeldb.HtlcStateAccepted {
|
if htlcState != HtlcStateAccepted {
|
||||||
log.Debugf("cancelSingleHtlc: htlc %v on invoice %v "+
|
log.Debugf("cancelSingleHtlc: htlc %v on invoice %v "+
|
||||||
"is already resolved", key, invoiceRef)
|
"is already resolved", key, invoiceRef)
|
||||||
|
|
||||||
@@ -702,11 +706,11 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
|||||||
|
|
||||||
// Return an update descriptor that cancels htlc and keeps
|
// Return an update descriptor that cancels htlc and keeps
|
||||||
// invoice open.
|
// invoice open.
|
||||||
canceledHtlcs := map[channeldb.CircuitKey]struct{}{
|
canceledHtlcs := map[CircuitKey]struct{}{
|
||||||
key: {},
|
key: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &channeldb.InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
CancelHtlcs: canceledHtlcs,
|
CancelHtlcs: canceledHtlcs,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -715,11 +719,11 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
|||||||
// Try to mark the specified htlc as canceled in the invoice database.
|
// Try to mark the specified htlc as canceled in the invoice database.
|
||||||
// Intercept the update descriptor to set the local updated variable. If
|
// Intercept the update descriptor to set the local updated variable. If
|
||||||
// no invoice update is performed, we can return early.
|
// no invoice update is performed, we can return early.
|
||||||
setID := (*channeldb.SetID)(invoiceRef.SetID())
|
setID := (*SetID)(invoiceRef.SetID())
|
||||||
var updated bool
|
var updated bool
|
||||||
invoice, err := i.cdb.UpdateInvoice(invoiceRef, setID,
|
invoice, err := i.idb.UpdateInvoice(invoiceRef, setID,
|
||||||
func(invoice *channeldb.Invoice) (
|
func(invoice *Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
*InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
updateDesc, err := updateInvoice(invoice)
|
updateDesc, err := updateInvoice(invoice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -743,7 +747,7 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("htlc %v not found", key)
|
return fmt.Errorf("htlc %v not found", key)
|
||||||
}
|
}
|
||||||
if htlc.State == channeldb.HtlcStateCanceled {
|
if htlc.State == HtlcStateCanceled {
|
||||||
resolution := NewFailResolution(
|
resolution := NewFailResolution(
|
||||||
key, int32(htlc.AcceptHeight), result,
|
key, int32(htlc.AcceptHeight), result,
|
||||||
)
|
)
|
||||||
@@ -803,12 +807,12 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
|
|||||||
// to not be indexed. In the future, once AMP is merged, this should be
|
// to not be indexed. In the future, once AMP is merged, this should be
|
||||||
// replaced by generating a random payment address on the behalf of the
|
// replaced by generating a random payment address on the behalf of the
|
||||||
// sender.
|
// sender.
|
||||||
payAddr := channeldb.BlankPayAddr
|
payAddr := BlankPayAddr
|
||||||
|
|
||||||
// Create placeholder invoice.
|
// Create placeholder invoice.
|
||||||
invoice := &channeldb.Invoice{
|
invoice := &Invoice{
|
||||||
CreationDate: i.cfg.Clock.Now(),
|
CreationDate: i.cfg.Clock.Now(),
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: ContractTerm{
|
||||||
FinalCltvDelta: finalCltvDelta,
|
FinalCltvDelta: finalCltvDelta,
|
||||||
Value: amt,
|
Value: amt,
|
||||||
PaymentPreimage: &preimage,
|
PaymentPreimage: &preimage,
|
||||||
@@ -825,7 +829,7 @@ func (i *InvoiceRegistry) processKeySend(ctx invoiceUpdateCtx) error {
|
|||||||
// Insert invoice into database. Ignore duplicates, because this
|
// Insert invoice into database. Ignore duplicates, because this
|
||||||
// may be a replay.
|
// may be a replay.
|
||||||
_, err = i.AddInvoice(invoice, ctx.hash)
|
_, err = i.AddInvoice(invoice, ctx.hash)
|
||||||
if err != nil && err != channeldb.ErrDuplicateInvoice {
|
if err != nil && !errors.Is(err, ErrDuplicateInvoice) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,9 +872,9 @@ func (i *InvoiceRegistry) processAMP(ctx invoiceUpdateCtx) error {
|
|||||||
payAddr := ctx.mpp.PaymentAddr()
|
payAddr := ctx.mpp.PaymentAddr()
|
||||||
|
|
||||||
// Create placeholder invoice.
|
// Create placeholder invoice.
|
||||||
invoice := &channeldb.Invoice{
|
invoice := &Invoice{
|
||||||
CreationDate: i.cfg.Clock.Now(),
|
CreationDate: i.cfg.Clock.Now(),
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: ContractTerm{
|
||||||
FinalCltvDelta: finalCltvDelta,
|
FinalCltvDelta: finalCltvDelta,
|
||||||
Value: amt,
|
Value: amt,
|
||||||
PaymentPreimage: nil,
|
PaymentPreimage: nil,
|
||||||
@@ -883,10 +887,10 @@ func (i *InvoiceRegistry) processAMP(ctx invoiceUpdateCtx) error {
|
|||||||
// payment addrs, this may be a replay or a different HTLC for the AMP
|
// payment addrs, this may be a replay or a different HTLC for the AMP
|
||||||
// invoice.
|
// invoice.
|
||||||
_, err := i.AddInvoice(invoice, ctx.hash)
|
_, err := i.AddInvoice(invoice, ctx.hash)
|
||||||
|
isDuplicatedInvoice := errors.Is(err, ErrDuplicateInvoice)
|
||||||
|
isDuplicatedPayAddr := errors.Is(err, ErrDuplicatePayAddr)
|
||||||
switch {
|
switch {
|
||||||
case err == channeldb.ErrDuplicateInvoice:
|
case isDuplicatedInvoice || isDuplicatedPayAddr:
|
||||||
return nil
|
|
||||||
case err == channeldb.ErrDuplicatePayAddr:
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
@@ -910,7 +914,7 @@ func (i *InvoiceRegistry) processAMP(ctx invoiceUpdateCtx) error {
|
|||||||
// held htlc.
|
// held htlc.
|
||||||
func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||||
amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
circuitKey CircuitKey, hodlChan chan<- interface{},
|
||||||
payload Payload) (HtlcResolution, error) {
|
payload Payload) (HtlcResolution, error) {
|
||||||
|
|
||||||
// Create the update context containing the relevant details of the
|
// Create the update context containing the relevant details of the
|
||||||
@@ -977,9 +981,9 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
|||||||
// main event loop.
|
// main event loop.
|
||||||
case *htlcAcceptResolution:
|
case *htlcAcceptResolution:
|
||||||
if r.autoRelease {
|
if r.autoRelease {
|
||||||
var invRef channeldb.InvoiceRef
|
var invRef InvoiceRef
|
||||||
if ctx.amp != nil {
|
if ctx.amp != nil {
|
||||||
invRef = channeldb.InvoiceRefBySetID(*ctx.setID())
|
invRef = InvoiceRefBySetID(*ctx.setID())
|
||||||
} else {
|
} else {
|
||||||
invRef = ctx.invoiceRef()
|
invRef = ctx.invoiceRef()
|
||||||
}
|
}
|
||||||
@@ -1020,12 +1024,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
resolution HtlcResolution
|
resolution HtlcResolution
|
||||||
updateSubscribers bool
|
updateSubscribers bool
|
||||||
)
|
)
|
||||||
invoice, err := i.cdb.UpdateInvoice(
|
|
||||||
ctx.invoiceRef(),
|
|
||||||
(*channeldb.SetID)(ctx.setID()),
|
|
||||||
func(inv *channeldb.Invoice) (
|
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
|
||||||
|
|
||||||
|
callback := func(inv *Invoice) (*InvoiceUpdateDesc, error) {
|
||||||
updateDesc, res, err := updateInvoice(ctx, inv)
|
updateDesc, res, err := updateInvoice(ctx, inv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1039,10 +1039,14 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
resolution = res
|
resolution = res
|
||||||
|
|
||||||
return updateDesc, nil
|
return updateDesc, nil
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
|
||||||
if _, ok := err.(channeldb.ErrDuplicateSetID); ok {
|
invoiceRef := ctx.invoiceRef()
|
||||||
|
setID := (*SetID)(ctx.setID())
|
||||||
|
invoice, err := i.idb.UpdateInvoice(invoiceRef, setID, callback)
|
||||||
|
|
||||||
|
var duplicateSetIDErr ErrDuplicateSetID
|
||||||
|
if errors.As(err, &duplicateSetIDErr) {
|
||||||
return NewFailResolution(
|
return NewFailResolution(
|
||||||
ctx.circuitKey, ctx.currentHeight,
|
ctx.circuitKey, ctx.currentHeight,
|
||||||
ResultInvoiceNotFound,
|
ResultInvoiceNotFound,
|
||||||
@@ -1050,7 +1054,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch err {
|
switch err {
|
||||||
case channeldb.ErrInvoiceNotFound:
|
case ErrInvoiceNotFound:
|
||||||
// If the invoice was not found, return a failure resolution
|
// If the invoice was not found, return a failure resolution
|
||||||
// with an invoice not found result.
|
// with an invoice not found result.
|
||||||
return NewFailResolution(
|
return NewFailResolution(
|
||||||
@@ -1058,7 +1062,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
ResultInvoiceNotFound,
|
ResultInvoiceNotFound,
|
||||||
), nil, nil
|
), nil, nil
|
||||||
|
|
||||||
case channeldb.ErrInvRefEquivocation:
|
case ErrInvRefEquivocation:
|
||||||
return NewFailResolution(
|
return NewFailResolution(
|
||||||
ctx.circuitKey, ctx.currentHeight,
|
ctx.circuitKey, ctx.currentHeight,
|
||||||
ResultInvoiceNotFound,
|
ResultInvoiceNotFound,
|
||||||
@@ -1100,7 +1104,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
// Also cancel any HTLCs in the HTLC set that are also in the
|
// Also cancel any HTLCs in the HTLC set that are also in the
|
||||||
// canceled state with the same failure result.
|
// canceled state with the same failure result.
|
||||||
setID := ctx.setID()
|
setID := ctx.setID()
|
||||||
canceledHtlcSet := invoice.HTLCSet(setID, channeldb.HtlcStateCanceled)
|
canceledHtlcSet := invoice.HTLCSet(setID, HtlcStateCanceled)
|
||||||
for key, htlc := range canceledHtlcSet {
|
for key, htlc := range canceledHtlcSet {
|
||||||
htlcFailResolution := NewFailResolution(
|
htlcFailResolution := NewFailResolution(
|
||||||
key, int32(htlc.AcceptHeight), res.Outcome,
|
key, int32(htlc.AcceptHeight), res.Outcome,
|
||||||
@@ -1120,7 +1124,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
// marked as settled, we should follow now and settle the htlc
|
// marked as settled, we should follow now and settle the htlc
|
||||||
// with our peer.
|
// with our peer.
|
||||||
setID := ctx.setID()
|
setID := ctx.setID()
|
||||||
settledHtlcSet := invoice.HTLCSet(setID, channeldb.HtlcStateSettled)
|
settledHtlcSet := invoice.HTLCSet(setID, HtlcStateSettled)
|
||||||
for key, htlc := range settledHtlcSet {
|
for key, htlc := range settledHtlcSet {
|
||||||
preimage := res.Preimage
|
preimage := res.Preimage
|
||||||
if htlc.AMP != nil && htlc.AMP.Preimage != nil {
|
if htlc.AMP != nil && htlc.AMP.Preimage != nil {
|
||||||
@@ -1150,7 +1154,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
//
|
//
|
||||||
// TODO(roasbeef): can remove now??
|
// TODO(roasbeef): can remove now??
|
||||||
canceledHtlcSet := invoice.HTLCSetCompliment(
|
canceledHtlcSet := invoice.HTLCSetCompliment(
|
||||||
setID, channeldb.HtlcStateCanceled,
|
setID, HtlcStateCanceled,
|
||||||
)
|
)
|
||||||
for key, htlc := range canceledHtlcSet {
|
for key, htlc := range canceledHtlcSet {
|
||||||
htlcFailResolution := NewFailResolution(
|
htlcFailResolution := NewFailResolution(
|
||||||
@@ -1184,7 +1188,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
|
|||||||
// Auto-release the htlc if the invoice is still open. It can
|
// Auto-release the htlc if the invoice is still open. It can
|
||||||
// only happen for mpp payments that there are htlcs in state
|
// only happen for mpp payments that there are htlcs in state
|
||||||
// Accepted while the invoice is Open.
|
// Accepted while the invoice is Open.
|
||||||
if invoice.State == channeldb.ContractOpen {
|
if invoice.State == ContractOpen {
|
||||||
res.acceptTime = invoiceHtlc.AcceptTime
|
res.acceptTime = invoiceHtlc.AcceptTime
|
||||||
res.autoRelease = true
|
res.autoRelease = true
|
||||||
}
|
}
|
||||||
@@ -1226,29 +1230,31 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
|||||||
i.Lock()
|
i.Lock()
|
||||||
defer i.Unlock()
|
defer i.Unlock()
|
||||||
|
|
||||||
updateInvoice := func(invoice *channeldb.Invoice) (
|
updateInvoice := func(invoice *Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
*InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
switch invoice.State {
|
switch invoice.State {
|
||||||
case channeldb.ContractOpen:
|
case ContractOpen:
|
||||||
return nil, channeldb.ErrInvoiceStillOpen
|
return nil, ErrInvoiceStillOpen
|
||||||
case channeldb.ContractCanceled:
|
|
||||||
return nil, channeldb.ErrInvoiceAlreadyCanceled
|
case ContractCanceled:
|
||||||
case channeldb.ContractSettled:
|
return nil, ErrInvoiceAlreadyCanceled
|
||||||
return nil, channeldb.ErrInvoiceAlreadySettled
|
|
||||||
|
case ContractSettled:
|
||||||
|
return nil, ErrInvoiceAlreadySettled
|
||||||
}
|
}
|
||||||
|
|
||||||
return &channeldb.InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
State: &channeldb.InvoiceStateUpdateDesc{
|
State: &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractSettled,
|
NewState: ContractSettled,
|
||||||
Preimage: &preimage,
|
Preimage: &preimage,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := preimage.Hash()
|
hash := preimage.Hash()
|
||||||
invoiceRef := channeldb.InvoiceRefByHash(hash)
|
invoiceRef := InvoiceRefByHash(hash)
|
||||||
invoice, err := i.cdb.UpdateInvoice(invoiceRef, nil, updateInvoice)
|
invoice, err := i.idb.UpdateInvoice(invoiceRef, nil, updateInvoice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("SettleHodlInvoice with preimage %v: %v",
|
log.Errorf("SettleHodlInvoice with preimage %v: %v",
|
||||||
preimage, err)
|
preimage, err)
|
||||||
@@ -1266,7 +1272,7 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
|||||||
// that were already settled before, will be notified again. This isn't
|
// that were already settled before, will be notified again. This isn't
|
||||||
// necessary but doesn't hurt either.
|
// necessary but doesn't hurt either.
|
||||||
for key, htlc := range invoice.Htlcs {
|
for key, htlc := range invoice.Htlcs {
|
||||||
if htlc.State != channeldb.HtlcStateSettled {
|
if htlc.State != HtlcStateSettled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1291,8 +1297,8 @@ func (i *InvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
|
|||||||
// cancel already accepted invoices, taking our force cancel boolean into
|
// cancel already accepted invoices, taking our force cancel boolean into
|
||||||
// account. This is pulled out into its own function so that tests that mock
|
// account. This is pulled out into its own function so that tests that mock
|
||||||
// cancelInvoiceImpl can reuse this logic.
|
// cancelInvoiceImpl can reuse this logic.
|
||||||
func shouldCancel(state channeldb.ContractState, cancelAccepted bool) bool {
|
func shouldCancel(state ContractState, cancelAccepted bool) bool {
|
||||||
if state != channeldb.ContractAccepted {
|
if state != ContractAccepted {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1311,12 +1317,10 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
i.Lock()
|
i.Lock()
|
||||||
defer i.Unlock()
|
defer i.Unlock()
|
||||||
|
|
||||||
ref := channeldb.InvoiceRefByHash(payHash)
|
ref := InvoiceRefByHash(payHash)
|
||||||
log.Debugf("Invoice%v: canceling invoice", ref)
|
log.Debugf("Invoice%v: canceling invoice", ref)
|
||||||
|
|
||||||
updateInvoice := func(invoice *channeldb.Invoice) (
|
updateInvoice := func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
|
||||||
|
|
||||||
if !shouldCancel(invoice.State, cancelAccepted) {
|
if !shouldCancel(invoice.State, cancelAccepted) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -1324,19 +1328,19 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
// Move invoice to the canceled state. Rely on validation in
|
// Move invoice to the canceled state. Rely on validation in
|
||||||
// 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 &channeldb.InvoiceUpdateDesc{
|
return &InvoiceUpdateDesc{
|
||||||
State: &channeldb.InvoiceStateUpdateDesc{
|
State: &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractCanceled,
|
NewState: ContractCanceled,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
invoiceRef := channeldb.InvoiceRefByHash(payHash)
|
invoiceRef := InvoiceRefByHash(payHash)
|
||||||
invoice, err := i.cdb.UpdateInvoice(invoiceRef, nil, updateInvoice)
|
invoice, err := i.idb.UpdateInvoice(invoiceRef, nil, updateInvoice)
|
||||||
|
|
||||||
// Implement idempotency by returning success if the invoice was already
|
// Implement idempotency by returning success if the invoice was already
|
||||||
// canceled.
|
// canceled.
|
||||||
if err == channeldb.ErrInvoiceAlreadyCanceled {
|
if errors.Is(err, ErrInvoiceAlreadyCanceled) {
|
||||||
log.Debugf("Invoice%v: already canceled", ref)
|
log.Debugf("Invoice%v: already canceled", ref)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1345,7 +1349,7 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return without cancellation if the invoice state is ContractAccepted.
|
// Return without cancellation if the invoice state is ContractAccepted.
|
||||||
if invoice.State == channeldb.ContractAccepted {
|
if invoice.State == ContractAccepted {
|
||||||
log.Debugf("Invoice%v: remains accepted as cancel wasn't"+
|
log.Debugf("Invoice%v: remains accepted as cancel wasn't"+
|
||||||
"explicitly requested.", ref)
|
"explicitly requested.", ref)
|
||||||
return nil
|
return nil
|
||||||
@@ -1359,7 +1363,7 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
// before, will be notified again. This isn't necessary but doesn't hurt
|
// before, will be notified again. This isn't necessary but doesn't hurt
|
||||||
// either.
|
// either.
|
||||||
for key, htlc := range invoice.Htlcs {
|
for key, htlc := range invoice.Htlcs {
|
||||||
if htlc.State != channeldb.HtlcStateCanceled {
|
if htlc.State != HtlcStateCanceled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1376,24 +1380,22 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
if i.cfg.GcCanceledInvoicesOnTheFly {
|
if i.cfg.GcCanceledInvoicesOnTheFly {
|
||||||
// Assemble the delete reference and attempt to delete through
|
// Assemble the delete reference and attempt to delete through
|
||||||
// the invocice from the DB.
|
// the invocice from the DB.
|
||||||
deleteRef := channeldb.InvoiceDeleteRef{
|
deleteRef := InvoiceDeleteRef{
|
||||||
PayHash: payHash,
|
PayHash: payHash,
|
||||||
AddIndex: invoice.AddIndex,
|
AddIndex: invoice.AddIndex,
|
||||||
SettleIndex: invoice.SettleIndex,
|
SettleIndex: invoice.SettleIndex,
|
||||||
}
|
}
|
||||||
if invoice.Terms.PaymentAddr != channeldb.BlankPayAddr {
|
if invoice.Terms.PaymentAddr != BlankPayAddr {
|
||||||
deleteRef.PayAddr = &invoice.Terms.PaymentAddr
|
deleteRef.PayAddr = &invoice.Terms.PaymentAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
err = i.cdb.DeleteInvoice(
|
err = i.idb.DeleteInvoice([]InvoiceDeleteRef{deleteRef})
|
||||||
[]channeldb.InvoiceDeleteRef{deleteRef},
|
|
||||||
)
|
|
||||||
// If by any chance deletion failed, then log it instead of
|
// If by any chance deletion failed, then log it instead of
|
||||||
// returning the error, as the invoice itsels has already been
|
// returning the error, as the invoice itself has already been
|
||||||
// canceled.
|
// canceled.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Invoice%v could not be deleted: %v",
|
log.Warnf("Invoice %v could not be deleted: %v", ref,
|
||||||
ref, err)
|
err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1403,7 +1405,7 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
|||||||
// notifyClients notifies all currently registered invoice notification clients
|
// notifyClients notifies all currently registered invoice notification clients
|
||||||
// of a newly added/settled invoice.
|
// of a newly added/settled invoice.
|
||||||
func (i *InvoiceRegistry) notifyClients(hash lntypes.Hash,
|
func (i *InvoiceRegistry) notifyClients(hash lntypes.Hash,
|
||||||
invoice *channeldb.Invoice, setID *[32]byte) {
|
invoice *Invoice, setID *[32]byte) {
|
||||||
|
|
||||||
event := &invoiceEvent{
|
event := &invoiceEvent{
|
||||||
invoice: invoice,
|
invoice: invoice,
|
||||||
@@ -1446,12 +1448,12 @@ type InvoiceSubscription struct {
|
|||||||
// NewInvoices is a channel that we'll use to send all newly created
|
// NewInvoices is a channel that we'll use to send all newly created
|
||||||
// invoices with an invoice index greater than the specified
|
// invoices with an invoice index greater than the specified
|
||||||
// StartingInvoiceIndex field.
|
// StartingInvoiceIndex field.
|
||||||
NewInvoices chan *channeldb.Invoice
|
NewInvoices chan *Invoice
|
||||||
|
|
||||||
// SettledInvoices is a channel that we'll use to send all settled
|
// SettledInvoices is a channel that we'll use to send all settled
|
||||||
// invoices with an invoices index greater than the specified
|
// invoices with an invoices index greater than the specified
|
||||||
// StartingInvoiceIndex field.
|
// StartingInvoiceIndex field.
|
||||||
SettledInvoices chan *channeldb.Invoice
|
SettledInvoices chan *Invoice
|
||||||
|
|
||||||
// addIndex is the highest add index the caller knows of. We'll use
|
// addIndex is the highest add index the caller knows of. We'll use
|
||||||
// this information to send out an event backlog to the notifications
|
// this information to send out an event backlog to the notifications
|
||||||
@@ -1472,11 +1474,19 @@ type InvoiceSubscription struct {
|
|||||||
type SingleInvoiceSubscription struct {
|
type SingleInvoiceSubscription struct {
|
||||||
invoiceSubscriptionKit
|
invoiceSubscriptionKit
|
||||||
|
|
||||||
invoiceRef channeldb.InvoiceRef
|
invoiceRef InvoiceRef
|
||||||
|
|
||||||
// Updates is a channel that we'll use to send all invoice events for
|
// Updates is a channel that we'll use to send all invoice events for
|
||||||
// the invoice that is subscribed to.
|
// the invoice that is subscribed to.
|
||||||
Updates chan *channeldb.Invoice
|
Updates chan *Invoice
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayHash returns the optional payment hash of the target invoice.
|
||||||
|
//
|
||||||
|
// TODO(positiveblue): This method is only supposed to be used in tests. It will
|
||||||
|
// be deleted as soon as invoiceregistery_test is in the same module.
|
||||||
|
func (s *SingleInvoiceSubscription) PayHash() *lntypes.Hash {
|
||||||
|
return s.invoiceRef.PayHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel unregisters the InvoiceSubscription, freeing any previously allocated
|
// Cancel unregisters the InvoiceSubscription, freeing any previously allocated
|
||||||
@@ -1493,6 +1503,7 @@ func (i *invoiceSubscriptionKit) Cancel() {
|
|||||||
func (i *invoiceSubscriptionKit) notify(event *invoiceEvent) error {
|
func (i *invoiceSubscriptionKit) notify(event *invoiceEvent) error {
|
||||||
select {
|
select {
|
||||||
case i.ntfnQueue.ChanIn() <- event:
|
case i.ntfnQueue.ChanIn() <- event:
|
||||||
|
|
||||||
case <-i.cancelChan:
|
case <-i.cancelChan:
|
||||||
// This can only be triggered by delivery of non-backlog
|
// This can only be triggered by delivery of non-backlog
|
||||||
// events.
|
// events.
|
||||||
@@ -1513,8 +1524,8 @@ func (i *InvoiceRegistry) SubscribeNotifications(
|
|||||||
addIndex, settleIndex uint64) (*InvoiceSubscription, error) {
|
addIndex, settleIndex uint64) (*InvoiceSubscription, error) {
|
||||||
|
|
||||||
client := &InvoiceSubscription{
|
client := &InvoiceSubscription{
|
||||||
NewInvoices: make(chan *channeldb.Invoice),
|
NewInvoices: make(chan *Invoice),
|
||||||
SettledInvoices: make(chan *channeldb.Invoice),
|
SettledInvoices: make(chan *Invoice),
|
||||||
addIndex: addIndex,
|
addIndex: addIndex,
|
||||||
settleIndex: settleIndex,
|
settleIndex: settleIndex,
|
||||||
invoiceSubscriptionKit: invoiceSubscriptionKit{
|
invoiceSubscriptionKit: invoiceSubscriptionKit{
|
||||||
@@ -1551,24 +1562,25 @@ func (i *InvoiceRegistry) SubscribeNotifications(
|
|||||||
case ntfn := <-client.ntfnQueue.ChanOut():
|
case ntfn := <-client.ntfnQueue.ChanOut():
|
||||||
invoiceEvent := ntfn.(*invoiceEvent)
|
invoiceEvent := ntfn.(*invoiceEvent)
|
||||||
|
|
||||||
var targetChan chan *channeldb.Invoice
|
var targetChan chan *Invoice
|
||||||
state := invoiceEvent.invoice.State
|
state := invoiceEvent.invoice.State
|
||||||
switch {
|
switch {
|
||||||
// AMP invoices never move to settled, but will
|
// AMP invoices never move to settled, but will
|
||||||
// be sent with a set ID if an HTLC set is
|
// be sent with a set ID if an HTLC set is
|
||||||
// being settled.
|
// being settled.
|
||||||
case state == channeldb.ContractOpen &&
|
case state == ContractOpen &&
|
||||||
invoiceEvent.setID != nil:
|
invoiceEvent.setID != nil:
|
||||||
fallthrough
|
fallthrough
|
||||||
case state == channeldb.ContractSettled:
|
|
||||||
|
case state == ContractSettled:
|
||||||
targetChan = client.SettledInvoices
|
targetChan = client.SettledInvoices
|
||||||
|
|
||||||
case state == channeldb.ContractOpen:
|
case state == ContractOpen:
|
||||||
targetChan = client.NewInvoices
|
targetChan = client.NewInvoices
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Errorf("unknown invoice "+
|
log.Errorf("unknown invoice state: %v",
|
||||||
"state: %v", state)
|
state)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1614,14 +1626,14 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
|
|||||||
hash lntypes.Hash) (*SingleInvoiceSubscription, error) {
|
hash lntypes.Hash) (*SingleInvoiceSubscription, error) {
|
||||||
|
|
||||||
client := &SingleInvoiceSubscription{
|
client := &SingleInvoiceSubscription{
|
||||||
Updates: make(chan *channeldb.Invoice),
|
Updates: make(chan *Invoice),
|
||||||
invoiceSubscriptionKit: invoiceSubscriptionKit{
|
invoiceSubscriptionKit: invoiceSubscriptionKit{
|
||||||
quit: i.quit,
|
quit: i.quit,
|
||||||
ntfnQueue: queue.NewConcurrentQueue(20),
|
ntfnQueue: queue.NewConcurrentQueue(20),
|
||||||
cancelChan: make(chan struct{}),
|
cancelChan: make(chan struct{}),
|
||||||
backlogDelivered: make(chan struct{}),
|
backlogDelivered: make(chan struct{}),
|
||||||
},
|
},
|
||||||
invoiceRef: channeldb.InvoiceRefByHash(hash),
|
invoiceRef: InvoiceRefByHash(hash),
|
||||||
}
|
}
|
||||||
client.ntfnQueue.Start()
|
client.ntfnQueue.Start()
|
||||||
|
|
||||||
@@ -1715,7 +1727,7 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
|||||||
|
|
||||||
// hodlSubscribe adds a new invoice subscription.
|
// hodlSubscribe adds a new invoice subscription.
|
||||||
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
||||||
circuitKey channeldb.CircuitKey) {
|
circuitKey CircuitKey) {
|
||||||
|
|
||||||
i.hodlSubscriptionsMux.Lock()
|
i.hodlSubscriptionsMux.Lock()
|
||||||
defer i.hodlSubscriptionsMux.Unlock()
|
defer i.hodlSubscriptionsMux.Unlock()
|
||||||
@@ -1731,7 +1743,7 @@ func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
|||||||
|
|
||||||
reverseSubscriptions, ok := i.hodlReverseSubscriptions[subscriber]
|
reverseSubscriptions, ok := i.hodlReverseSubscriptions[subscriber]
|
||||||
if !ok {
|
if !ok {
|
||||||
reverseSubscriptions = make(map[channeldb.CircuitKey]struct{})
|
reverseSubscriptions = make(map[CircuitKey]struct{})
|
||||||
i.hodlReverseSubscriptions[subscriber] = reverseSubscriptions
|
i.hodlReverseSubscriptions[subscriber] = reverseSubscriptions
|
||||||
}
|
}
|
||||||
reverseSubscriptions[circuitKey] = struct{}{}
|
reverseSubscriptions[circuitKey] = struct{}{}
|
||||||
@@ -1752,7 +1764,7 @@ func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
|
|||||||
|
|
||||||
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
|
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
|
||||||
// useful when we need to iterate the map to send notifications.
|
// useful when we need to iterate the map to send notifications.
|
||||||
func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscription {
|
func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscription { //nolint:lll
|
||||||
i.notificationClientMux.RLock()
|
i.notificationClientMux.RLock()
|
||||||
defer i.notificationClientMux.RUnlock()
|
defer i.notificationClientMux.RUnlock()
|
||||||
|
|
||||||
|
|||||||
+201
-191
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,795 @@
|
|||||||
|
package invoices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/feature"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxMemoSize is maximum size of the memo field within invoices stored
|
||||||
|
// in the database.
|
||||||
|
MaxMemoSize = 1024
|
||||||
|
|
||||||
|
// MaxPaymentRequestSize is the max size of a payment request for
|
||||||
|
// this invoice.
|
||||||
|
// TODO(halseth): determine the max length payment request when field
|
||||||
|
// lengths are final.
|
||||||
|
MaxPaymentRequestSize = 4096
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// unknownPreimage is an all-zeroes preimage that indicates that the
|
||||||
|
// preimage for this invoice is not yet known.
|
||||||
|
UnknownPreimage lntypes.Preimage
|
||||||
|
|
||||||
|
// BlankPayAddr is a sentinel payment address for legacy invoices.
|
||||||
|
// Invoices with this payment address are special-cased in the insertion
|
||||||
|
// logic to prevent being indexed in the payment address index,
|
||||||
|
// otherwise they would cause collisions after the first insertion.
|
||||||
|
BlankPayAddr [32]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
// RefModifier is a modification on top of a base invoice ref. It allows the
|
||||||
|
// caller to opt to skip out on HTLCs for a given payAddr, or only return the
|
||||||
|
// set of specified HTLCs for a given setID.
|
||||||
|
type RefModifier uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultModifier is the base modifier that doesn't change any
|
||||||
|
// behavior.
|
||||||
|
DefaultModifier RefModifier = iota
|
||||||
|
|
||||||
|
// HtlcSetOnlyModifier can only be used with a setID based invoice ref,
|
||||||
|
// and specifies that only the set of HTLCs related to that setID are
|
||||||
|
// to be returned.
|
||||||
|
HtlcSetOnlyModifier
|
||||||
|
|
||||||
|
// HtlcSetOnlyModifier can only be used with a payAddr based invoice
|
||||||
|
// ref, and specifies that the returned invoice shouldn't include any
|
||||||
|
// HTLCs at all.
|
||||||
|
HtlcSetBlankModifier
|
||||||
|
)
|
||||||
|
|
||||||
|
// InvoiceRef is a composite identifier for invoices. Invoices can be referenced
|
||||||
|
// by various combinations of payment hash and payment addr, in certain contexts
|
||||||
|
// only some of these are known. An InvoiceRef and its constructors thus
|
||||||
|
// encapsulate the valid combinations of query parameters that can be supplied
|
||||||
|
// to LookupInvoice and UpdateInvoice.
|
||||||
|
type InvoiceRef struct {
|
||||||
|
// payHash is the payment hash of the target invoice. All invoices are
|
||||||
|
// currently indexed by payment hash. This value will be used as a
|
||||||
|
// fallback when no payment address is known.
|
||||||
|
payHash *lntypes.Hash
|
||||||
|
|
||||||
|
// payAddr is the payment addr of the target invoice. Newer invoices
|
||||||
|
// (0.11 and up) are indexed by payment address in addition to payment
|
||||||
|
// hash, but pre 0.8 invoices do not have one at all. When this value is
|
||||||
|
// known it will be used as the primary identifier, falling back to
|
||||||
|
// payHash if no value is known.
|
||||||
|
payAddr *[32]byte
|
||||||
|
|
||||||
|
// setID is the optional set id for an AMP payment. This can be used to
|
||||||
|
// lookup or update the invoice knowing only this value. Queries by set
|
||||||
|
// id are only used to facilitate user-facing requests, e.g. lookup,
|
||||||
|
// settle or cancel an AMP invoice. The regular update flow from the
|
||||||
|
// invoice registry will always query for the invoice by
|
||||||
|
// payHash+payAddr.
|
||||||
|
setID *[32]byte
|
||||||
|
|
||||||
|
// refModifier allows an invoice ref to include or exclude specific
|
||||||
|
// HTLC sets based on the payAddr or setId.
|
||||||
|
refModifier RefModifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefByHash creates an InvoiceRef that queries for an invoice only by
|
||||||
|
// its payment hash.
|
||||||
|
func InvoiceRefByHash(payHash lntypes.Hash) InvoiceRef {
|
||||||
|
return InvoiceRef{
|
||||||
|
payHash: &payHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefByHashAndAddr creates an InvoiceRef that first queries for an
|
||||||
|
// invoice by the provided payment address, falling back to the payment hash if
|
||||||
|
// the payment address is unknown.
|
||||||
|
func InvoiceRefByHashAndAddr(payHash lntypes.Hash,
|
||||||
|
payAddr [32]byte) InvoiceRef {
|
||||||
|
|
||||||
|
return InvoiceRef{
|
||||||
|
payHash: &payHash,
|
||||||
|
payAddr: &payAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefByAddr creates an InvoiceRef that queries the payment addr index
|
||||||
|
// for an invoice with the provided payment address.
|
||||||
|
func InvoiceRefByAddr(addr [32]byte) InvoiceRef {
|
||||||
|
return InvoiceRef{
|
||||||
|
payAddr: &addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefByAddrBlankHtlc creates an InvoiceRef that queries the payment addr
|
||||||
|
// index for an invoice with the provided payment address, but excludes any of
|
||||||
|
// the core HTLC information.
|
||||||
|
func InvoiceRefByAddrBlankHtlc(addr [32]byte) InvoiceRef {
|
||||||
|
return InvoiceRef{
|
||||||
|
payAddr: &addr,
|
||||||
|
refModifier: HtlcSetBlankModifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefBySetID creates an InvoiceRef that queries the set id index for an
|
||||||
|
// invoice with the provided setID. If the invoice is not found, the query will
|
||||||
|
// not fallback to payHash or payAddr.
|
||||||
|
func InvoiceRefBySetID(setID [32]byte) InvoiceRef {
|
||||||
|
return InvoiceRef{
|
||||||
|
setID: &setID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceRefBySetIDFiltered is similar to the InvoiceRefBySetID identifier,
|
||||||
|
// but it specifies that the returned set of HTLCs should be filtered to only
|
||||||
|
// include HTLCs that are part of that set.
|
||||||
|
func InvoiceRefBySetIDFiltered(setID [32]byte) InvoiceRef {
|
||||||
|
return InvoiceRef{
|
||||||
|
setID: &setID,
|
||||||
|
refModifier: HtlcSetOnlyModifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayHash returns the optional payment hash of the target invoice.
|
||||||
|
//
|
||||||
|
// NOTE: This value may be nil.
|
||||||
|
func (r InvoiceRef) PayHash() *lntypes.Hash {
|
||||||
|
if r.payHash != nil {
|
||||||
|
hash := *r.payHash
|
||||||
|
return &hash
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayAddr returns the optional payment address of the target invoice.
|
||||||
|
//
|
||||||
|
// NOTE: This value may be nil.
|
||||||
|
func (r InvoiceRef) PayAddr() *[32]byte {
|
||||||
|
if r.payAddr != nil {
|
||||||
|
addr := *r.payAddr
|
||||||
|
return &addr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetID returns the optional set id of the target invoice.
|
||||||
|
//
|
||||||
|
// NOTE: This value may be nil.
|
||||||
|
func (r InvoiceRef) SetID() *[32]byte {
|
||||||
|
if r.setID != nil {
|
||||||
|
id := *r.setID
|
||||||
|
return &id
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifier defines the set of available modifications to the base invoice ref
|
||||||
|
// look up that are available.
|
||||||
|
func (r InvoiceRef) Modifier() RefModifier {
|
||||||
|
return r.refModifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable representation of an InvoiceRef.
|
||||||
|
func (r InvoiceRef) String() string {
|
||||||
|
var ids []string
|
||||||
|
if r.payHash != nil {
|
||||||
|
ids = append(ids, fmt.Sprintf("pay_hash=%v", *r.payHash))
|
||||||
|
}
|
||||||
|
if r.payAddr != nil {
|
||||||
|
ids = append(ids, fmt.Sprintf("pay_addr=%x", *r.payAddr))
|
||||||
|
}
|
||||||
|
if r.setID != nil {
|
||||||
|
ids = append(ids, fmt.Sprintf("set_id=%x", *r.setID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("(%s)", strings.Join(ids, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractState describes the state the invoice is in.
|
||||||
|
type ContractState uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContractOpen means the invoice has only been created.
|
||||||
|
ContractOpen ContractState = 0
|
||||||
|
|
||||||
|
// ContractSettled means the htlc is settled and the invoice has been
|
||||||
|
// paid.
|
||||||
|
ContractSettled ContractState = 1
|
||||||
|
|
||||||
|
// ContractCanceled means the invoice has been canceled.
|
||||||
|
ContractCanceled ContractState = 2
|
||||||
|
|
||||||
|
// ContractAccepted means the HTLC has been accepted but not settled
|
||||||
|
// yet.
|
||||||
|
ContractAccepted ContractState = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a human readable identifier for the ContractState type.
|
||||||
|
func (c ContractState) String() string {
|
||||||
|
switch c {
|
||||||
|
case ContractOpen:
|
||||||
|
return "Open"
|
||||||
|
|
||||||
|
case ContractSettled:
|
||||||
|
return "Settled"
|
||||||
|
|
||||||
|
case ContractCanceled:
|
||||||
|
return "Canceled"
|
||||||
|
|
||||||
|
case ContractAccepted:
|
||||||
|
return "Accepted"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFinal returns a boolean indicating whether an invoice state is final.
|
||||||
|
func (c ContractState) IsFinal() bool {
|
||||||
|
return c == ContractSettled || c == ContractCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractTerm is a companion struct to the Invoice struct. This struct houses
|
||||||
|
// the necessary conditions required before the invoice can be considered fully
|
||||||
|
// settled by the payee.
|
||||||
|
type ContractTerm struct {
|
||||||
|
// FinalCltvDelta is the minimum required number of blocks before htlc
|
||||||
|
// expiry when the invoice is accepted.
|
||||||
|
FinalCltvDelta int32
|
||||||
|
|
||||||
|
// Expiry defines how long after creation this invoice should expire.
|
||||||
|
Expiry time.Duration
|
||||||
|
|
||||||
|
// PaymentPreimage is the preimage which is to be revealed in the
|
||||||
|
// occasion that an HTLC paying to the hash of this preimage is
|
||||||
|
// 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.
|
||||||
|
Value lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// PaymentAddr is a randomly generated value include in the MPP record
|
||||||
|
// by the sender to prevent probing of the receiver.
|
||||||
|
PaymentAddr [32]byte
|
||||||
|
|
||||||
|
// Features is the feature vectors advertised on the payment request.
|
||||||
|
Features *lnwire.FeatureVector
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable description of the prominent contract terms.
|
||||||
|
func (c ContractTerm) String() string {
|
||||||
|
return fmt.Sprintf("amt=%v, expiry=%v, final_cltv_delta=%v", c.Value,
|
||||||
|
c.Expiry, c.FinalCltvDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetID is the extra unique tuple item for AMP invoices. In addition to
|
||||||
|
// setting a payment address, each repeated payment to an AMP invoice will also
|
||||||
|
// contain a set ID as well.
|
||||||
|
type SetID [32]byte
|
||||||
|
|
||||||
|
// InvoiceStateAMP is a struct that associates the current state of an AMP
|
||||||
|
// invoice identified by its set ID along with the set of invoices identified
|
||||||
|
// by the circuit key. This allows callers to easily look up the latest state
|
||||||
|
// of an AMP "sub-invoice" and also look up the invoice HLTCs themselves in the
|
||||||
|
// greater HTLC map index.
|
||||||
|
type InvoiceStateAMP struct {
|
||||||
|
// State is the state of this sub-AMP invoice.
|
||||||
|
State HtlcState
|
||||||
|
|
||||||
|
// SettleIndex indicates the location in the settle index that
|
||||||
|
// references this instance of InvoiceStateAMP, but only if
|
||||||
|
// this value is set (non-zero), and State is HtlcStateSettled.
|
||||||
|
SettleIndex uint64
|
||||||
|
|
||||||
|
// SettleDate is the date that the setID was settled.
|
||||||
|
SettleDate time.Time
|
||||||
|
|
||||||
|
// InvoiceKeys is the set of circuit keys that can be used to locate
|
||||||
|
// the invoices for a given set ID.
|
||||||
|
InvoiceKeys map[CircuitKey]struct{}
|
||||||
|
|
||||||
|
// AmtPaid is the total amount that was paid in the AMP sub-invoice.
|
||||||
|
// Fetching the full HTLC/invoice state allows one to extract the
|
||||||
|
// custom records as well as the break down of the payment splits used
|
||||||
|
// when paying.
|
||||||
|
AmtPaid lnwire.MilliSatoshi
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy makes a deep copy of the underlying InvoiceStateAMP.
|
||||||
|
func (i *InvoiceStateAMP) copy() (InvoiceStateAMP, error) {
|
||||||
|
result := *i
|
||||||
|
|
||||||
|
// Make a copy of the InvoiceKeys map.
|
||||||
|
result.InvoiceKeys = make(map[CircuitKey]struct{})
|
||||||
|
for k := range i.InvoiceKeys {
|
||||||
|
result.InvoiceKeys[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// As a safety measure, copy SettleDate. time.Time is concurrency safe
|
||||||
|
// except when using any of the (un)marshalling methods.
|
||||||
|
settleDateBytes, err := i.SettleDate.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return InvoiceStateAMP{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = result.SettleDate.UnmarshalBinary(settleDateBytes)
|
||||||
|
if err != nil {
|
||||||
|
return InvoiceStateAMP{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMPInvoiceState represents a type that stores metadata related to the set of
|
||||||
|
// settled AMP "sub-invoices".
|
||||||
|
type AMPInvoiceState map[SetID]InvoiceStateAMP
|
||||||
|
|
||||||
|
// Invoice is a payment invoice generated by a payee in order to request
|
||||||
|
// payment for some good or service. The inclusion of invoices within Lightning
|
||||||
|
// creates a payment work flow for merchants very similar to that of the
|
||||||
|
// existing financial system within PayPal, etc. Invoices are added to the
|
||||||
|
// database when a payment is requested, then can be settled manually once the
|
||||||
|
// payment is received at the upper layer. For record keeping purposes,
|
||||||
|
// invoices are never deleted from the database, instead a bit is toggled
|
||||||
|
// denoting the invoice has been fully settled. Within the database, all
|
||||||
|
// invoices must have a unique payment hash which is generated by taking the
|
||||||
|
// sha256 of the payment preimage.
|
||||||
|
type Invoice struct {
|
||||||
|
// Memo is an optional memo to be stored along side an invoice. The
|
||||||
|
// memo may contain further details pertaining to the invoice itself,
|
||||||
|
// or any other message which fits within the size constraints.
|
||||||
|
Memo []byte
|
||||||
|
|
||||||
|
// PaymentRequest is the encoded payment request for this invoice. For
|
||||||
|
// spontaneous (keysend) payments, this field will be empty.
|
||||||
|
PaymentRequest []byte
|
||||||
|
|
||||||
|
// CreationDate is the exact time the invoice was created.
|
||||||
|
CreationDate time.Time
|
||||||
|
|
||||||
|
// SettleDate is the exact time the invoice was settled.
|
||||||
|
SettleDate time.Time
|
||||||
|
|
||||||
|
// Terms are the contractual payment terms of the invoice. Once all the
|
||||||
|
// terms have been satisfied by the payer, then the invoice can be
|
||||||
|
// considered fully fulfilled.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): later allow for multiple terms to fulfill the final
|
||||||
|
// invoice: payment fragmentation, etc.
|
||||||
|
Terms ContractTerm
|
||||||
|
|
||||||
|
// AddIndex is an auto-incrementing integer that acts as a
|
||||||
|
// monotonically increasing sequence number for all invoices created.
|
||||||
|
// Clients can then use this field as a "checkpoint" of sorts when
|
||||||
|
// implementing a streaming RPC to notify consumers of instances where
|
||||||
|
// an invoice has been added before they re-connected.
|
||||||
|
//
|
||||||
|
// NOTE: This index starts at 1.
|
||||||
|
AddIndex uint64
|
||||||
|
|
||||||
|
// SettleIndex is an auto-incrementing integer that acts as a
|
||||||
|
// monotonically increasing sequence number for all settled invoices.
|
||||||
|
// Clients can then use this field as a "checkpoint" of sorts when
|
||||||
|
// implementing a streaming RPC to notify consumers of instances where
|
||||||
|
// an invoice has been settled before they re-connected.
|
||||||
|
//
|
||||||
|
// NOTE: This index starts at 1.
|
||||||
|
SettleIndex uint64
|
||||||
|
|
||||||
|
// State describes the state the invoice is in. This is the global
|
||||||
|
// state of the invoice which may remain open even when a series of
|
||||||
|
// sub-invoices for this invoice has been settled.
|
||||||
|
State ContractState
|
||||||
|
|
||||||
|
// AmtPaid is the final amount that we ultimately accepted for pay for
|
||||||
|
// this invoice. We specify this value independently as it's possible
|
||||||
|
// that the invoice originally didn't specify an amount, or the sender
|
||||||
|
// overpaid.
|
||||||
|
AmtPaid lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// Htlcs records all htlcs that paid to this invoice. Some of these
|
||||||
|
// htlcs may have been marked as canceled.
|
||||||
|
Htlcs map[CircuitKey]*InvoiceHTLC
|
||||||
|
|
||||||
|
// AMPState describes the state of any related sub-invoices AMP to this
|
||||||
|
// greater invoice. A sub-invoice is defined by a set of HTLCs with the
|
||||||
|
// same set ID that attempt to make one time or recurring payments to
|
||||||
|
// this greater invoice. It's possible for a sub-invoice to be canceled
|
||||||
|
// or settled, but the greater invoice still open.
|
||||||
|
AMPState AMPInvoiceState
|
||||||
|
|
||||||
|
// HodlInvoice indicates whether the invoice should be held in the
|
||||||
|
// Accepted state or be settled right away.
|
||||||
|
HodlInvoice bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTLCSet returns the set of HTLCs belonging to setID and in the provided
|
||||||
|
// state. Passing a nil setID will return all HTLCs in the provided state in the
|
||||||
|
// case of legacy or MPP, and no HTLCs in the case of AMP. Otherwise, the
|
||||||
|
// returned set will be filtered by the populated setID which is used to
|
||||||
|
// retrieve AMP HTLC sets.
|
||||||
|
func (i *Invoice) HTLCSet(setID *[32]byte,
|
||||||
|
state HtlcState) map[CircuitKey]*InvoiceHTLC {
|
||||||
|
|
||||||
|
htlcSet := make(map[CircuitKey]*InvoiceHTLC)
|
||||||
|
for key, htlc := range i.Htlcs {
|
||||||
|
// Only add HTLCs that are in the requested HtlcState.
|
||||||
|
if htlc.State != state {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !htlc.IsInHTLCSet(setID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
htlcSet[key] = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
return htlcSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTLCSetCompliment returns the set of all HTLCs not belonging to setID that
|
||||||
|
// are in the target state. Passing a nil setID will return no invoices, since
|
||||||
|
// all MPP HTLCs are part of the same HTLC set.
|
||||||
|
func (i *Invoice) HTLCSetCompliment(setID *[32]byte,
|
||||||
|
state HtlcState) map[CircuitKey]*InvoiceHTLC {
|
||||||
|
|
||||||
|
htlcSet := make(map[CircuitKey]*InvoiceHTLC)
|
||||||
|
for key, htlc := range i.Htlcs {
|
||||||
|
// Only add HTLCs that are in the requested HtlcState.
|
||||||
|
if htlc.State != state {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are constructing the compliment, so filter anything that
|
||||||
|
// matches this set id.
|
||||||
|
if htlc.IsInHTLCSet(setID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
htlcSet[key] = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
return htlcSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// HtlcState defines the states an htlc paying to an invoice can be in.
|
||||||
|
type HtlcState uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HtlcStateAccepted indicates the htlc is locked-in, but not resolved.
|
||||||
|
HtlcStateAccepted HtlcState = iota
|
||||||
|
|
||||||
|
// HtlcStateCanceled indicates the htlc is canceled back to the
|
||||||
|
// sender.
|
||||||
|
HtlcStateCanceled
|
||||||
|
|
||||||
|
// HtlcStateSettled indicates the htlc is settled.
|
||||||
|
HtlcStateSettled
|
||||||
|
)
|
||||||
|
|
||||||
|
// InvoiceHTLC contains details about an htlc paying to this invoice.
|
||||||
|
type InvoiceHTLC struct {
|
||||||
|
// Amt is the amount that is carried by this htlc.
|
||||||
|
Amt lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// MppTotalAmt is a field for mpp that indicates the expected total
|
||||||
|
// amount.
|
||||||
|
MppTotalAmt lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// AcceptHeight is the block height at which the invoice registry
|
||||||
|
// decided to accept this htlc as a payment to the invoice. At this
|
||||||
|
// height, the invoice cltv delay must have been met.
|
||||||
|
AcceptHeight uint32
|
||||||
|
|
||||||
|
// AcceptTime is the wall clock time at which the invoice registry
|
||||||
|
// decided to accept the htlc.
|
||||||
|
AcceptTime time.Time
|
||||||
|
|
||||||
|
// ResolveTime is the wall clock time at which the invoice registry
|
||||||
|
// decided to settle the htlc.
|
||||||
|
ResolveTime time.Time
|
||||||
|
|
||||||
|
// Expiry is the expiry height of this htlc.
|
||||||
|
Expiry uint32
|
||||||
|
|
||||||
|
// State indicates the state the invoice htlc is currently in. A
|
||||||
|
// canceled htlc isn't just removed from the invoice htlcs map, because
|
||||||
|
// we need AcceptHeight to properly cancel the htlc back.
|
||||||
|
State HtlcState
|
||||||
|
|
||||||
|
// CustomRecords contains the custom key/value pairs that accompanied
|
||||||
|
// the htlc.
|
||||||
|
CustomRecords record.CustomSet
|
||||||
|
|
||||||
|
// AMP encapsulates additional data relevant to AMP HTLCs. This includes
|
||||||
|
// the AMP onion record, in addition to the HTLC's payment hash and
|
||||||
|
// preimage since these are unique to each AMP HTLC, and not the invoice
|
||||||
|
// as a whole.
|
||||||
|
//
|
||||||
|
// NOTE: This value will only be set for AMP HTLCs.
|
||||||
|
AMP *InvoiceHtlcAMPData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of the target InvoiceHTLC.
|
||||||
|
func (h *InvoiceHTLC) Copy() *InvoiceHTLC {
|
||||||
|
result := *h
|
||||||
|
|
||||||
|
// Make a copy of the CustomSet map.
|
||||||
|
result.CustomRecords = make(record.CustomSet)
|
||||||
|
for k, v := range h.CustomRecords {
|
||||||
|
result.CustomRecords[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
result.AMP = h.AMP.Copy()
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInHTLCSet returns true if this HTLC is part an HTLC set. If nil is passed,
|
||||||
|
// this method returns true if this is an MPP HTLC. Otherwise, it only returns
|
||||||
|
// true if the AMP HTLC's set id matches the populated setID.
|
||||||
|
func (h *InvoiceHTLC) IsInHTLCSet(setID *[32]byte) bool {
|
||||||
|
wantAMPSet := setID != nil
|
||||||
|
isAMPHtlc := h.AMP != nil
|
||||||
|
|
||||||
|
// Non-AMP HTLCs cannot be part of AMP HTLC sets, and vice versa.
|
||||||
|
if wantAMPSet != isAMPHtlc {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip AMP HTLCs that have differing set ids.
|
||||||
|
if isAMPHtlc && *setID != h.AMP.Record.SetID() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceHtlcAMPData is a struct hodling the additional metadata stored for
|
||||||
|
// each received AMP HTLC. This includes the AMP onion record, in addition to
|
||||||
|
// the HTLC's payment hash and preimage.
|
||||||
|
type InvoiceHtlcAMPData struct {
|
||||||
|
// AMP is a copy of the AMP record presented in the onion payload
|
||||||
|
// containing the information necessary to correlate and settle a
|
||||||
|
// spontaneous HTLC set. Newly accepted legacy keysend payments will
|
||||||
|
// also have this field set as we automatically promote them into an AMP
|
||||||
|
// payment for internal processing.
|
||||||
|
Record record.AMP
|
||||||
|
|
||||||
|
// Hash is an HTLC-level payment hash that is stored only for AMP
|
||||||
|
// payments. This is done because an AMP HTLC will carry a different
|
||||||
|
// payment hash from the invoice it might be satisfying, so we track the
|
||||||
|
// payment hashes individually to able to compute whether or not the
|
||||||
|
// reconstructed preimage correctly matches the HTLC's hash.
|
||||||
|
Hash lntypes.Hash
|
||||||
|
|
||||||
|
// Preimage is an HTLC-level preimage that satisfies the AMP HTLC's
|
||||||
|
// Hash. The preimage will be derived either from secret share
|
||||||
|
// reconstruction of the shares in the AMP payload.
|
||||||
|
//
|
||||||
|
// NOTE: Preimage will only be present once the HTLC is in
|
||||||
|
// HtlcStateSettled.
|
||||||
|
Preimage *lntypes.Preimage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a deep copy of the InvoiceHtlcAMPData.
|
||||||
|
func (d *InvoiceHtlcAMPData) Copy() *InvoiceHtlcAMPData {
|
||||||
|
if d == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var preimage *lntypes.Preimage
|
||||||
|
if d.Preimage != nil {
|
||||||
|
pimg := *d.Preimage
|
||||||
|
preimage = &pimg
|
||||||
|
}
|
||||||
|
|
||||||
|
return &InvoiceHtlcAMPData{
|
||||||
|
Record: d.Record,
|
||||||
|
Hash: d.Hash,
|
||||||
|
Preimage: preimage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HtlcAcceptDesc describes the details of a newly accepted htlc.
|
||||||
|
type HtlcAcceptDesc struct {
|
||||||
|
// AcceptHeight is the block height at which this htlc was accepted.
|
||||||
|
AcceptHeight int32
|
||||||
|
|
||||||
|
// Amt is the amount that is carried by this htlc.
|
||||||
|
Amt lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// MppTotalAmt is a field for mpp that indicates the expected total
|
||||||
|
// amount.
|
||||||
|
MppTotalAmt lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// Expiry is the expiry height of this htlc.
|
||||||
|
Expiry uint32
|
||||||
|
|
||||||
|
// CustomRecords contains the custom key/value pairs that accompanied
|
||||||
|
// the htlc.
|
||||||
|
CustomRecords record.CustomSet
|
||||||
|
|
||||||
|
// AMP encapsulates additional data relevant to AMP HTLCs. This includes
|
||||||
|
// the AMP onion record, in addition to the HTLC's payment hash and
|
||||||
|
// preimage since these are unique to each AMP HTLC, and not the invoice
|
||||||
|
// as a whole.
|
||||||
|
//
|
||||||
|
// NOTE: This value will only be set for AMP HTLCs.
|
||||||
|
AMP *InvoiceHtlcAMPData
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceUpdateDesc describes the changes that should be applied to the
|
||||||
|
// invoice.
|
||||||
|
type InvoiceUpdateDesc struct {
|
||||||
|
// State is the new state that this invoice should progress to. If nil,
|
||||||
|
// the state is left unchanged.
|
||||||
|
State *InvoiceStateUpdateDesc
|
||||||
|
|
||||||
|
// CancelHtlcs describes the htlcs that need to be canceled.
|
||||||
|
CancelHtlcs map[CircuitKey]struct{}
|
||||||
|
|
||||||
|
// AddHtlcs describes the newly accepted htlcs that need to be added to
|
||||||
|
// the invoice.
|
||||||
|
AddHtlcs map[CircuitKey]*HtlcAcceptDesc
|
||||||
|
|
||||||
|
// SetID is an optional set ID for AMP invoices that allows operations
|
||||||
|
// 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.
|
||||||
|
SetID *SetID
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceStateUpdateDesc describes an invoice-level state transition.
|
||||||
|
type InvoiceStateUpdateDesc struct {
|
||||||
|
// NewState is the new state that this invoice should progress to.
|
||||||
|
NewState ContractState
|
||||||
|
|
||||||
|
// Preimage must be set to the preimage when NewState is settled.
|
||||||
|
Preimage *lntypes.Preimage
|
||||||
|
|
||||||
|
// HTLCPreimages set the HTLC-level preimages stored for AMP HTLCs.
|
||||||
|
// These are only learned when settling the invoice as a whole. Must be
|
||||||
|
// set when settling an invoice with non-nil SetID.
|
||||||
|
HTLCPreimages map[CircuitKey]lntypes.Preimage
|
||||||
|
|
||||||
|
// SetID identifies a specific set of HTLCs destined for the same
|
||||||
|
// invoice as part of a larger AMP payment. This value will be nil for
|
||||||
|
// legacy or MPP payments.
|
||||||
|
SetID *[32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceUpdateCallback is a callback used in the db transaction to update the
|
||||||
|
// invoice.
|
||||||
|
type InvoiceUpdateCallback = func(invoice *Invoice) (*InvoiceUpdateDesc, 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))
|
||||||
|
}
|
||||||
|
if len(i.PaymentRequest) > MaxPaymentRequestSize {
|
||||||
|
return fmt.Errorf("max length of payment request is %v, "+
|
||||||
|
"length provided was %v", MaxPaymentRequestSize,
|
||||||
|
len(i.PaymentRequest))
|
||||||
|
}
|
||||||
|
if i.Terms.Features == nil {
|
||||||
|
return errors.New("invoice must have a feature vector")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := feature.ValidateDeps(i.Terms.Features)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMP invoices and hodl invoices are allowed to have no preimage
|
||||||
|
// specified.
|
||||||
|
isAMP := i.Terms.Features.HasFeature(
|
||||||
|
lnwire.AMPOptional,
|
||||||
|
)
|
||||||
|
if i.Terms.PaymentPreimage == nil && !(i.HodlInvoice || isAMP) {
|
||||||
|
return errors.New("non-hodl invoices must have a preimage")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.Htlcs) > 0 {
|
||||||
|
return ErrInvoiceHasHtlcs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPending returns true if the invoice is in ContractOpen state.
|
||||||
|
func (i *Invoice) IsPending() bool {
|
||||||
|
return i.State == ContractOpen || i.State == ContractAccepted
|
||||||
|
}
|
||||||
|
|
||||||
|
// copySlice allocates a new slice and copies the source into it.
|
||||||
|
func copySlice(src []byte) []byte {
|
||||||
|
dest := make([]byte, len(src))
|
||||||
|
copy(dest, src)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyInvoice makes a deep copy of the supplied invoice.
|
||||||
|
func CopyInvoice(src *Invoice) (*Invoice, error) {
|
||||||
|
dest := Invoice{
|
||||||
|
Memo: copySlice(src.Memo),
|
||||||
|
PaymentRequest: copySlice(src.PaymentRequest),
|
||||||
|
CreationDate: src.CreationDate,
|
||||||
|
SettleDate: src.SettleDate,
|
||||||
|
Terms: src.Terms,
|
||||||
|
AddIndex: src.AddIndex,
|
||||||
|
SettleIndex: src.SettleIndex,
|
||||||
|
State: src.State,
|
||||||
|
AmtPaid: src.AmtPaid,
|
||||||
|
Htlcs: make(
|
||||||
|
map[CircuitKey]*InvoiceHTLC, len(src.Htlcs),
|
||||||
|
),
|
||||||
|
AMPState: make(map[SetID]InvoiceStateAMP),
|
||||||
|
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] = v.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lastly, copy the amp invoice state.
|
||||||
|
for k, v := range src.AMPState {
|
||||||
|
ampInvState, err := v.copy()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.AMPState[k] = ampInvState
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceDeleteRef holds a reference to an invoice to be deleted.
|
||||||
|
type InvoiceDeleteRef struct {
|
||||||
|
// PayHash is the payment hash of the target invoice. All invoices are
|
||||||
|
// currently indexed by payment hash.
|
||||||
|
PayHash lntypes.Hash
|
||||||
|
|
||||||
|
// PayAddr is the payment addr of the target invoice. Newer invoices
|
||||||
|
// (0.11 and up) are indexed by payment address in addition to payment
|
||||||
|
// hash, but pre 0.8 invoices do not have one at all.
|
||||||
|
PayAddr *[32]byte
|
||||||
|
|
||||||
|
// AddIndex is the add index of the invoice.
|
||||||
|
AddIndex uint64
|
||||||
|
|
||||||
|
// SettleIndex is the settle index of the invoice.
|
||||||
|
SettleIndex uint64
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package invoices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockInvoiceDB struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInvoicesDBMock() *MockInvoiceDB {
|
||||||
|
return &MockInvoiceDB{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) AddInvoice(invoice *Invoice,
|
||||||
|
paymentHash lntypes.Hash) (uint64, error) {
|
||||||
|
|
||||||
|
args := m.Called(invoice, paymentHash)
|
||||||
|
|
||||||
|
addIndex, _ := args.Get(0).(uint64)
|
||||||
|
|
||||||
|
// NOTE: this is a side effect of the AddInvoice method.
|
||||||
|
invoice.AddIndex = addIndex
|
||||||
|
|
||||||
|
return addIndex, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) InvoicesAddedSince(idx uint64) ([]Invoice, error) {
|
||||||
|
args := m.Called(idx)
|
||||||
|
invoices, _ := args.Get(0).([]Invoice)
|
||||||
|
|
||||||
|
return invoices, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) InvoicesSettledSince(idx uint64) ([]Invoice, error) {
|
||||||
|
args := m.Called(idx)
|
||||||
|
invoices, _ := args.Get(0).([]Invoice)
|
||||||
|
|
||||||
|
return invoices, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) LookupInvoice(ref InvoiceRef) (Invoice, error) {
|
||||||
|
args := m.Called(ref)
|
||||||
|
invoice, _ := args.Get(0).(Invoice)
|
||||||
|
|
||||||
|
return invoice, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) ScanInvoices(scanFunc InvScanFunc,
|
||||||
|
reset func()) error {
|
||||||
|
|
||||||
|
args := m.Called(scanFunc, reset)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
||||||
|
args := m.Called(q)
|
||||||
|
invoiceSlice, _ := args.Get(0).(InvoiceSlice)
|
||||||
|
|
||||||
|
return invoiceSlice, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) UpdateInvoice(ref InvoiceRef, setIDHint *SetID,
|
||||||
|
callback InvoiceUpdateCallback) (*Invoice, error) {
|
||||||
|
|
||||||
|
args := m.Called(ref, setIDHint, callback)
|
||||||
|
invoice, _ := args.Get(0).(*Invoice)
|
||||||
|
|
||||||
|
return invoice, args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInvoiceDB) DeleteInvoice(invoices []InvoiceDeleteRef) error {
|
||||||
|
args := m.Called(invoices)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
+12
-13
@@ -3,7 +3,6 @@ package invoices
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,14 +10,14 @@ import (
|
|||||||
type HtlcResolution interface {
|
type HtlcResolution interface {
|
||||||
// CircuitKey returns the circuit key for the htlc that we have a
|
// CircuitKey returns the circuit key for the htlc that we have a
|
||||||
// resolution for.
|
// resolution for.
|
||||||
CircuitKey() channeldb.CircuitKey
|
CircuitKey() CircuitKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// HtlcFailResolution is an implementation of the HtlcResolution interface
|
// HtlcFailResolution is an implementation of the HtlcResolution interface
|
||||||
// which is returned when a htlc is failed.
|
// which is returned when a htlc is failed.
|
||||||
type HtlcFailResolution struct {
|
type HtlcFailResolution struct {
|
||||||
// circuitKey is the key of the htlc for which we have a resolution.
|
// circuitKey is the key of the htlc for which we have a resolution.
|
||||||
circuitKey channeldb.CircuitKey
|
circuitKey CircuitKey
|
||||||
|
|
||||||
// AcceptHeight is the original height at which the htlc was accepted.
|
// AcceptHeight is the original height at which the htlc was accepted.
|
||||||
AcceptHeight int32
|
AcceptHeight int32
|
||||||
@@ -28,8 +27,8 @@ type HtlcFailResolution struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewFailResolution returns a htlc failure resolution.
|
// NewFailResolution returns a htlc failure resolution.
|
||||||
func NewFailResolution(key channeldb.CircuitKey,
|
func NewFailResolution(key CircuitKey, acceptHeight int32,
|
||||||
acceptHeight int32, outcome FailResolutionResult) *HtlcFailResolution {
|
outcome FailResolutionResult) *HtlcFailResolution {
|
||||||
|
|
||||||
return &HtlcFailResolution{
|
return &HtlcFailResolution{
|
||||||
circuitKey: key,
|
circuitKey: key,
|
||||||
@@ -42,7 +41,7 @@ func NewFailResolution(key channeldb.CircuitKey,
|
|||||||
// resolution for.
|
// resolution for.
|
||||||
//
|
//
|
||||||
// Note: it is part of the HtlcResolution interface.
|
// Note: it is part of the HtlcResolution interface.
|
||||||
func (f *HtlcFailResolution) CircuitKey() channeldb.CircuitKey {
|
func (f *HtlcFailResolution) CircuitKey() CircuitKey {
|
||||||
return f.circuitKey
|
return f.circuitKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +52,7 @@ type HtlcSettleResolution struct {
|
|||||||
Preimage lntypes.Preimage
|
Preimage lntypes.Preimage
|
||||||
|
|
||||||
// circuitKey is the key of the htlc for which we have a resolution.
|
// circuitKey is the key of the htlc for which we have a resolution.
|
||||||
circuitKey channeldb.CircuitKey
|
circuitKey CircuitKey
|
||||||
|
|
||||||
// acceptHeight is the original height at which the htlc was accepted.
|
// acceptHeight is the original height at which the htlc was accepted.
|
||||||
AcceptHeight int32
|
AcceptHeight int32
|
||||||
@@ -64,8 +63,8 @@ type HtlcSettleResolution struct {
|
|||||||
|
|
||||||
// NewSettleResolution returns a htlc resolution which is associated with a
|
// NewSettleResolution returns a htlc resolution which is associated with a
|
||||||
// settle.
|
// settle.
|
||||||
func NewSettleResolution(preimage lntypes.Preimage,
|
func NewSettleResolution(preimage lntypes.Preimage, key CircuitKey,
|
||||||
key channeldb.CircuitKey, acceptHeight int32,
|
acceptHeight int32,
|
||||||
outcome SettleResolutionResult) *HtlcSettleResolution {
|
outcome SettleResolutionResult) *HtlcSettleResolution {
|
||||||
|
|
||||||
return &HtlcSettleResolution{
|
return &HtlcSettleResolution{
|
||||||
@@ -80,7 +79,7 @@ func NewSettleResolution(preimage lntypes.Preimage,
|
|||||||
// resolution for.
|
// resolution for.
|
||||||
//
|
//
|
||||||
// Note: it is part of the HtlcResolution interface.
|
// Note: it is part of the HtlcResolution interface.
|
||||||
func (s *HtlcSettleResolution) CircuitKey() channeldb.CircuitKey {
|
func (s *HtlcSettleResolution) CircuitKey() CircuitKey {
|
||||||
return s.circuitKey
|
return s.circuitKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +91,7 @@ func (s *HtlcSettleResolution) CircuitKey() channeldb.CircuitKey {
|
|||||||
// acceptResolution, a nil resolution should be surfaced.
|
// acceptResolution, a nil resolution should be surfaced.
|
||||||
type htlcAcceptResolution struct {
|
type htlcAcceptResolution struct {
|
||||||
// circuitKey is the key of the htlc for which we have a resolution.
|
// circuitKey is the key of the htlc for which we have a resolution.
|
||||||
circuitKey channeldb.CircuitKey
|
circuitKey CircuitKey
|
||||||
|
|
||||||
// autoRelease signals that the htlc should be automatically released
|
// autoRelease signals that the htlc should be automatically released
|
||||||
// after a timeout.
|
// after a timeout.
|
||||||
@@ -107,7 +106,7 @@ type htlcAcceptResolution struct {
|
|||||||
|
|
||||||
// newAcceptResolution returns a htlc resolution which is associated with a
|
// newAcceptResolution returns a htlc resolution which is associated with a
|
||||||
// htlc accept.
|
// htlc accept.
|
||||||
func newAcceptResolution(key channeldb.CircuitKey,
|
func newAcceptResolution(key CircuitKey,
|
||||||
outcome acceptResolutionResult) *htlcAcceptResolution {
|
outcome acceptResolutionResult) *htlcAcceptResolution {
|
||||||
|
|
||||||
return &htlcAcceptResolution{
|
return &htlcAcceptResolution{
|
||||||
@@ -120,6 +119,6 @@ func newAcceptResolution(key channeldb.CircuitKey,
|
|||||||
// resolution for.
|
// resolution for.
|
||||||
//
|
//
|
||||||
// Note: it is part of the HtlcResolution interface.
|
// Note: it is part of the HtlcResolution interface.
|
||||||
func (a *htlcAcceptResolution) CircuitKey() channeldb.CircuitKey {
|
func (a *htlcAcceptResolution) CircuitKey() CircuitKey {
|
||||||
return a.circuitKey
|
return a.circuitKey
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
package invoices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockChainNotifier struct {
|
||||||
|
chainntnfs.ChainNotifier
|
||||||
|
|
||||||
|
blockChan chan *chainntnfs.BlockEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockNotifier() *mockChainNotifier {
|
||||||
|
return &mockChainNotifier{
|
||||||
|
blockChan: make(chan *chainntnfs.BlockEpoch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterBlockEpochNtfn mocks a block epoch notification, using the mock's
|
||||||
|
// block channel to deliver blocks to the client.
|
||||||
|
func (m *mockChainNotifier) RegisterBlockEpochNtfn(*chainntnfs.BlockEpoch) (
|
||||||
|
*chainntnfs.BlockEpochEvent, error) {
|
||||||
|
|
||||||
|
return &chainntnfs.BlockEpochEvent{
|
||||||
|
Epochs: m.blockChan,
|
||||||
|
Cancel: func() {},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCurrentHeight = int32(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
testTime = time.Date(2018, time.February, 2, 14, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
testInvoicePreimage = lntypes.Preimage{1}
|
||||||
|
|
||||||
|
testPrivKeyBytes, _ = hex.DecodeString(
|
||||||
|
"e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2d" +
|
||||||
|
"b734",
|
||||||
|
)
|
||||||
|
|
||||||
|
testPrivKey, _ = btcec.PrivKeyFromBytes(testPrivKeyBytes)
|
||||||
|
|
||||||
|
testInvoiceDescription = "coffee"
|
||||||
|
|
||||||
|
testInvoiceAmount = lnwire.MilliSatoshi(100000) //nolint:gomnd
|
||||||
|
|
||||||
|
testNetParams = &chaincfg.MainNetParams
|
||||||
|
|
||||||
|
testMessageSigner = zpay32.MessageSigner{
|
||||||
|
SignCompact: func(msg []byte) ([]byte, error) {
|
||||||
|
hash := chainhash.HashB(msg)
|
||||||
|
sig, err := ecdsa.SignCompact(testPrivKey, hash, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't sign the "+
|
||||||
|
"message: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testFeatures = lnwire.NewFeatureVector(
|
||||||
|
nil, lnwire.Features,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
|
||||||
|
timestamp time.Time, expiry time.Duration) *Invoice {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if expiry == 0 {
|
||||||
|
expiry = time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
var payAddr [32]byte
|
||||||
|
if _, err := rand.Read(payAddr[:]); err != nil {
|
||||||
|
t.Fatalf("unable to generate payment addr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawInvoice, err := zpay32.NewInvoice(
|
||||||
|
testNetParams,
|
||||||
|
preimage.Hash(),
|
||||||
|
timestamp,
|
||||||
|
zpay32.Amount(testInvoiceAmount),
|
||||||
|
zpay32.Description(testInvoiceDescription),
|
||||||
|
zpay32.Expiry(expiry),
|
||||||
|
zpay32.PaymentAddr(payAddr),
|
||||||
|
)
|
||||||
|
require.NoError(t, err, "Error while creating new invoice")
|
||||||
|
|
||||||
|
paymentRequest, err := rawInvoice.Encode(testMessageSigner)
|
||||||
|
|
||||||
|
require.NoError(t, err, "Error while encoding payment request")
|
||||||
|
|
||||||
|
return &Invoice{
|
||||||
|
Terms: ContractTerm{
|
||||||
|
PaymentPreimage: &preimage,
|
||||||
|
PaymentAddr: payAddr,
|
||||||
|
Value: testInvoiceAmount,
|
||||||
|
Expiry: expiry,
|
||||||
|
Features: testFeatures,
|
||||||
|
},
|
||||||
|
PaymentRequest: []byte(paymentRequest),
|
||||||
|
CreationDate: timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoiceExpiryTestData simply holds generated expired and pending invoices.
|
||||||
|
type invoiceExpiryTestData struct {
|
||||||
|
expiredInvoices map[lntypes.Hash]*Invoice
|
||||||
|
pendingInvoices map[lntypes.Hash]*Invoice
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateInvoiceExpiryTestData generates the specified number of fake expired
|
||||||
|
// and pending invoices anchored to the passed now timestamp.
|
||||||
|
func generateInvoiceExpiryTestData(
|
||||||
|
t *testing.T, now time.Time,
|
||||||
|
offset, numExpired, numPending int) invoiceExpiryTestData {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var testData invoiceExpiryTestData
|
||||||
|
|
||||||
|
testData.expiredInvoices = make(map[lntypes.Hash]*Invoice)
|
||||||
|
testData.pendingInvoices = make(map[lntypes.Hash]*Invoice)
|
||||||
|
|
||||||
|
expiredCreationDate := now.Add(-24 * time.Hour)
|
||||||
|
|
||||||
|
for i := 1; i <= numExpired; i++ {
|
||||||
|
var preimage lntypes.Preimage
|
||||||
|
binary.BigEndian.PutUint32(preimage[:4], uint32(offset+i))
|
||||||
|
duration := (i + offset) % 24 //nolint:gomnd
|
||||||
|
expiry := time.Duration(duration) * time.Hour
|
||||||
|
invoice := newTestInvoice(
|
||||||
|
t, preimage, expiredCreationDate, expiry,
|
||||||
|
)
|
||||||
|
testData.expiredInvoices[preimage.Hash()] = invoice
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i <= numPending; i++ {
|
||||||
|
var preimage lntypes.Preimage
|
||||||
|
binary.BigEndian.PutUint32(preimage[4:], uint32(offset+i))
|
||||||
|
duration := (i + offset) % 24 //nolint:gomnd
|
||||||
|
expiry := time.Duration(duration) * time.Hour
|
||||||
|
invoice := newTestInvoice(t, preimage, now, expiry)
|
||||||
|
testData.pendingInvoices[preimage.Hash()] = invoice
|
||||||
|
}
|
||||||
|
|
||||||
|
return testData
|
||||||
|
}
|
||||||
|
|
||||||
|
type hodlExpiryTest struct {
|
||||||
|
hash lntypes.Hash
|
||||||
|
state ContractState
|
||||||
|
stateLock sync.Mutex
|
||||||
|
mockNotifier *mockChainNotifier
|
||||||
|
mockClock *clock.TestClock
|
||||||
|
cancelChan chan lntypes.Hash
|
||||||
|
watcher *InvoiceExpiryWatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hodlExpiryTest) setState(state ContractState) {
|
||||||
|
h.stateLock.Lock()
|
||||||
|
defer h.stateLock.Unlock()
|
||||||
|
|
||||||
|
h.state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hodlExpiryTest) announceBlock(t *testing.T, height uint32) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case h.mockNotifier.blockChan <- &chainntnfs.BlockEpoch{
|
||||||
|
Height: int32(height),
|
||||||
|
}:
|
||||||
|
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatalf("block %v not consumed", height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hodlExpiryTest) assertCanceled(t *testing.T, expected lntypes.Hash) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case actual := <-h.cancelChan:
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatalf("invoice: %v not canceled", h.hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupHodlExpiry creates a hodl invoice in our expiry watcher and runs an
|
||||||
|
// arbitrary update function which advances the invoices's state.
|
||||||
|
func setupHodlExpiry(t *testing.T, creationDate time.Time,
|
||||||
|
expiry time.Duration, heightDelta uint32,
|
||||||
|
startState ContractState,
|
||||||
|
startHtlcs []*InvoiceHTLC) *hodlExpiryTest {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
mockNotifier := newMockNotifier()
|
||||||
|
mockClock := clock.NewTestClock(testTime)
|
||||||
|
|
||||||
|
test := &hodlExpiryTest{
|
||||||
|
state: startState,
|
||||||
|
watcher: NewInvoiceExpiryWatcher(
|
||||||
|
mockClock, heightDelta, uint32(testCurrentHeight), nil,
|
||||||
|
mockNotifier,
|
||||||
|
),
|
||||||
|
cancelChan: make(chan lntypes.Hash),
|
||||||
|
mockNotifier: mockNotifier,
|
||||||
|
mockClock: mockClock,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use an unbuffered channel to block on cancel calls so that the test
|
||||||
|
// does not exit before we've processed all the invoices we expect.
|
||||||
|
cancelImpl := func(paymentHash lntypes.Hash, force bool) error {
|
||||||
|
test.stateLock.Lock()
|
||||||
|
currentState := test.state
|
||||||
|
test.stateLock.Unlock()
|
||||||
|
|
||||||
|
if currentState != ContractOpen && !force {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case test.cancelChan <- paymentHash:
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, test.watcher.Start(cancelImpl))
|
||||||
|
|
||||||
|
// We set preimage and hash so that we can use our existing test
|
||||||
|
// helpers. In practice we would only have the hash, but this does not
|
||||||
|
// affect what we're testing at all.
|
||||||
|
preimage := lntypes.Preimage{1}
|
||||||
|
test.hash = preimage.Hash()
|
||||||
|
|
||||||
|
invoice := newTestInvoice(t, preimage, creationDate, expiry)
|
||||||
|
invoice.State = startState
|
||||||
|
invoice.HodlInvoice = true
|
||||||
|
invoice.Htlcs = make(map[CircuitKey]*InvoiceHTLC)
|
||||||
|
|
||||||
|
// If we have any htlcs, add them with unique circult keys.
|
||||||
|
for i, htlc := range startHtlcs {
|
||||||
|
key := CircuitKey{
|
||||||
|
HtlcID: uint64(i),
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.Htlcs[key] = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an expiry entry for our invoice in its starting state. This
|
||||||
|
// mimics adding invoices to the watcher on start.
|
||||||
|
entry := makeInvoiceExpiry(test.hash, invoice)
|
||||||
|
test.watcher.AddInvoices(entry)
|
||||||
|
|
||||||
|
return test
|
||||||
|
}
|
||||||
+85
-118
@@ -1,4 +1,4 @@
|
|||||||
package invoices
|
package invoices_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
|
invpkg "github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
@@ -54,6 +55,29 @@ func (p *mockPayload) Metadata() []byte {
|
|||||||
return p.metadata
|
return p.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockChainNotifier struct {
|
||||||
|
chainntnfs.ChainNotifier
|
||||||
|
|
||||||
|
blockChan chan *chainntnfs.BlockEpoch
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockNotifier() *mockChainNotifier {
|
||||||
|
return &mockChainNotifier{
|
||||||
|
blockChan: make(chan *chainntnfs.BlockEpoch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterBlockEpochNtfn mocks a block epoch notification, using the mock's
|
||||||
|
// block channel to deliver blocks to the client.
|
||||||
|
func (m *mockChainNotifier) RegisterBlockEpochNtfn(*chainntnfs.BlockEpoch) (
|
||||||
|
*chainntnfs.BlockEpochEvent, error) {
|
||||||
|
|
||||||
|
return &chainntnfs.BlockEpochEvent{
|
||||||
|
Epochs: m.blockChan,
|
||||||
|
Cancel: func() {},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testHtlcExpiry = uint32(5)
|
testHtlcExpiry = uint32(5)
|
||||||
|
|
||||||
@@ -91,7 +115,8 @@ var (
|
|||||||
hash := chainhash.HashB(msg)
|
hash := chainhash.HashB(msg)
|
||||||
sig, err := ecdsa.SignCompact(testPrivKey, hash, true)
|
sig, err := ecdsa.SignCompact(testPrivKey, hash, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't sign the message: %v", err)
|
return nil, fmt.Errorf("can't sign the "+
|
||||||
|
"message: %v", err)
|
||||||
}
|
}
|
||||||
return sig, nil
|
return sig, nil
|
||||||
},
|
},
|
||||||
@@ -108,8 +133,8 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
testInvoiceAmt = lnwire.MilliSatoshi(100000)
|
testInvoiceAmt = lnwire.MilliSatoshi(100000)
|
||||||
testInvoice = &channeldb.Invoice{
|
testInvoice = &invpkg.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invpkg.ContractTerm{
|
||||||
PaymentPreimage: &testInvoicePreimage,
|
PaymentPreimage: &testInvoicePreimage,
|
||||||
Value: testInvoiceAmt,
|
Value: testInvoiceAmt,
|
||||||
Expiry: time.Hour,
|
Expiry: time.Hour,
|
||||||
@@ -118,8 +143,8 @@ var (
|
|||||||
CreationDate: testInvoiceCreationDate,
|
CreationDate: testInvoiceCreationDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
testPayAddrReqInvoice = &channeldb.Invoice{
|
testPayAddrReqInvoice = &invpkg.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invpkg.ContractTerm{
|
||||||
PaymentPreimage: &testInvoicePreimage,
|
PaymentPreimage: &testInvoicePreimage,
|
||||||
Value: testInvoiceAmt,
|
Value: testInvoiceAmt,
|
||||||
Expiry: time.Hour,
|
Expiry: time.Hour,
|
||||||
@@ -134,8 +159,8 @@ var (
|
|||||||
CreationDate: testInvoiceCreationDate,
|
CreationDate: testInvoiceCreationDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
testPayAddrOptionalInvoice = &channeldb.Invoice{
|
testPayAddrOptionalInvoice = &invpkg.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invpkg.ContractTerm{
|
||||||
PaymentPreimage: &testInvoicePreimage,
|
PaymentPreimage: &testInvoicePreimage,
|
||||||
Value: testInvoiceAmt,
|
Value: testInvoiceAmt,
|
||||||
Expiry: time.Hour,
|
Expiry: time.Hour,
|
||||||
@@ -150,8 +175,8 @@ var (
|
|||||||
CreationDate: testInvoiceCreationDate,
|
CreationDate: testInvoiceCreationDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
testHodlInvoice = &channeldb.Invoice{
|
testHodlInvoice = &invpkg.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invpkg.ContractTerm{
|
||||||
Value: testInvoiceAmt,
|
Value: testInvoiceAmt,
|
||||||
Expiry: time.Hour,
|
Expiry: time.Hour,
|
||||||
Features: testFeatures,
|
Features: testFeatures,
|
||||||
@@ -162,6 +187,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func newTestChannelDB(t *testing.T, clock clock.Clock) (*channeldb.DB, error) {
|
func newTestChannelDB(t *testing.T, clock clock.Clock) (*channeldb.DB, error) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
// Create channeldb for the first time.
|
// Create channeldb for the first time.
|
||||||
cdb, err := channeldb.Open(
|
cdb, err := channeldb.Open(
|
||||||
t.TempDir(), channeldb.OptionClock(clock),
|
t.TempDir(), channeldb.OptionClock(clock),
|
||||||
@@ -178,35 +205,47 @@ func newTestChannelDB(t *testing.T, clock clock.Clock) (*channeldb.DB, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testContext struct {
|
type testContext struct {
|
||||||
cdb *channeldb.DB
|
idb *channeldb.DB
|
||||||
registry *InvoiceRegistry
|
registry *invpkg.InvoiceRegistry
|
||||||
notifier *mockChainNotifier
|
notifier *mockChainNotifier
|
||||||
clock *clock.TestClock
|
clock *clock.TestClock
|
||||||
|
|
||||||
t *testing.T
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestContext(t *testing.T) *testContext {
|
func defaultRegistryConfig() invpkg.RegistryConfig {
|
||||||
|
return invpkg.RegistryConfig{
|
||||||
|
FinalCltvRejectDelta: testFinalCltvRejectDelta,
|
||||||
|
HtlcHoldDuration: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestContext(t *testing.T,
|
||||||
|
registryCfg *invpkg.RegistryConfig) *testContext {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
clock := clock.NewTestClock(testTime)
|
clock := clock.NewTestClock(testTime)
|
||||||
|
|
||||||
cdb, err := newTestChannelDB(t, clock)
|
idb, err := newTestChannelDB(t, clock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier := newMockNotifier()
|
notifier := newMockNotifier()
|
||||||
|
|
||||||
expiryWatcher := NewInvoiceExpiryWatcher(
|
expiryWatcher := invpkg.NewInvoiceExpiryWatcher(
|
||||||
clock, 0, uint32(testCurrentHeight), nil, notifier,
|
clock, 0, uint32(testCurrentHeight), nil, notifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Instantiate and start the invoice ctx.registry.
|
cfg := defaultRegistryConfig()
|
||||||
cfg := RegistryConfig{
|
if registryCfg != nil {
|
||||||
FinalCltvRejectDelta: testFinalCltvRejectDelta,
|
cfg = *registryCfg
|
||||||
HtlcHoldDuration: 30 * time.Second,
|
|
||||||
Clock: clock,
|
|
||||||
}
|
}
|
||||||
registry := NewRegistry(cdb, expiryWatcher, &cfg)
|
cfg.Clock = clock
|
||||||
|
|
||||||
|
// Instantiate and start the invoice ctx.registry.
|
||||||
|
registry := invpkg.NewRegistry(idb, expiryWatcher, &cfg)
|
||||||
|
|
||||||
err = registry.Start()
|
err = registry.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -217,7 +256,7 @@ func newTestContext(t *testing.T) *testContext {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ctx := testContext{
|
ctx := testContext{
|
||||||
cdb: cdb,
|
idb: idb,
|
||||||
registry: registry,
|
registry: registry,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
@@ -227,8 +266,8 @@ func newTestContext(t *testing.T) *testContext {
|
|||||||
return &ctx
|
return &ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCircuitKey(htlcID uint64) channeldb.CircuitKey {
|
func getCircuitKey(htlcID uint64) invpkg.CircuitKey {
|
||||||
return channeldb.CircuitKey{
|
return invpkg.CircuitKey{
|
||||||
ChanID: lnwire.ShortChannelID{
|
ChanID: lnwire.ShortChannelID{
|
||||||
BlockHeight: 1, TxIndex: 2, TxPosition: 3,
|
BlockHeight: 1, TxIndex: 2, TxPosition: 3,
|
||||||
},
|
},
|
||||||
@@ -237,7 +276,7 @@ func getCircuitKey(htlcID uint64) channeldb.CircuitKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
|
func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
|
||||||
timestamp time.Time, expiry time.Duration) *channeldb.Invoice {
|
timestamp time.Time, expiry time.Duration) *invpkg.Invoice {
|
||||||
|
|
||||||
if expiry == 0 {
|
if expiry == 0 {
|
||||||
expiry = time.Hour
|
expiry = time.Hour
|
||||||
@@ -263,8 +302,8 @@ func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
|
|||||||
|
|
||||||
require.NoError(t, err, "Error while encoding payment request")
|
require.NoError(t, err, "Error while encoding payment request")
|
||||||
|
|
||||||
return &channeldb.Invoice{
|
return &invpkg.Invoice{
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invpkg.ContractTerm{
|
||||||
PaymentPreimage: &preimage,
|
PaymentPreimage: &preimage,
|
||||||
PaymentAddr: payAddr,
|
PaymentAddr: payAddr,
|
||||||
Value: testInvoiceAmount,
|
Value: testInvoiceAmount,
|
||||||
@@ -285,7 +324,8 @@ func timeout() func() {
|
|||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("error writing to std out after timeout: %v", err))
|
panic(fmt.Sprintf("error writing to std out "+
|
||||||
|
"after timeout: %v", err))
|
||||||
}
|
}
|
||||||
panic("timeout")
|
panic("timeout")
|
||||||
case <-done:
|
case <-done:
|
||||||
@@ -299,8 +339,8 @@ func timeout() func() {
|
|||||||
|
|
||||||
// invoiceExpiryTestData simply holds generated expired and pending invoices.
|
// invoiceExpiryTestData simply holds generated expired and pending invoices.
|
||||||
type invoiceExpiryTestData struct {
|
type invoiceExpiryTestData struct {
|
||||||
expiredInvoices map[lntypes.Hash]*channeldb.Invoice
|
expiredInvoices map[lntypes.Hash]*invpkg.Invoice
|
||||||
pendingInvoices map[lntypes.Hash]*channeldb.Invoice
|
pendingInvoices map[lntypes.Hash]*invpkg.Invoice
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateInvoiceExpiryTestData generates the specified number of fake expired
|
// generateInvoiceExpiryTestData generates the specified number of fake expired
|
||||||
@@ -311,8 +351,8 @@ func generateInvoiceExpiryTestData(
|
|||||||
|
|
||||||
var testData invoiceExpiryTestData
|
var testData invoiceExpiryTestData
|
||||||
|
|
||||||
testData.expiredInvoices = make(map[lntypes.Hash]*channeldb.Invoice)
|
testData.expiredInvoices = make(map[lntypes.Hash]*invpkg.Invoice)
|
||||||
testData.pendingInvoices = make(map[lntypes.Hash]*channeldb.Invoice)
|
testData.pendingInvoices = make(map[lntypes.Hash]*invpkg.Invoice)
|
||||||
|
|
||||||
expiredCreationDate := now.Add(-24 * time.Hour)
|
expiredCreationDate := now.Add(-24 * time.Hour)
|
||||||
|
|
||||||
@@ -320,7 +360,9 @@ func generateInvoiceExpiryTestData(
|
|||||||
var preimage lntypes.Preimage
|
var preimage lntypes.Preimage
|
||||||
binary.BigEndian.PutUint32(preimage[:4], uint32(offset+i))
|
binary.BigEndian.PutUint32(preimage[:4], uint32(offset+i))
|
||||||
expiry := time.Duration((i+offset)%24) * time.Hour
|
expiry := time.Duration((i+offset)%24) * time.Hour
|
||||||
invoice := newTestInvoice(t, preimage, expiredCreationDate, expiry)
|
invoice := newTestInvoice(
|
||||||
|
t, preimage, expiredCreationDate, expiry,
|
||||||
|
)
|
||||||
testData.expiredInvoices[preimage.Hash()] = invoice
|
testData.expiredInvoices[preimage.Hash()] = invoice
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,12 +380,12 @@ func generateInvoiceExpiryTestData(
|
|||||||
// checkSettleResolution asserts the resolution is a settle with the correct
|
// checkSettleResolution asserts the resolution is a settle with the correct
|
||||||
// preimage. If successful, the HtlcSettleResolution is returned in case further
|
// preimage. If successful, the HtlcSettleResolution is returned in case further
|
||||||
// checks are desired.
|
// checks are desired.
|
||||||
func checkSettleResolution(t *testing.T, res HtlcResolution,
|
func checkSettleResolution(t *testing.T, res invpkg.HtlcResolution,
|
||||||
expPreimage lntypes.Preimage) *HtlcSettleResolution {
|
expPreimage lntypes.Preimage) *invpkg.HtlcSettleResolution {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
settleResolution, ok := res.(*HtlcSettleResolution)
|
settleResolution, ok := res.(*invpkg.HtlcSettleResolution)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, expPreimage, settleResolution.Preimage)
|
require.Equal(t, expPreimage, settleResolution.Preimage)
|
||||||
|
|
||||||
@@ -353,11 +395,11 @@ func checkSettleResolution(t *testing.T, res HtlcResolution,
|
|||||||
// checkFailResolution asserts the resolution is a fail with the correct reason.
|
// checkFailResolution asserts the resolution is a fail with the correct reason.
|
||||||
// If successful, the HtlcFailResolution is returned in case further checks are
|
// If successful, the HtlcFailResolution is returned in case further checks are
|
||||||
// desired.
|
// desired.
|
||||||
func checkFailResolution(t *testing.T, res HtlcResolution,
|
func checkFailResolution(t *testing.T, res invpkg.HtlcResolution,
|
||||||
expOutcome FailResolutionResult) *HtlcFailResolution {
|
expOutcome invpkg.FailResolutionResult) *invpkg.HtlcFailResolution {
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
failResolution, ok := res.(*HtlcFailResolution)
|
failResolution, ok := res.(*invpkg.HtlcFailResolution)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, expOutcome, failResolution.Outcome)
|
require.Equal(t, expOutcome, failResolution.Outcome)
|
||||||
|
|
||||||
@@ -366,22 +408,17 @@ func checkFailResolution(t *testing.T, res HtlcResolution,
|
|||||||
|
|
||||||
type hodlExpiryTest struct {
|
type hodlExpiryTest struct {
|
||||||
hash lntypes.Hash
|
hash lntypes.Hash
|
||||||
state channeldb.ContractState
|
state invpkg.ContractState
|
||||||
stateLock sync.Mutex
|
stateLock sync.Mutex
|
||||||
mockNotifier *mockChainNotifier
|
mockNotifier *mockChainNotifier
|
||||||
mockClock *clock.TestClock
|
mockClock *clock.TestClock
|
||||||
cancelChan chan lntypes.Hash
|
cancelChan chan lntypes.Hash
|
||||||
watcher *InvoiceExpiryWatcher
|
watcher *invpkg.InvoiceExpiryWatcher
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hodlExpiryTest) setState(state channeldb.ContractState) {
|
|
||||||
h.stateLock.Lock()
|
|
||||||
defer h.stateLock.Unlock()
|
|
||||||
|
|
||||||
h.state = state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hodlExpiryTest) announceBlock(t *testing.T, height uint32) {
|
func (h *hodlExpiryTest) announceBlock(t *testing.T, height uint32) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case h.mockNotifier.blockChan <- &chainntnfs.BlockEpoch{
|
case h.mockNotifier.blockChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: int32(height),
|
Height: int32(height),
|
||||||
@@ -401,73 +438,3 @@ func (h *hodlExpiryTest) assertCanceled(t *testing.T, expected lntypes.Hash) {
|
|||||||
t.Fatalf("invoice: %v not canceled", h.hash)
|
t.Fatalf("invoice: %v not canceled", h.hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupHodlExpiry creates a hodl invoice in our expiry watcher and runs an
|
|
||||||
// arbitrary update function which advances the invoices's state.
|
|
||||||
func setupHodlExpiry(t *testing.T, creationDate time.Time,
|
|
||||||
expiry time.Duration, heightDelta uint32,
|
|
||||||
startState channeldb.ContractState,
|
|
||||||
startHtlcs []*channeldb.InvoiceHTLC) *hodlExpiryTest {
|
|
||||||
|
|
||||||
mockNotifier := newMockNotifier()
|
|
||||||
mockClock := clock.NewTestClock(testTime)
|
|
||||||
|
|
||||||
test := &hodlExpiryTest{
|
|
||||||
state: startState,
|
|
||||||
watcher: NewInvoiceExpiryWatcher(
|
|
||||||
mockClock, heightDelta, uint32(testCurrentHeight), nil,
|
|
||||||
mockNotifier,
|
|
||||||
),
|
|
||||||
cancelChan: make(chan lntypes.Hash),
|
|
||||||
mockNotifier: mockNotifier,
|
|
||||||
mockClock: mockClock,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use an unbuffered channel to block on cancel calls so that the test
|
|
||||||
// does not exit before we've processed all the invoices we expect.
|
|
||||||
cancelImpl := func(paymentHash lntypes.Hash, force bool) error {
|
|
||||||
test.stateLock.Lock()
|
|
||||||
currentState := test.state
|
|
||||||
test.stateLock.Unlock()
|
|
||||||
|
|
||||||
if currentState != channeldb.ContractOpen && !force {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case test.cancelChan <- paymentHash:
|
|
||||||
case <-time.After(testTimeout):
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, test.watcher.Start(cancelImpl))
|
|
||||||
|
|
||||||
// We set preimage and hash so that we can use our existing test
|
|
||||||
// helpers. In practice we would only have the hash, but this does not
|
|
||||||
// affect what we're testing at all.
|
|
||||||
preimage := lntypes.Preimage{1}
|
|
||||||
test.hash = preimage.Hash()
|
|
||||||
|
|
||||||
invoice := newTestInvoice(t, preimage, creationDate, expiry)
|
|
||||||
invoice.State = startState
|
|
||||||
invoice.HodlInvoice = true
|
|
||||||
invoice.Htlcs = make(map[channeldb.CircuitKey]*channeldb.InvoiceHTLC)
|
|
||||||
|
|
||||||
// If we have any htlcs, add them with unique circult keys.
|
|
||||||
for i, htlc := range startHtlcs {
|
|
||||||
key := channeldb.CircuitKey{
|
|
||||||
HtlcID: uint64(i),
|
|
||||||
}
|
|
||||||
|
|
||||||
invoice.Htlcs[key] = htlc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an expiry entry for our invoice in its starting state. This
|
|
||||||
// mimics adding invoices to the watcher on start.
|
|
||||||
entry := makeInvoiceExpiry(test.hash, invoice)
|
|
||||||
test.watcher.AddInvoices(entry)
|
|
||||||
|
|
||||||
return test
|
|
||||||
}
|
|
||||||
+42
-42
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/amp"
|
"github.com/lightningnetwork/lnd/amp"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
@@ -15,7 +14,7 @@ import (
|
|||||||
// update to be carried out.
|
// update to be carried out.
|
||||||
type invoiceUpdateCtx struct {
|
type invoiceUpdateCtx struct {
|
||||||
hash lntypes.Hash
|
hash lntypes.Hash
|
||||||
circuitKey channeldb.CircuitKey
|
circuitKey CircuitKey
|
||||||
amtPaid lnwire.MilliSatoshi
|
amtPaid lnwire.MilliSatoshi
|
||||||
expiry uint32
|
expiry uint32
|
||||||
currentHeight int32
|
currentHeight int32
|
||||||
@@ -28,16 +27,18 @@ type invoiceUpdateCtx struct {
|
|||||||
|
|
||||||
// invoiceRef returns an identifier that can be used to lookup or update the
|
// invoiceRef returns an identifier that can be used to lookup or update the
|
||||||
// invoice this HTLC is targeting.
|
// invoice this HTLC is targeting.
|
||||||
func (i *invoiceUpdateCtx) invoiceRef() channeldb.InvoiceRef {
|
func (i *invoiceUpdateCtx) invoiceRef() InvoiceRef {
|
||||||
switch {
|
switch {
|
||||||
case i.amp != nil && i.mpp != nil:
|
case i.amp != nil && i.mpp != nil:
|
||||||
payAddr := i.mpp.PaymentAddr()
|
payAddr := i.mpp.PaymentAddr()
|
||||||
return channeldb.InvoiceRefByAddr(payAddr)
|
return InvoiceRefByAddr(payAddr)
|
||||||
|
|
||||||
case i.mpp != nil:
|
case i.mpp != nil:
|
||||||
payAddr := i.mpp.PaymentAddr()
|
payAddr := i.mpp.PaymentAddr()
|
||||||
return channeldb.InvoiceRefByHashAndAddr(i.hash, payAddr)
|
return InvoiceRefByHashAndAddr(i.hash, payAddr)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return channeldb.InvoiceRefByHash(i.hash)
|
return InvoiceRefByHash(i.hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,20 +95,20 @@ func (i invoiceUpdateCtx) acceptRes(outcome acceptResolutionResult) *htlcAcceptR
|
|||||||
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
||||||
// settlement logic. It returns a hltc resolution that indicates what the
|
// settlement logic. It returns a hltc resolution that indicates what the
|
||||||
// outcome of the update was.
|
// outcome of the update was.
|
||||||
func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
func updateInvoice(ctx *invoiceUpdateCtx, inv *Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, HtlcResolution, error) {
|
*InvoiceUpdateDesc, HtlcResolution, error) {
|
||||||
|
|
||||||
// Don't update the invoice when this is a replayed htlc.
|
// Don't update the invoice when this is a replayed htlc.
|
||||||
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
||||||
if ok {
|
if ok {
|
||||||
switch htlc.State {
|
switch htlc.State {
|
||||||
case channeldb.HtlcStateCanceled:
|
case HtlcStateCanceled:
|
||||||
return nil, ctx.failRes(ResultReplayToCanceled), nil
|
return nil, ctx.failRes(ResultReplayToCanceled), nil
|
||||||
|
|
||||||
case channeldb.HtlcStateAccepted:
|
case HtlcStateAccepted:
|
||||||
return nil, ctx.acceptRes(resultReplayToAccepted), nil
|
return nil, ctx.acceptRes(resultReplayToAccepted), nil
|
||||||
|
|
||||||
case channeldb.HtlcStateSettled:
|
case HtlcStateSettled:
|
||||||
pre := inv.Terms.PaymentPreimage
|
pre := inv.Terms.PaymentPreimage
|
||||||
|
|
||||||
// Terms.PaymentPreimage will be nil for AMP invoices.
|
// Terms.PaymentPreimage will be nil for AMP invoices.
|
||||||
@@ -138,8 +139,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
|
|
||||||
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
||||||
// settlement logic for mpp payments.
|
// settlement logic for mpp payments.
|
||||||
func updateMpp(ctx *invoiceUpdateCtx,
|
func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
|
||||||
inv *channeldb.Invoice) (*channeldb.InvoiceUpdateDesc,
|
|
||||||
HtlcResolution, error) {
|
HtlcResolution, error) {
|
||||||
|
|
||||||
// Reject HTLCs to AMP invoices if they are missing an AMP payload, and
|
// Reject HTLCs to AMP invoices if they are missing an AMP payload, and
|
||||||
@@ -159,7 +159,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
setID := ctx.setID()
|
setID := ctx.setID()
|
||||||
|
|
||||||
// Start building the accept descriptor.
|
// Start building the accept descriptor.
|
||||||
acceptDesc := &channeldb.HtlcAcceptDesc{
|
acceptDesc := &HtlcAcceptDesc{
|
||||||
Amt: ctx.amtPaid,
|
Amt: ctx.amtPaid,
|
||||||
Expiry: ctx.expiry,
|
Expiry: ctx.expiry,
|
||||||
AcceptHeight: ctx.currentHeight,
|
AcceptHeight: ctx.currentHeight,
|
||||||
@@ -168,7 +168,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.amp != nil {
|
if ctx.amp != nil {
|
||||||
acceptDesc.AMP = &channeldb.InvoiceHtlcAMPData{
|
acceptDesc.AMP = &InvoiceHtlcAMPData{
|
||||||
Record: *ctx.amp,
|
Record: *ctx.amp,
|
||||||
Hash: ctx.hash,
|
Hash: ctx.hash,
|
||||||
Preimage: nil,
|
Preimage: nil,
|
||||||
@@ -179,7 +179,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
// non-mpp payments that are accepted even after the invoice is settled.
|
// non-mpp payments that are accepted even after the invoice is settled.
|
||||||
// Because non-mpp payments don't have a payment address, this is needed
|
// Because non-mpp payments don't have a payment address, this is needed
|
||||||
// to thwart probing.
|
// to thwart probing.
|
||||||
if inv.State != channeldb.ContractOpen {
|
if inv.State != ContractOpen {
|
||||||
return nil, ctx.failRes(ResultInvoiceNotOpen), nil
|
return nil, ctx.failRes(ResultInvoiceNotOpen), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
|
return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
htlcSet := inv.HTLCSet(setID, channeldb.HtlcStateAccepted)
|
htlcSet := inv.HTLCSet(setID, HtlcStateAccepted)
|
||||||
|
|
||||||
// Check whether total amt matches other htlcs in the set.
|
// Check whether total amt matches other htlcs in the set.
|
||||||
var newSetTotal lnwire.MilliSatoshi
|
var newSetTotal lnwire.MilliSatoshi
|
||||||
@@ -228,16 +228,16 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if setID != nil && *setID == channeldb.BlankPayAddr {
|
if setID != nil && *setID == BlankPayAddr {
|
||||||
return nil, ctx.failRes(ResultAmpError), nil
|
return nil, ctx.failRes(ResultAmpError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record HTLC in the invoice database.
|
// Record HTLC in the invoice database.
|
||||||
newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{
|
newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
|
||||||
ctx.circuitKey: acceptDesc,
|
ctx.circuitKey: acceptDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
update := channeldb.InvoiceUpdateDesc{
|
update := InvoiceUpdateDesc{
|
||||||
AddHtlcs: newHtlcs,
|
AddHtlcs: newHtlcs,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,23 +250,23 @@ 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.
|
||||||
if inv.HodlInvoice {
|
if inv.HodlInvoice {
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: ContractAccepted,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
}
|
}
|
||||||
return &update, ctx.acceptRes(resultAccepted), nil
|
return &update, ctx.acceptRes(resultAccepted), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
htlcPreimages map[channeldb.CircuitKey]lntypes.Preimage
|
htlcPreimages map[CircuitKey]lntypes.Preimage
|
||||||
htlcPreimage lntypes.Preimage
|
htlcPreimage lntypes.Preimage
|
||||||
)
|
)
|
||||||
if ctx.amp != nil {
|
if ctx.amp != nil {
|
||||||
var failRes *HtlcFailResolution
|
var failRes *HtlcFailResolution
|
||||||
htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
|
htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
|
||||||
if failRes != nil {
|
if failRes != nil {
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractCanceled,
|
NewState: ContractCanceled,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
}
|
}
|
||||||
return &update, failRes, nil
|
return &update, failRes, nil
|
||||||
@@ -279,8 +279,8 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
htlcPreimage = *inv.Terms.PaymentPreimage
|
htlcPreimage = *inv.Terms.PaymentPreimage
|
||||||
}
|
}
|
||||||
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractSettled,
|
NewState: ContractSettled,
|
||||||
Preimage: inv.Terms.PaymentPreimage,
|
Preimage: inv.Terms.PaymentPreimage,
|
||||||
HTLCPreimages: htlcPreimages,
|
HTLCPreimages: htlcPreimages,
|
||||||
SetID: setID,
|
SetID: setID,
|
||||||
@@ -290,10 +290,10 @@ func updateMpp(ctx *invoiceUpdateCtx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTLCSet is a map of CircuitKey to InvoiceHTLC.
|
// HTLCSet is a map of CircuitKey to InvoiceHTLC.
|
||||||
type HTLCSet = map[channeldb.CircuitKey]*channeldb.InvoiceHTLC
|
type HTLCSet = map[CircuitKey]*InvoiceHTLC
|
||||||
|
|
||||||
// HTLCPreimages is a map of CircuitKey to preimage.
|
// HTLCPreimages is a map of CircuitKey to preimage.
|
||||||
type HTLCPreimages = map[channeldb.CircuitKey]lntypes.Preimage
|
type HTLCPreimages = map[CircuitKey]lntypes.Preimage
|
||||||
|
|
||||||
// reconstructAMPPreimages reconstructs the root seed for an AMP HTLC set and
|
// reconstructAMPPreimages reconstructs the root seed for an AMP HTLC set and
|
||||||
// verifies that all derived child hashes match the payment hashes of the HTLCs
|
// verifies that all derived child hashes match the payment hashes of the HTLCs
|
||||||
@@ -316,7 +316,7 @@ func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
|
|||||||
|
|
||||||
// Next, construct an index mapping the position in childDescs to a
|
// Next, construct an index mapping the position in childDescs to a
|
||||||
// circuit key for all preexisting HTLCs.
|
// circuit key for all preexisting HTLCs.
|
||||||
indexToCircuitKey := make(map[int]channeldb.CircuitKey)
|
indexToCircuitKey := make(map[int]CircuitKey)
|
||||||
|
|
||||||
// Add the child descriptor for each HTLC in the HTLC set, recording
|
// Add the child descriptor for each HTLC in the HTLC set, recording
|
||||||
// it's position within the slice.
|
// it's position within the slice.
|
||||||
@@ -350,7 +350,7 @@ func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
|
|||||||
// Finally, construct the map of learned preimages indexed by circuit
|
// Finally, construct the map of learned preimages indexed by circuit
|
||||||
// key, so that they can be persisted along with each HTLC when updating
|
// key, so that they can be persisted along with each HTLC when updating
|
||||||
// the invoice.
|
// the invoice.
|
||||||
htlcPreimages := make(map[channeldb.CircuitKey]lntypes.Preimage)
|
htlcPreimages := make(map[CircuitKey]lntypes.Preimage)
|
||||||
htlcPreimages[ctx.circuitKey] = children[0].Preimage
|
htlcPreimages[ctx.circuitKey] = children[0].Preimage
|
||||||
for idx, child := range children[1:] {
|
for idx, child := range children[1:] {
|
||||||
circuitKey := indexToCircuitKey[idx]
|
circuitKey := indexToCircuitKey[idx]
|
||||||
@@ -367,11 +367,11 @@ func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
|
|||||||
// send payments and any invoices we created in the past that are valid and
|
// send payments and any invoices we created in the past that are valid and
|
||||||
// still had the optional mpp bit set.
|
// still had the optional mpp bit set.
|
||||||
func updateLegacy(ctx *invoiceUpdateCtx,
|
func updateLegacy(ctx *invoiceUpdateCtx,
|
||||||
inv *channeldb.Invoice) (*channeldb.InvoiceUpdateDesc, HtlcResolution, error) {
|
inv *Invoice) (*InvoiceUpdateDesc, HtlcResolution, error) {
|
||||||
|
|
||||||
// If the invoice is already canceled, there is no further
|
// If the invoice is already canceled, there is no further
|
||||||
// checking to do.
|
// checking to do.
|
||||||
if inv.State == channeldb.ContractCanceled {
|
if inv.State == ContractCanceled {
|
||||||
return nil, ctx.failRes(ResultInvoiceAlreadyCanceled), nil
|
return nil, ctx.failRes(ResultInvoiceAlreadyCanceled), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +401,7 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
// Don't allow settling the invoice with an old style
|
// Don't allow settling the invoice with an old style
|
||||||
// htlc if we are already in the process of gathering an
|
// htlc if we are already in the process of gathering an
|
||||||
// mpp set.
|
// mpp set.
|
||||||
for _, htlc := range inv.HTLCSet(nil, channeldb.HtlcStateAccepted) {
|
for _, htlc := range inv.HTLCSet(nil, HtlcStateAccepted) {
|
||||||
if htlc.MppTotalAmt > 0 {
|
if htlc.MppTotalAmt > 0 {
|
||||||
return nil, ctx.failRes(ResultMppInProgress), nil
|
return nil, ctx.failRes(ResultMppInProgress), nil
|
||||||
}
|
}
|
||||||
@@ -417,7 +417,7 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Record HTLC in the invoice database.
|
// Record HTLC in the invoice database.
|
||||||
newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{
|
newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
|
||||||
ctx.circuitKey: {
|
ctx.circuitKey: {
|
||||||
Amt: ctx.amtPaid,
|
Amt: ctx.amtPaid,
|
||||||
Expiry: ctx.expiry,
|
Expiry: ctx.expiry,
|
||||||
@@ -426,17 +426,17 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
update := channeldb.InvoiceUpdateDesc{
|
update := InvoiceUpdateDesc{
|
||||||
AddHtlcs: newHtlcs,
|
AddHtlcs: newHtlcs,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't update invoice state if we are accepting a duplicate payment.
|
// Don't update invoice state if we are accepting a duplicate payment.
|
||||||
// We do accept or settle the HTLC.
|
// We do accept or settle the HTLC.
|
||||||
switch inv.State {
|
switch inv.State {
|
||||||
case channeldb.ContractAccepted:
|
case ContractAccepted:
|
||||||
return &update, ctx.acceptRes(resultDuplicateToAccepted), nil
|
return &update, ctx.acceptRes(resultDuplicateToAccepted), nil
|
||||||
|
|
||||||
case channeldb.ContractSettled:
|
case ContractSettled:
|
||||||
return &update, ctx.settleRes(
|
return &update, ctx.settleRes(
|
||||||
*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
|
*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
|
||||||
), nil
|
), nil
|
||||||
@@ -445,15 +445,15 @@ func updateLegacy(ctx *invoiceUpdateCtx,
|
|||||||
// 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.
|
||||||
if inv.HodlInvoice {
|
if inv.HodlInvoice {
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: ContractAccepted,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &update, ctx.acceptRes(resultAccepted), nil
|
return &update, ctx.acceptRes(resultAccepted), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractSettled,
|
NewState: ContractSettled,
|
||||||
Preimage: inv.Terms.PaymentPreimage,
|
Preimage: inv.Terms.PaymentPreimage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/netann"
|
"github.com/lightningnetwork/lnd/netann"
|
||||||
@@ -46,7 +47,7 @@ const (
|
|||||||
// AddInvoiceConfig contains dependencies for invoice creation.
|
// AddInvoiceConfig contains dependencies for invoice creation.
|
||||||
type AddInvoiceConfig struct {
|
type AddInvoiceConfig struct {
|
||||||
// AddInvoice is called to add the invoice to the registry.
|
// AddInvoice is called to add the invoice to the registry.
|
||||||
AddInvoice func(invoice *channeldb.Invoice, paymentHash lntypes.Hash) (
|
AddInvoice func(invoice *invoices.Invoice, paymentHash lntypes.Hash) (
|
||||||
uint64, error)
|
uint64, error)
|
||||||
|
|
||||||
// IsChannelActive is used to generate valid hop hints.
|
// IsChannelActive is used to generate valid hop hints.
|
||||||
@@ -234,7 +235,7 @@ func (d *AddInvoiceData) mppPaymentHashAndPreimage() (*lntypes.Preimage,
|
|||||||
// duplicated invoices are rejected, therefore all invoices *must* have a
|
// duplicated invoices are rejected, therefore all invoices *must* have a
|
||||||
// unique payment preimage.
|
// unique payment preimage.
|
||||||
func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
||||||
invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {
|
invoice *AddInvoiceData) (*lntypes.Hash, *invoices.Invoice, error) {
|
||||||
|
|
||||||
paymentPreimage, paymentHash, err := invoice.paymentHashAndPreimage()
|
paymentPreimage, paymentHash, err := invoice.paymentHashAndPreimage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -243,10 +244,10 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
|
|
||||||
// The size of the memo, receipt and description hash attached must not
|
// The size of the memo, receipt and description hash attached must not
|
||||||
// exceed the maximum values for either of the fields.
|
// exceed the maximum values for either of the fields.
|
||||||
if len(invoice.Memo) > channeldb.MaxMemoSize {
|
if len(invoice.Memo) > invoices.MaxMemoSize {
|
||||||
return nil, nil, fmt.Errorf("memo too large: %v bytes "+
|
return nil, nil, fmt.Errorf("memo too large: %v bytes "+
|
||||||
"(maxsize=%v)", len(invoice.Memo),
|
"(maxsize=%v)", len(invoice.Memo),
|
||||||
channeldb.MaxMemoSize)
|
invoices.MaxMemoSize)
|
||||||
}
|
}
|
||||||
if len(invoice.DescriptionHash) > 0 &&
|
if len(invoice.DescriptionHash) > 0 &&
|
||||||
len(invoice.DescriptionHash) != 32 {
|
len(invoice.DescriptionHash) != 32 {
|
||||||
@@ -448,11 +449,11 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newInvoice := &channeldb.Invoice{
|
newInvoice := &invoices.Invoice{
|
||||||
CreationDate: creationDate,
|
CreationDate: creationDate,
|
||||||
Memo: []byte(invoice.Memo),
|
Memo: []byte(invoice.Memo),
|
||||||
PaymentRequest: []byte(payReqString),
|
PaymentRequest: []byte(payReqString),
|
||||||
Terms: channeldb.ContractTerm{
|
Terms: invoices.ContractTerm{
|
||||||
FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()),
|
FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()),
|
||||||
Expiry: payReq.Expiry(),
|
Expiry: payReq.Expiry(),
|
||||||
Value: amtMSat,
|
Value: amtMSat,
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ package invoicesrpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/macaroons"
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
@@ -287,7 +288,7 @@ func (s *Server) SettleInvoice(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = s.cfg.InvoiceRegistry.SettleHodlInvoice(preimage)
|
err = s.cfg.InvoiceRegistry.SettleHodlInvoice(preimage)
|
||||||
if err != nil && err != channeldb.ErrInvoiceAlreadySettled {
|
if err != nil && !errors.Is(err, invoices.ErrInvoiceAlreadySettled) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +381,7 @@ func (s *Server) AddHoldInvoice(ctx context.Context,
|
|||||||
func (s *Server) LookupInvoiceV2(ctx context.Context,
|
func (s *Server) LookupInvoiceV2(ctx context.Context,
|
||||||
req *LookupInvoiceMsg) (*lnrpc.Invoice, error) {
|
req *LookupInvoiceMsg) (*lnrpc.Invoice, error) {
|
||||||
|
|
||||||
var invoiceRef channeldb.InvoiceRef
|
var invoiceRef invoices.InvoiceRef
|
||||||
|
|
||||||
// First, we'll attempt to parse out the invoice ref from the proto
|
// First, we'll attempt to parse out the invoice ref from the proto
|
||||||
// oneof. If none of the three currently supported types was
|
// oneof. If none of the three currently supported types was
|
||||||
@@ -395,7 +396,7 @@ func (s *Server) LookupInvoiceV2(ctx context.Context,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
invoiceRef = channeldb.InvoiceRefByHash(payHash)
|
invoiceRef = invoices.InvoiceRefByHash(payHash)
|
||||||
|
|
||||||
case req.GetPaymentAddr() != nil &&
|
case req.GetPaymentAddr() != nil &&
|
||||||
req.LookupModifier == LookupModifier_HTLC_SET_BLANK:
|
req.LookupModifier == LookupModifier_HTLC_SET_BLANK:
|
||||||
@@ -403,13 +404,13 @@ func (s *Server) LookupInvoiceV2(ctx context.Context,
|
|||||||
var payAddr [32]byte
|
var payAddr [32]byte
|
||||||
copy(payAddr[:], req.GetPaymentAddr())
|
copy(payAddr[:], req.GetPaymentAddr())
|
||||||
|
|
||||||
invoiceRef = channeldb.InvoiceRefByAddrBlankHtlc(payAddr)
|
invoiceRef = invoices.InvoiceRefByAddrBlankHtlc(payAddr)
|
||||||
|
|
||||||
case req.GetPaymentAddr() != nil:
|
case req.GetPaymentAddr() != nil:
|
||||||
var payAddr [32]byte
|
var payAddr [32]byte
|
||||||
copy(payAddr[:], req.GetPaymentAddr())
|
copy(payAddr[:], req.GetPaymentAddr())
|
||||||
|
|
||||||
invoiceRef = channeldb.InvoiceRefByAddr(payAddr)
|
invoiceRef = invoices.InvoiceRefByAddr(payAddr)
|
||||||
|
|
||||||
case req.GetSetId() != nil &&
|
case req.GetSetId() != nil &&
|
||||||
req.LookupModifier == LookupModifier_HTLC_SET_ONLY:
|
req.LookupModifier == LookupModifier_HTLC_SET_ONLY:
|
||||||
@@ -417,13 +418,13 @@ func (s *Server) LookupInvoiceV2(ctx context.Context,
|
|||||||
var setID [32]byte
|
var setID [32]byte
|
||||||
copy(setID[:], req.GetSetId())
|
copy(setID[:], req.GetSetId())
|
||||||
|
|
||||||
invoiceRef = channeldb.InvoiceRefBySetIDFiltered(setID)
|
invoiceRef = invoices.InvoiceRefBySetIDFiltered(setID)
|
||||||
|
|
||||||
case req.GetSetId() != nil:
|
case req.GetSetId() != nil:
|
||||||
var setID [32]byte
|
var setID [32]byte
|
||||||
copy(setID[:], req.GetSetId())
|
copy(setID[:], req.GetSetId())
|
||||||
|
|
||||||
invoiceRef = channeldb.InvoiceRefBySetID(setID)
|
invoiceRef = invoices.InvoiceRefBySetID(setID)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, status.Error(codes.InvalidArgument,
|
return nil, status.Error(codes.InvalidArgument,
|
||||||
@@ -434,7 +435,7 @@ func (s *Server) LookupInvoiceV2(ctx context.Context,
|
|||||||
// we can't find it in the database.
|
// we can't find it in the database.
|
||||||
invoice, err := s.cfg.InvoiceRegistry.LookupInvoiceByRef(invoiceRef)
|
invoice, err := s.cfg.InvoiceRegistry.LookupInvoiceByRef(invoiceRef)
|
||||||
switch {
|
switch {
|
||||||
case err == channeldb.ErrInvoiceNotFound:
|
case errors.Is(err, invoices.ErrInvoiceNotFound):
|
||||||
return nil, status.Error(codes.NotFound, err.Error())
|
return nil, status.Error(codes.NotFound, err.Error())
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+21
-17
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
// because not all information is stored in dedicated invoice fields. If there
|
// because not all information is stored in dedicated invoice fields. If there
|
||||||
// is no payment request present, a dummy request will be returned. This can
|
// is no payment request present, a dummy request will be returned. This can
|
||||||
// happen with just-in-time inserted keysend invoices.
|
// happen with just-in-time inserted keysend invoices.
|
||||||
func decodePayReq(invoice *channeldb.Invoice,
|
func decodePayReq(invoice *invoices.Invoice,
|
||||||
activeNetParams *chaincfg.Params) (*zpay32.Invoice, error) {
|
activeNetParams *chaincfg.Params) (*zpay32.Invoice, error) {
|
||||||
|
|
||||||
paymentRequest := string(invoice.PaymentRequest)
|
paymentRequest := string(invoice.PaymentRequest)
|
||||||
@@ -40,8 +40,8 @@ func decodePayReq(invoice *channeldb.Invoice,
|
|||||||
return decoded, nil
|
return decoded, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRPCInvoice creates an *lnrpc.Invoice from the *channeldb.Invoice.
|
// CreateRPCInvoice creates an *lnrpc.Invoice from the *invoices.Invoice.
|
||||||
func CreateRPCInvoice(invoice *channeldb.Invoice,
|
func CreateRPCInvoice(invoice *invoices.Invoice,
|
||||||
activeNetParams *chaincfg.Params) (*lnrpc.Invoice, error) {
|
activeNetParams *chaincfg.Params) (*lnrpc.Invoice, error) {
|
||||||
|
|
||||||
decoded, err := decodePayReq(invoice, activeNetParams)
|
decoded, err := decodePayReq(invoice, activeNetParams)
|
||||||
@@ -76,18 +76,22 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
satAmt := invoice.Terms.Value.ToSatoshis()
|
satAmt := invoice.Terms.Value.ToSatoshis()
|
||||||
satAmtPaid := invoice.AmtPaid.ToSatoshis()
|
satAmtPaid := invoice.AmtPaid.ToSatoshis()
|
||||||
|
|
||||||
isSettled := invoice.State == channeldb.ContractSettled
|
isSettled := invoice.State == invoices.ContractSettled
|
||||||
|
|
||||||
var state lnrpc.Invoice_InvoiceState
|
var state lnrpc.Invoice_InvoiceState
|
||||||
switch invoice.State {
|
switch invoice.State {
|
||||||
case channeldb.ContractOpen:
|
case invoices.ContractOpen:
|
||||||
state = lnrpc.Invoice_OPEN
|
state = lnrpc.Invoice_OPEN
|
||||||
case channeldb.ContractSettled:
|
|
||||||
|
case invoices.ContractSettled:
|
||||||
state = lnrpc.Invoice_SETTLED
|
state = lnrpc.Invoice_SETTLED
|
||||||
case channeldb.ContractCanceled:
|
|
||||||
|
case invoices.ContractCanceled:
|
||||||
state = lnrpc.Invoice_CANCELED
|
state = lnrpc.Invoice_CANCELED
|
||||||
case channeldb.ContractAccepted:
|
|
||||||
|
case invoices.ContractAccepted:
|
||||||
state = lnrpc.Invoice_ACCEPTED
|
state = lnrpc.Invoice_ACCEPTED
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown invoice state %v",
|
return nil, fmt.Errorf("unknown invoice state %v",
|
||||||
invoice.State)
|
invoice.State)
|
||||||
@@ -97,11 +101,11 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
for key, htlc := range invoice.Htlcs {
|
for key, htlc := range invoice.Htlcs {
|
||||||
var state lnrpc.InvoiceHTLCState
|
var state lnrpc.InvoiceHTLCState
|
||||||
switch htlc.State {
|
switch htlc.State {
|
||||||
case channeldb.HtlcStateAccepted:
|
case invoices.HtlcStateAccepted:
|
||||||
state = lnrpc.InvoiceHTLCState_ACCEPTED
|
state = lnrpc.InvoiceHTLCState_ACCEPTED
|
||||||
case channeldb.HtlcStateSettled:
|
case invoices.HtlcStateSettled:
|
||||||
state = lnrpc.InvoiceHTLCState_SETTLED
|
state = lnrpc.InvoiceHTLCState_SETTLED
|
||||||
case channeldb.HtlcStateCanceled:
|
case invoices.HtlcStateCanceled:
|
||||||
state = lnrpc.InvoiceHTLCState_CANCELED
|
state = lnrpc.InvoiceHTLCState_CANCELED
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown state %v", htlc.State)
|
return nil, fmt.Errorf("unknown state %v", htlc.State)
|
||||||
@@ -139,7 +143,7 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only report resolved times if htlc is resolved.
|
// Only report resolved times if htlc is resolved.
|
||||||
if htlc.State != channeldb.HtlcStateAccepted {
|
if htlc.State != invoices.HtlcStateAccepted {
|
||||||
rpcHtlc.ResolveTime = htlc.ResolveTime.Unix()
|
rpcHtlc.ResolveTime = htlc.ResolveTime.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,11 +186,11 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
|
|
||||||
var state lnrpc.InvoiceHTLCState
|
var state lnrpc.InvoiceHTLCState
|
||||||
switch ampState.State {
|
switch ampState.State {
|
||||||
case channeldb.HtlcStateAccepted:
|
case invoices.HtlcStateAccepted:
|
||||||
state = lnrpc.InvoiceHTLCState_ACCEPTED
|
state = lnrpc.InvoiceHTLCState_ACCEPTED
|
||||||
case channeldb.HtlcStateSettled:
|
case invoices.HtlcStateSettled:
|
||||||
state = lnrpc.InvoiceHTLCState_SETTLED
|
state = lnrpc.InvoiceHTLCState_SETTLED
|
||||||
case channeldb.HtlcStateCanceled:
|
case invoices.HtlcStateCanceled:
|
||||||
state = lnrpc.InvoiceHTLCState_CANCELED
|
state = lnrpc.InvoiceHTLCState_CANCELED
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown state %v", ampState.State)
|
return nil, fmt.Errorf("unknown state %v", ampState.State)
|
||||||
@@ -202,7 +206,7 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
// If at least one of the present HTLC sets show up as being
|
// If at least one of the present HTLC sets show up as being
|
||||||
// settled, then we'll mark the invoice itself as being
|
// settled, then we'll mark the invoice itself as being
|
||||||
// settled.
|
// settled.
|
||||||
if ampState.State == channeldb.HtlcStateSettled {
|
if ampState.State == invoices.HtlcStateSettled {
|
||||||
rpcInvoice.Settled = true // nolint:staticcheck
|
rpcInvoice.Settled = true // nolint:staticcheck
|
||||||
rpcInvoice.State = lnrpc.Invoice_SETTLED
|
rpcInvoice.State = lnrpc.Invoice_SETTLED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package routerrpc
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
@@ -102,7 +102,7 @@ func (r *forwardInterceptor) resolveFromClient(
|
|||||||
|
|
||||||
log.Tracef("Resolving intercepted packet %v", in)
|
log.Tracef("Resolving intercepted packet %v", in)
|
||||||
|
|
||||||
circuitKey := channeldb.CircuitKey{
|
circuitKey := models.CircuitKey{
|
||||||
ChanID: lnwire.NewShortChanIDFromInt(in.IncomingCircuitKey.ChanId),
|
ChanID: lnwire.NewShortChanIDFromInt(in.IncomingCircuitKey.ChanId),
|
||||||
HtlcID: in.IncomingCircuitKey.HtlcId,
|
HtlcID: in.IncomingCircuitKey.HtlcId,
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-14
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -286,7 +287,7 @@ type PaymentDescriptor struct {
|
|||||||
// NOTE: This field is only populated for payment descriptors in the
|
// NOTE: This field is only populated for payment descriptors in the
|
||||||
// *local* update log, and if the Add packet was delivered by the
|
// *local* update log, and if the Add packet was delivered by the
|
||||||
// switch.
|
// switch.
|
||||||
OpenCircuitKey *channeldb.CircuitKey
|
OpenCircuitKey *models.CircuitKey
|
||||||
|
|
||||||
// ClosedCircuitKey references the incoming Chan/HTLC ID of the Add HTLC
|
// ClosedCircuitKey references the incoming Chan/HTLC ID of the Add HTLC
|
||||||
// that opened the circuit.
|
// that opened the circuit.
|
||||||
@@ -294,7 +295,7 @@ type PaymentDescriptor struct {
|
|||||||
// NOTE: This field is only populated for payment descriptors in the
|
// NOTE: This field is only populated for payment descriptors in the
|
||||||
// *local* update log, and if settle/fails have a committed circuit in
|
// *local* update log, and if settle/fails have a committed circuit in
|
||||||
// the circuit map.
|
// the circuit map.
|
||||||
ClosedCircuitKey *channeldb.CircuitKey
|
ClosedCircuitKey *models.CircuitKey
|
||||||
|
|
||||||
// localOutputIndex is the output index of this HTLc output in the
|
// localOutputIndex is the output index of this HTLc output in the
|
||||||
// commitment transaction of the local node.
|
// commitment transaction of the local node.
|
||||||
@@ -3294,8 +3295,8 @@ func (lc *LightningChannel) createCommitDiff(
|
|||||||
logUpdates []channeldb.LogUpdate
|
logUpdates []channeldb.LogUpdate
|
||||||
ackAddRefs []channeldb.AddRef
|
ackAddRefs []channeldb.AddRef
|
||||||
settleFailRefs []channeldb.SettleFailRef
|
settleFailRefs []channeldb.SettleFailRef
|
||||||
openCircuitKeys []channeldb.CircuitKey
|
openCircuitKeys []models.CircuitKey
|
||||||
closedCircuitKeys []channeldb.CircuitKey
|
closedCircuitKeys []models.CircuitKey
|
||||||
)
|
)
|
||||||
|
|
||||||
// We'll now run through our local update log to locate the items which
|
// We'll now run through our local update log to locate the items which
|
||||||
@@ -3850,14 +3851,14 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig,
|
|||||||
// not received
|
// not received
|
||||||
//
|
//
|
||||||
// If we detect a scenario where we need to send a CommitSig+Updates, this
|
// If we detect a scenario where we need to send a CommitSig+Updates, this
|
||||||
// method also returns two sets channeldb.CircuitKeys identifying the circuits
|
// method also returns two sets models.CircuitKeys identifying the circuits
|
||||||
// that were opened and closed, respectively, as a result of signing the
|
// that were opened and closed, respectively, as a result of signing the
|
||||||
// previous commitment txn. This allows the link to clear its mailbox of those
|
// previous commitment txn. This allows the link to clear its mailbox of those
|
||||||
// circuits in case they are still in memory, and ensure the switch's circuit
|
// circuits in case they are still in memory, and ensure the switch's circuit
|
||||||
// map has been updated by deleting the closed circuits.
|
// map has been updated by deleting the closed circuits.
|
||||||
func (lc *LightningChannel) ProcessChanSyncMsg(
|
func (lc *LightningChannel) ProcessChanSyncMsg(
|
||||||
msg *lnwire.ChannelReestablish) ([]lnwire.Message, []channeldb.CircuitKey,
|
msg *lnwire.ChannelReestablish) ([]lnwire.Message, []models.CircuitKey,
|
||||||
[]channeldb.CircuitKey, error) {
|
[]models.CircuitKey, error) {
|
||||||
|
|
||||||
// Now we'll examine the state we have, vs what was contained in the
|
// Now we'll examine the state we have, vs what was contained in the
|
||||||
// chain sync message. If we're de-synchronized, then we'll send a
|
// chain sync message. If we're de-synchronized, then we'll send a
|
||||||
@@ -3865,8 +3866,8 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
|
|||||||
// resync.
|
// resync.
|
||||||
var (
|
var (
|
||||||
updates []lnwire.Message
|
updates []lnwire.Message
|
||||||
openedCircuits []channeldb.CircuitKey
|
openedCircuits []models.CircuitKey
|
||||||
closedCircuits []channeldb.CircuitKey
|
closedCircuits []models.CircuitKey
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the remote party included the optional fields, then we'll verify
|
// If the remote party included the optional fields, then we'll verify
|
||||||
@@ -5162,7 +5163,7 @@ func (lc *LightningChannel) InitNextRevocation(revKey *btcec.PublicKey) error {
|
|||||||
//
|
//
|
||||||
// NOTE: It is okay for sourceRef to be nil when unit testing the wallet.
|
// NOTE: It is okay for sourceRef to be nil when unit testing the wallet.
|
||||||
func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC,
|
func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC,
|
||||||
openKey *channeldb.CircuitKey) (uint64, error) {
|
openKey *models.CircuitKey) (uint64, error) {
|
||||||
|
|
||||||
lc.Lock()
|
lc.Lock()
|
||||||
defer lc.Unlock()
|
defer lc.Unlock()
|
||||||
@@ -5275,7 +5276,7 @@ func (lc *LightningChannel) MayAddOutgoingHtlc(amt lnwire.MilliSatoshi) error {
|
|||||||
&lnwire.UpdateAddHTLC{
|
&lnwire.UpdateAddHTLC{
|
||||||
Amount: mockHtlcAmt,
|
Amount: mockHtlcAmt,
|
||||||
},
|
},
|
||||||
&channeldb.CircuitKey{},
|
&models.CircuitKey{},
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := lc.validateAddHtlc(pd); err != nil {
|
if err := lc.validateAddHtlc(pd); err != nil {
|
||||||
@@ -5289,7 +5290,7 @@ func (lc *LightningChannel) MayAddOutgoingHtlc(amt lnwire.MilliSatoshi) error {
|
|||||||
// htlcAddDescriptor returns a payment descriptor for the htlc and open key
|
// htlcAddDescriptor returns a payment descriptor for the htlc and open key
|
||||||
// provided to add to our local update log.
|
// provided to add to our local update log.
|
||||||
func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC,
|
func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC,
|
||||||
openKey *channeldb.CircuitKey) *PaymentDescriptor {
|
openKey *models.CircuitKey) *PaymentDescriptor {
|
||||||
|
|
||||||
return &PaymentDescriptor{
|
return &PaymentDescriptor{
|
||||||
EntryType: Add,
|
EntryType: Add,
|
||||||
@@ -5399,7 +5400,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, err
|
|||||||
// testing the wallet.
|
// testing the wallet.
|
||||||
func (lc *LightningChannel) SettleHTLC(preimage [32]byte,
|
func (lc *LightningChannel) SettleHTLC(preimage [32]byte,
|
||||||
htlcIndex uint64, sourceRef *channeldb.AddRef,
|
htlcIndex uint64, sourceRef *channeldb.AddRef,
|
||||||
destRef *channeldb.SettleFailRef, closeKey *channeldb.CircuitKey) error {
|
destRef *channeldb.SettleFailRef, closeKey *models.CircuitKey) error {
|
||||||
|
|
||||||
lc.Lock()
|
lc.Lock()
|
||||||
defer lc.Unlock()
|
defer lc.Unlock()
|
||||||
@@ -5510,7 +5511,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6
|
|||||||
// testing the wallet.
|
// testing the wallet.
|
||||||
func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte,
|
func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte,
|
||||||
sourceRef *channeldb.AddRef, destRef *channeldb.SettleFailRef,
|
sourceRef *channeldb.AddRef, destRef *channeldb.SettleFailRef,
|
||||||
closeKey *channeldb.CircuitKey) error {
|
closeKey *models.CircuitKey) error {
|
||||||
|
|
||||||
lc.Lock()
|
lc.Lock()
|
||||||
defer lc.Unlock()
|
defer lc.Unlock()
|
||||||
|
|||||||
@@ -3055,10 +3055,10 @@ func TestLightningWallet(t *testing.T, targetBackEnd string) {
|
|||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
db, err := channeldb.Open(tempDir)
|
db, err := channeldb.Open(tempDir)
|
||||||
require.NoError(t, err, "unable to create db")
|
require.NoError(t, err, "unable to create db")
|
||||||
testCfg := chainntnfs.CacheConfig{
|
testCfg := channeldb.CacheConfig{
|
||||||
QueryDisable: false,
|
QueryDisable: false,
|
||||||
}
|
}
|
||||||
hintCache, err := chainntnfs.NewHeightHintCache(testCfg, db.Backend)
|
hintCache, err := channeldb.NewHeightHintCache(testCfg, db.Backend)
|
||||||
require.NoError(t, err, "unable to create height hint cache")
|
require.NoError(t, err, "unable to create height hint cache")
|
||||||
blockCache := blockcache.NewBlockCache(10000)
|
blockCache := blockcache.NewBlockCache(10000)
|
||||||
chainNotifier, err := btcdnotify.New(
|
chainNotifier, err := btcdnotify.New(
|
||||||
|
|||||||
+2
-2
@@ -5507,7 +5507,7 @@ func (r *rpcServer) LookupInvoice(ctx context.Context,
|
|||||||
|
|
||||||
invoice, err := r.server.invoices.LookupInvoice(payHash)
|
invoice, err := r.server.invoices.LookupInvoice(payHash)
|
||||||
switch {
|
switch {
|
||||||
case err == channeldb.ErrInvoiceNotFound:
|
case errors.Is(err, invoices.ErrInvoiceNotFound):
|
||||||
return nil, status.Error(codes.NotFound, err.Error())
|
return nil, status.Error(codes.NotFound, err.Error())
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -5551,7 +5551,7 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
|
|||||||
|
|
||||||
// Next, we'll map the proto request into a format that is understood by
|
// Next, we'll map the proto request into a format that is understood by
|
||||||
// the database.
|
// the database.
|
||||||
q := channeldb.InvoiceQuery{
|
q := invoices.InvoiceQuery{
|
||||||
IndexOffset: req.IndexOffset,
|
IndexOffset: req.IndexOffset,
|
||||||
NumMaxInvoices: req.NumMaxInvoices,
|
NumMaxInvoices: req.NumMaxInvoices,
|
||||||
PendingOnly: req.PendingOnly,
|
PendingOnly: req.PendingOnly,
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
uint32(currentHeight), currentHash, cc.ChainNotifier,
|
uint32(currentHeight), currentHash, cc.ChainNotifier,
|
||||||
)
|
)
|
||||||
s.invoices = invoices.NewRegistry(
|
s.invoices = invoices.NewRegistry(
|
||||||
dbs.ChanStateDB, expiryWatcher, ®istryConfig,
|
dbs.InvoiceDB, expiryWatcher, ®istryConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
s.htlcNotifier = htlcswitch.NewHtlcNotifier(time.Now)
|
s.htlcNotifier = htlcswitch.NewHtlcNotifier(time.Now)
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
@@ -95,7 +96,7 @@ func (p *preimageBeacon) SubscribeUpdates(
|
|||||||
Hash: htlc.RHash,
|
Hash: htlc.RHash,
|
||||||
IncomingExpiry: htlc.RefundTimeout,
|
IncomingExpiry: htlc.RefundTimeout,
|
||||||
IncomingAmount: htlc.Amt,
|
IncomingAmount: htlc.Amt,
|
||||||
IncomingCircuit: channeldb.CircuitKey{
|
IncomingCircuit: models.CircuitKey{
|
||||||
ChanID: chanID,
|
ChanID: chanID,
|
||||||
HtlcID: htlc.HtlcIndex,
|
HtlcID: htlc.HtlcIndex,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user