mirror of
https://github.com/lightninglabs/faraday.git
synced 2026-08-20 13:28:33 +02:00
accounting: add regular and circular payment entries
This commit is contained in:
parent
5e89717cb9
commit
a1fd2dbbd2
5 changed files with 220 additions and 0 deletions
|
|
@ -24,6 +24,11 @@ func invertedSatsToMsats(sats int64) int64 {
|
|||
return satsToMsat(sats) * -1
|
||||
}
|
||||
|
||||
// invertMsat flips the sign value of a msat value.
|
||||
func invertMsat(msat int64) int64 {
|
||||
return msat * -1
|
||||
}
|
||||
|
||||
// getConversion is a helper function which queries coincap for a relevant set
|
||||
// of price data and returns a convert function which can be used to get
|
||||
// individual price points from this data.
|
||||
|
|
|
|||
|
|
@ -103,6 +103,36 @@ Circular receipts record instances where we have paid one of our own invoices.
|
|||
- Reference: The preimage of the invoice.
|
||||
- Note: Optionally set if the invoice had a memo attached, was overpaid, or was a keysend.
|
||||
|
||||
### Payment
|
||||
Payments off chain represent payments made via the Lightning Network.
|
||||
|
||||
- Amount: The amount in millisatoshis that we paid, excluding the off chain fees paid.
|
||||
- TxID: The payment hash.
|
||||
- Reference: Unique payment ID: Payment hash.
|
||||
- Note: The preimage for the payment, which serves as proof of payment.
|
||||
|
||||
### Fee
|
||||
- Amount: The amount in millisatoshis that was paid in off chain fees.
|
||||
- TxID: The payment hash.
|
||||
- Reference: Unique payment ID: Payment hash: -1.
|
||||
- Note: A note indicating the number of htlcs the payment was paid over.
|
||||
|
||||
### Circular Payment
|
||||
Circular payments represent payments made to our own node to rebalance channels. These payments are paid from our node to one of our own invoices.
|
||||
|
||||
- Amount: The amount that was rebalanced.
|
||||
- TxID: The payment hash.
|
||||
- Reference: Unique payment ID: Payment hash.
|
||||
- Note: The preimage for the payment, which serves as proof of payment.
|
||||
|
||||
### Circular Payment Fee
|
||||
Circular payment fees represent the fees we paid to loop a circular payment to ourselves.
|
||||
|
||||
- Amount: The amount that was paid in off chain fees.
|
||||
- TxID: The payment hash.
|
||||
- Reference: Unique payment ID: Payment hash: -1.
|
||||
- Note: A note indicating the number of htlcs the payment was paid over.
|
||||
|
||||
### Forwards
|
||||
A forward represents a payment that arrives at our node on an incoming channel and is forwarded out on an outgoing channel in exchange for fees. The forward itself does not changes our balance, since it just shifts funds over our channels. We include forwarding entries with zero balances for completeness. Forwarding fee entries reflect the increase in our holdings from the fee we are paid.
|
||||
|
||||
|
|
|
|||
|
|
@ -242,6 +242,80 @@ func invoiceEntry(invoice *lnrpc.Invoice, circularReceipt bool,
|
|||
)
|
||||
}
|
||||
|
||||
// paymentReference produces a unique reference for a payment. Since payment
|
||||
// hash is not guaranteed to be unique, we use the payments unique sequence
|
||||
// number and its hash.
|
||||
func paymentReference(sequenceNumber uint64, paymentHash string) string {
|
||||
return fmt.Sprintf("%v:%v", sequenceNumber, paymentHash)
|
||||
}
|
||||
|
||||
// paymentNote creates a note for payments from our node.
|
||||
func paymentNote(preimage string) string {
|
||||
return fmt.Sprintf("Preimage: %v", preimage)
|
||||
}
|
||||
|
||||
// paymentFeeNote creates a note for a payment fee entry.
|
||||
func paymentFeeNote(htlcs []*lnrpc.HTLCAttempt) string {
|
||||
return fmt.Sprintf("Settled with: %v htlc(s)", len(htlcs))
|
||||
}
|
||||
|
||||
// paymentEntry creates an entry for an off chain payment, including fee entries
|
||||
// where required.
|
||||
func paymentEntry(payment settledPayment, paidToSelf bool,
|
||||
convert msatToFiat) ([]*HarmonyEntry, error) {
|
||||
|
||||
// It is possible to make a payment to ourselves as part of a circular
|
||||
// rebalance which is operationally used to shift funds between
|
||||
// channels. For these payment types, we lose balance from fees, but do
|
||||
// not change our balance from the actual payment because it is paid
|
||||
// back to ourselves.
|
||||
var (
|
||||
paymentType = EntryTypePayment
|
||||
feeType = EntryTypeFee
|
||||
)
|
||||
|
||||
// If we made the payment to ourselves, we set special entry types,
|
||||
// since the payment amount did not actually affect our balance.
|
||||
if paidToSelf {
|
||||
paymentType = EntryTypeCircularPayment
|
||||
feeType = EntryTypeCircularPaymentFee
|
||||
}
|
||||
|
||||
note := paymentNote(payment.PaymentPreimage)
|
||||
ref := paymentReference(payment.PaymentIndex, payment.PaymentHash)
|
||||
|
||||
// Payment values are expressed as positive values over rpc, but they
|
||||
// decrease our balance so we flip our value to a negative one.
|
||||
amt := invertMsat(payment.ValueMsat)
|
||||
|
||||
paymentEntry, err := newHarmonyEntry(
|
||||
payment.settleTime.Unix(), amt, paymentType,
|
||||
payment.PaymentHash, ref, note, false, convert,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we paid no fees (possible for payments to our direct peer), then
|
||||
// we just return the payment entry.
|
||||
if payment.FeeMsat == 0 {
|
||||
return []*HarmonyEntry{paymentEntry}, nil
|
||||
}
|
||||
|
||||
feeNote := paymentFeeNote(payment.Htlcs)
|
||||
feeRef := feeReference(ref)
|
||||
feeAmt := invertMsat(payment.FeeMsat)
|
||||
|
||||
feeEntry, err := newHarmonyEntry(
|
||||
payment.settleTime.Unix(), feeAmt, feeType,
|
||||
payment.PaymentHash, feeRef, feeNote, false, convert,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*HarmonyEntry{paymentEntry, feeEntry}, nil
|
||||
}
|
||||
|
||||
// forwardTxid provides a best effort txid using incoming and outgoing channel
|
||||
// ID paired with timestamp in an effort to make txid unique per htlc forwarded.
|
||||
// This is not used as a reference because we could theoretically have duplicate
|
||||
|
|
|
|||
|
|
@ -120,6 +120,33 @@ var (
|
|||
IsKeysend: true,
|
||||
}
|
||||
|
||||
paymentTime = 1590399649
|
||||
|
||||
paymentHash = "11f414479f0a0c2762492c71c58dded5dce99d56d65c3fa523f73513605bebb3"
|
||||
|
||||
paymentPreimage = "adfef20b24152accd4ed9a05257fb77203d90a8bbbe6d4069a75c5320f0538d9"
|
||||
|
||||
paymentMsat = 30000
|
||||
|
||||
paymentFeeMsat = 45
|
||||
|
||||
paymentIndex = 33
|
||||
|
||||
payment = &lnrpc.Payment{
|
||||
PaymentHash: paymentHash,
|
||||
PaymentPreimage: paymentPreimage,
|
||||
ValueMsat: int64(paymentMsat),
|
||||
Status: lnrpc.Payment_SUCCEEDED,
|
||||
FeeMsat: int64(paymentFeeMsat),
|
||||
Htlcs: []*lnrpc.HTLCAttempt{{}},
|
||||
PaymentIndex: uint64(paymentIndex),
|
||||
}
|
||||
|
||||
settledPmt = settledPayment{
|
||||
Payment: payment,
|
||||
settleTime: time.Unix(int64(paymentTime), 0),
|
||||
}
|
||||
|
||||
forwardTs uint64 = 1590578022
|
||||
|
||||
forwardChanIn uint64 = 130841883770880
|
||||
|
|
@ -501,6 +528,82 @@ func TestInvoiceEntry(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestPaymentEntry tests creation of payment entries for circular rebalances
|
||||
// and regular payments.
|
||||
func TestPaymentEntry(t *testing.T) {
|
||||
// getEntries is a helper function which returns our expected entries
|
||||
// based on whether we are testing a payment to ourselves or not.
|
||||
getEntries := func(toSelf bool) []*HarmonyEntry {
|
||||
mockFiat, _ := mockConvert(int64(paymentMsat), 0)
|
||||
paymentRef := paymentReference(
|
||||
uint64(paymentIndex), paymentHash,
|
||||
)
|
||||
|
||||
paymentEntry := &HarmonyEntry{
|
||||
Timestamp: time.Unix(int64(paymentTime), 0),
|
||||
Amount: lnwire.MilliSatoshi(paymentMsat),
|
||||
FiatValue: mockFiat,
|
||||
TxID: paymentHash,
|
||||
Reference: paymentRef,
|
||||
Note: paymentNote(paymentPreimage),
|
||||
Type: EntryTypePayment,
|
||||
OnChain: false,
|
||||
Credit: false,
|
||||
}
|
||||
|
||||
feeFiat, _ := mockConvert(int64(paymentFeeMsat), 0)
|
||||
feeEntry := &HarmonyEntry{
|
||||
Timestamp: time.Unix(int64(paymentTime), 0),
|
||||
Amount: lnwire.MilliSatoshi(paymentFeeMsat),
|
||||
FiatValue: feeFiat,
|
||||
TxID: paymentHash,
|
||||
Reference: feeReference(paymentRef),
|
||||
Note: paymentFeeNote(payment.Htlcs),
|
||||
Type: EntryTypeFee,
|
||||
OnChain: false,
|
||||
Credit: false,
|
||||
}
|
||||
|
||||
if toSelf {
|
||||
paymentEntry.Type = EntryTypeCircularPayment
|
||||
feeEntry.Type = EntryTypeCircularPaymentFee
|
||||
}
|
||||
|
||||
return []*HarmonyEntry{paymentEntry, feeEntry}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
toSelf bool
|
||||
}{
|
||||
{
|
||||
name: "regular payment",
|
||||
toSelf: false,
|
||||
},
|
||||
{
|
||||
name: "to self",
|
||||
toSelf: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
entries, err := paymentEntry(
|
||||
settledPmt, test.toSelf, mockConvert,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expectedEntries := getEntries(test.toSelf)
|
||||
|
||||
require.Equal(t, expectedEntries, entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestForwardingEntry tests creation of a forwarding and forwarding fee entry.
|
||||
func TestForwardingEntry(t *testing.T) {
|
||||
entries, err := forwardingEntry(fwdEntry, mockConvert)
|
||||
|
|
|
|||
|
|
@ -136,4 +136,12 @@ const (
|
|||
// EntryTypeForwardFee represents the fees we earned forwarding a
|
||||
// payment.
|
||||
EntryTypeForwardFee
|
||||
|
||||
// EntryTypeCircularPayment represents an operational payment which
|
||||
// we pay to ourselves to rebalance channels.
|
||||
EntryTypeCircularPayment
|
||||
|
||||
// EntryTypeCircularPaymentFee represents a the fees paid on an
|
||||
// operational payment paid to ourselves to rebalance channels.
|
||||
EntryTypeCircularPaymentFee
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue