mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-02 11:09:38 +02:00
invoices+htlcswitch+lnrpc: cancel invoice
This commit is contained in:
parent
9cd88a04b7
commit
1b87fbfab2
@ -62,6 +62,10 @@ var (
|
|||||||
// ErrInvoiceAlreadySettled is returned when the invoice is already
|
// ErrInvoiceAlreadySettled is returned when the invoice is already
|
||||||
// settled.
|
// settled.
|
||||||
ErrInvoiceAlreadySettled = errors.New("invoice already settled")
|
ErrInvoiceAlreadySettled = errors.New("invoice already settled")
|
||||||
|
|
||||||
|
// ErrInvoiceAlreadyCanceled is returned when the invoice is already
|
||||||
|
// canceled.
|
||||||
|
ErrInvoiceAlreadyCanceled = errors.New("invoice already canceled")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -90,6 +94,9 @@ const (
|
|||||||
// ContractSettled means the htlc is settled and the invoice has been
|
// ContractSettled means the htlc is settled and the invoice has been
|
||||||
// paid.
|
// paid.
|
||||||
ContractSettled ContractState = 1
|
ContractSettled ContractState = 1
|
||||||
|
|
||||||
|
// ContractCanceled means the invoice has been canceled.
|
||||||
|
ContractCanceled ContractState = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns a human readable identifier for the ContractState type.
|
// String returns a human readable identifier for the ContractState type.
|
||||||
@ -99,6 +106,8 @@ func (c ContractState) String() string {
|
|||||||
return "Open"
|
return "Open"
|
||||||
case ContractSettled:
|
case ContractSettled:
|
||||||
return "Settled"
|
return "Settled"
|
||||||
|
case ContractCanceled:
|
||||||
|
return "Canceled"
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
@ -641,6 +650,37 @@ func (d *DB) SettleInvoice(paymentHash [32]byte,
|
|||||||
return settledInvoice, err
|
return settledInvoice, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CancelInvoice attempts to cancel the invoice corresponding to the passed
|
||||||
|
// payment hash.
|
||||||
|
func (d *DB) CancelInvoice(paymentHash lntypes.Hash) (*Invoice, error) {
|
||||||
|
var canceledInvoice *Invoice
|
||||||
|
err := d.Update(func(tx *bbolt.Tx) error {
|
||||||
|
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
invoiceIndex, err := invoices.CreateBucketIfNotExists(
|
||||||
|
invoiceIndexBucket,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the invoice index to see if an invoice paying to this
|
||||||
|
// hash exists within the DB.
|
||||||
|
invoiceNum := invoiceIndex.Get(paymentHash[:])
|
||||||
|
if invoiceNum == nil {
|
||||||
|
return ErrInvoiceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
canceledInvoice, err = cancelInvoice(invoices, invoiceNum)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return canceledInvoice, err
|
||||||
|
}
|
||||||
|
|
||||||
// InvoicesSettledSince can be used by callers to catch up any settled invoices
|
// 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
|
// they missed within the settled invoice time series. We'll return all known
|
||||||
// settled invoice that have a settle index higher than the passed
|
// settled invoice that have a settle index higher than the passed
|
||||||
@ -896,8 +936,11 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if invoice.Terms.State == ContractSettled {
|
switch invoice.Terms.State {
|
||||||
|
case ContractSettled:
|
||||||
return &invoice, ErrInvoiceAlreadySettled
|
return &invoice, ErrInvoiceAlreadySettled
|
||||||
|
case ContractCanceled:
|
||||||
|
return &invoice, ErrInvoiceAlreadyCanceled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we know the invoice hasn't already been settled, we'll
|
// Now that we know the invoice hasn't already been settled, we'll
|
||||||
@ -930,3 +973,32 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
|
|||||||
|
|
||||||
return &invoice, nil
|
return &invoice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelInvoice(invoices *bbolt.Bucket, invoiceNum []byte) (
|
||||||
|
*Invoice, error) {
|
||||||
|
|
||||||
|
invoice, err := fetchInvoice(invoiceNum, invoices)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch invoice.Terms.State {
|
||||||
|
case ContractSettled:
|
||||||
|
return &invoice, ErrInvoiceAlreadySettled
|
||||||
|
case ContractCanceled:
|
||||||
|
return &invoice, ErrInvoiceAlreadyCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.Terms.State = ContractCanceled
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := serializeInvoice(&buf, &invoice); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := invoices.Put(invoiceNum[:], buf.Bytes()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &invoice, nil
|
||||||
|
}
|
||||||
|
@ -20,6 +20,10 @@ type InvoiceDatabase interface {
|
|||||||
// SettleInvoice attempts to mark an invoice corresponding to the
|
// SettleInvoice attempts to mark an invoice corresponding to the
|
||||||
// passed payment hash as fully settled.
|
// passed payment hash as fully settled.
|
||||||
SettleInvoice(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi) error
|
SettleInvoice(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi) error
|
||||||
|
|
||||||
|
// CancelInvoice attempts to cancel the invoice corresponding to the
|
||||||
|
// passed payment hash.
|
||||||
|
CancelInvoice(payHash lntypes.Hash) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelLink is an interface which represents the subsystem for managing the
|
// ChannelLink is an interface which represents the subsystem for managing the
|
||||||
|
@ -2337,6 +2337,23 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject htlcs for canceled invoices.
|
||||||
|
if invoice.Terms.State == channeldb.ContractCanceled {
|
||||||
|
l.errorf("Rejecting htlc due to canceled " +
|
||||||
|
"invoice")
|
||||||
|
|
||||||
|
failure := lnwire.NewFailUnknownPaymentHash(
|
||||||
|
pd.Amount,
|
||||||
|
)
|
||||||
|
l.sendHTLCError(
|
||||||
|
pd.HtlcIndex, failure, obfuscator,
|
||||||
|
pd.SourceRef,
|
||||||
|
)
|
||||||
|
|
||||||
|
needUpdate = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// If the invoice is already settled, we choose to
|
// If the invoice is already settled, we choose to
|
||||||
// accept the payment to simplify failure recovery.
|
// accept the payment to simplify failure recovery.
|
||||||
//
|
//
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"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/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
@ -735,6 +735,25 @@ func (i *mockInvoiceRegistry) SettleInvoice(rhash lntypes.Hash,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *mockInvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
|
||||||
|
i.Lock()
|
||||||
|
defer i.Unlock()
|
||||||
|
|
||||||
|
invoice, ok := i.invoices[payHash]
|
||||||
|
if !ok {
|
||||||
|
return channeldb.ErrInvoiceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if invoice.Terms.State == channeldb.ContractCanceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.Terms.State = channeldb.ContractCanceled
|
||||||
|
i.invoices[payHash] = invoice
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *mockInvoiceRegistry) AddInvoice(invoice channeldb.Invoice) error {
|
func (i *mockInvoiceRegistry) AddInvoice(invoice channeldb.Invoice) error {
|
||||||
i.Lock()
|
i.Lock()
|
||||||
defer i.Unlock()
|
defer i.Unlock()
|
||||||
|
@ -162,7 +162,11 @@ func (i *InvoiceRegistry) invoiceEventNotifier() {
|
|||||||
// A sub-systems has just modified the invoice state, so we'll
|
// A sub-systems has just modified the invoice state, so we'll
|
||||||
// dispatch notifications to all registered clients.
|
// dispatch notifications to all registered clients.
|
||||||
case event := <-i.invoiceEvents:
|
case event := <-i.invoiceEvents:
|
||||||
|
// For backwards compatibility, do not notify all
|
||||||
|
// invoice subscribers of cancel events
|
||||||
|
if event.state != channeldb.ContractCanceled {
|
||||||
i.dispatchToClients(event)
|
i.dispatchToClients(event)
|
||||||
|
}
|
||||||
i.dispatchToSingleClients(event)
|
i.dispatchToSingleClients(event)
|
||||||
|
|
||||||
case <-i.quit:
|
case <-i.quit:
|
||||||
@ -256,7 +260,7 @@ func (i *InvoiceRegistry) dispatchToClients(event *invoiceEvent) {
|
|||||||
case channeldb.ContractOpen:
|
case channeldb.ContractOpen:
|
||||||
client.addIndex = invoice.AddIndex
|
client.addIndex = invoice.AddIndex
|
||||||
default:
|
default:
|
||||||
log.Errorf("unknown invoice state: %v", event.state)
|
log.Errorf("unexpected invoice state: %v", event.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,6 +480,34 @@ func (i *InvoiceRegistry) SettleInvoice(rHash lntypes.Hash,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CancelInvoice attempts to cancel the invoice corresponding to the passed
|
||||||
|
// payment hash.
|
||||||
|
func (i *InvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
|
||||||
|
i.Lock()
|
||||||
|
defer i.Unlock()
|
||||||
|
|
||||||
|
log.Debugf("Canceling invoice %v", payHash)
|
||||||
|
|
||||||
|
invoice, err := i.cdb.CancelInvoice(payHash)
|
||||||
|
|
||||||
|
// Implement idempotency by returning success if the invoice was already
|
||||||
|
// canceled.
|
||||||
|
if err == channeldb.ErrInvoiceAlreadyCanceled {
|
||||||
|
log.Debugf("Invoice %v already canceled", payHash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Invoice %v canceled", payHash)
|
||||||
|
|
||||||
|
i.notifyClients(payHash, invoice, channeldb.ContractCanceled)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -150,6 +150,122 @@ func TestSettleInvoice(t *testing.T) {
|
|||||||
if inv.AmtPaid != amtPaid {
|
if inv.AmtPaid != amtPaid {
|
||||||
t.Fatal("expected amount to be unchanged")
|
t.Fatal("expected amount to be unchanged")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to cancel.
|
||||||
|
err = registry.CancelInvoice(hash)
|
||||||
|
if err != channeldb.ErrInvoiceAlreadySettled {
|
||||||
|
t.Fatal("expected cancelation of a settled invoice to fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCancelInvoice tests cancelation of an invoice and related notifications.
|
||||||
|
func TestCancelInvoice(t *testing.T) {
|
||||||
|
cdb, cleanup, err := newDB()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
// Instantiate and start the invoice registry.
|
||||||
|
registry := NewRegistry(cdb, &chaincfg.MainNetParams)
|
||||||
|
|
||||||
|
err = registry.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer registry.Stop()
|
||||||
|
|
||||||
|
allSubscriptions := registry.SubscribeNotifications(0, 0)
|
||||||
|
defer allSubscriptions.Cancel()
|
||||||
|
|
||||||
|
// Try to cancel the not yet existing invoice. This should fail.
|
||||||
|
err = registry.CancelInvoice(hash)
|
||||||
|
if err != channeldb.ErrInvoiceNotFound {
|
||||||
|
t.Fatalf("expected ErrInvoiceNotFound, but got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to the not yet existing invoice.
|
||||||
|
subscription := registry.SubscribeSingleInvoice(hash)
|
||||||
|
defer subscription.Cancel()
|
||||||
|
|
||||||
|
if subscription.hash != hash {
|
||||||
|
t.Fatalf("expected subscription for provided hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the invoice.
|
||||||
|
amt := lnwire.MilliSatoshi(100000)
|
||||||
|
invoice := &channeldb.Invoice{
|
||||||
|
Terms: channeldb.ContractTerm{
|
||||||
|
PaymentPreimage: preimage,
|
||||||
|
Value: amt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = registry.AddInvoice(invoice, hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the open state to be sent to the single invoice subscriber.
|
||||||
|
select {
|
||||||
|
case update := <-subscription.Updates:
|
||||||
|
if update.Terms.State != channeldb.ContractOpen {
|
||||||
|
t.Fatalf(
|
||||||
|
"expected state ContractOpen, but got %v",
|
||||||
|
update.Terms.State,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatal("no update received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect a new invoice notification to be sent out.
|
||||||
|
select {
|
||||||
|
case newInvoice := <-allSubscriptions.NewInvoices:
|
||||||
|
if newInvoice.Terms.State != channeldb.ContractOpen {
|
||||||
|
t.Fatalf(
|
||||||
|
"expected state ContractOpen, but got %v",
|
||||||
|
newInvoice.Terms.State,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatal("no update received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel invoice.
|
||||||
|
err = registry.CancelInvoice(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the canceled state to be sent to the single invoice
|
||||||
|
// subscriber.
|
||||||
|
select {
|
||||||
|
case update := <-subscription.Updates:
|
||||||
|
if update.Terms.State != channeldb.ContractCanceled {
|
||||||
|
t.Fatalf(
|
||||||
|
"expected state ContractCanceled, but got %v",
|
||||||
|
update.Terms.State,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatal("no update received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect no cancel notification to be sent to all invoice
|
||||||
|
// subscribers (backwards compatibility).
|
||||||
|
|
||||||
|
// Try to cancel again.
|
||||||
|
err = registry.CancelInvoice(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("expected cancelation of a canceled invoice to succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to settle. This should not be possible.
|
||||||
|
err = registry.SettleInvoice(hash, amt)
|
||||||
|
if err != channeldb.ErrInvoiceAlreadyCanceled {
|
||||||
|
t.Fatal("expected settlement of a canceled invoice to fail")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDB() (*channeldb.DB, func(), error) {
|
func newDB() (*channeldb.DB, func(), error) {
|
||||||
|
@ -25,6 +25,80 @@ var _ = math.Inf
|
|||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type CancelInvoiceMsg struct {
|
||||||
|
// / Hash corresponding to the invoice to cancel.
|
||||||
|
PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CancelInvoiceMsg) Reset() { *m = CancelInvoiceMsg{} }
|
||||||
|
func (m *CancelInvoiceMsg) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*CancelInvoiceMsg) ProtoMessage() {}
|
||||||
|
func (*CancelInvoiceMsg) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_invoices_1b708c9c030aea0e, []int{0}
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceMsg) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_CancelInvoiceMsg.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_CancelInvoiceMsg.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *CancelInvoiceMsg) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CancelInvoiceMsg.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceMsg) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_CancelInvoiceMsg.Size(m)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceMsg) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CancelInvoiceMsg.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CancelInvoiceMsg proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *CancelInvoiceMsg) GetPaymentHash() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PaymentHash
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancelInvoiceResp struct {
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CancelInvoiceResp) Reset() { *m = CancelInvoiceResp{} }
|
||||||
|
func (m *CancelInvoiceResp) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*CancelInvoiceResp) ProtoMessage() {}
|
||||||
|
func (*CancelInvoiceResp) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_invoices_1b708c9c030aea0e, []int{1}
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceResp) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_CancelInvoiceResp.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_CancelInvoiceResp.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *CancelInvoiceResp) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CancelInvoiceResp.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceResp) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_CancelInvoiceResp.Size(m)
|
||||||
|
}
|
||||||
|
func (m *CancelInvoiceResp) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CancelInvoiceResp.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CancelInvoiceResp proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*CancelInvoiceMsg)(nil), "invoicesrpc.CancelInvoiceMsg")
|
||||||
|
proto.RegisterType((*CancelInvoiceResp)(nil), "invoicesrpc.CancelInvoiceResp")
|
||||||
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ context.Context
|
var _ context.Context
|
||||||
var _ grpc.ClientConn
|
var _ grpc.ClientConn
|
||||||
@ -42,6 +116,11 @@ type InvoicesClient interface {
|
|||||||
// to notify the client of state transitions of the specified invoice.
|
// to notify the client of state transitions of the specified invoice.
|
||||||
// Initially the current invoice state is always sent out.
|
// Initially the current invoice state is always sent out.
|
||||||
SubscribeSingleInvoice(ctx context.Context, in *lnrpc.PaymentHash, opts ...grpc.CallOption) (Invoices_SubscribeSingleInvoiceClient, error)
|
SubscribeSingleInvoice(ctx context.Context, in *lnrpc.PaymentHash, opts ...grpc.CallOption) (Invoices_SubscribeSingleInvoiceClient, error)
|
||||||
|
// *
|
||||||
|
// CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||||
|
// canceled, this call will succeed. If the invoice is already settled, it will
|
||||||
|
// fail.
|
||||||
|
CancelInvoice(ctx context.Context, in *CancelInvoiceMsg, opts ...grpc.CallOption) (*CancelInvoiceResp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type invoicesClient struct {
|
type invoicesClient struct {
|
||||||
@ -84,6 +163,15 @@ func (x *invoicesSubscribeSingleInvoiceClient) Recv() (*lnrpc.Invoice, error) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *invoicesClient) CancelInvoice(ctx context.Context, in *CancelInvoiceMsg, opts ...grpc.CallOption) (*CancelInvoiceResp, error) {
|
||||||
|
out := new(CancelInvoiceResp)
|
||||||
|
err := c.cc.Invoke(ctx, "/invoicesrpc.Invoices/CancelInvoice", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// InvoicesServer is the server API for Invoices service.
|
// InvoicesServer is the server API for Invoices service.
|
||||||
type InvoicesServer interface {
|
type InvoicesServer interface {
|
||||||
// *
|
// *
|
||||||
@ -91,6 +179,11 @@ type InvoicesServer interface {
|
|||||||
// to notify the client of state transitions of the specified invoice.
|
// to notify the client of state transitions of the specified invoice.
|
||||||
// Initially the current invoice state is always sent out.
|
// Initially the current invoice state is always sent out.
|
||||||
SubscribeSingleInvoice(*lnrpc.PaymentHash, Invoices_SubscribeSingleInvoiceServer) error
|
SubscribeSingleInvoice(*lnrpc.PaymentHash, Invoices_SubscribeSingleInvoiceServer) error
|
||||||
|
// *
|
||||||
|
// CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||||
|
// canceled, this call will succeed. If the invoice is already settled, it will
|
||||||
|
// fail.
|
||||||
|
CancelInvoice(context.Context, *CancelInvoiceMsg) (*CancelInvoiceResp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterInvoicesServer(s *grpc.Server, srv InvoicesServer) {
|
func RegisterInvoicesServer(s *grpc.Server, srv InvoicesServer) {
|
||||||
@ -118,10 +211,33 @@ func (x *invoicesSubscribeSingleInvoiceServer) Send(m *lnrpc.Invoice) error {
|
|||||||
return x.ServerStream.SendMsg(m)
|
return x.ServerStream.SendMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Invoices_CancelInvoice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CancelInvoiceMsg)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(InvoicesServer).CancelInvoice(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/invoicesrpc.Invoices/CancelInvoice",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(InvoicesServer).CancelInvoice(ctx, req.(*CancelInvoiceMsg))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
var _Invoices_serviceDesc = grpc.ServiceDesc{
|
var _Invoices_serviceDesc = grpc.ServiceDesc{
|
||||||
ServiceName: "invoicesrpc.Invoices",
|
ServiceName: "invoicesrpc.Invoices",
|
||||||
HandlerType: (*InvoicesServer)(nil),
|
HandlerType: (*InvoicesServer)(nil),
|
||||||
Methods: []grpc.MethodDesc{},
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "CancelInvoice",
|
||||||
|
Handler: _Invoices_CancelInvoice_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
StreamName: "SubscribeSingleInvoice",
|
StreamName: "SubscribeSingleInvoice",
|
||||||
@ -133,21 +249,25 @@ var _Invoices_serviceDesc = grpc.ServiceDesc{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterFile("invoicesrpc/invoices.proto", fileDescriptor_invoices_c6414974947f2940)
|
proto.RegisterFile("invoicesrpc/invoices.proto", fileDescriptor_invoices_1b708c9c030aea0e)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_invoices_c6414974947f2940 = []byte{
|
var fileDescriptor_invoices_1b708c9c030aea0e = []byte{
|
||||||
// 177 bytes of a gzipped FileDescriptorProto
|
// 246 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0xb1, 0x8e, 0xc2, 0x30,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xbf, 0x4b, 0x43, 0x31,
|
||||||
0x10, 0x44, 0x75, 0xcd, 0xe9, 0x2e, 0x27, 0x5d, 0xe1, 0x82, 0xc2, 0xe2, 0x1b, 0xb2, 0x40, 0x7a,
|
0x10, 0xc7, 0x79, 0x8b, 0x68, 0x5a, 0x45, 0x23, 0x88, 0x04, 0x15, 0xed, 0xe4, 0x94, 0xa8, 0xc5,
|
||||||
0x0a, 0x2a, 0xa0, 0x42, 0x4a, 0x47, 0x67, 0x1b, 0xcb, 0x59, 0xe1, 0xec, 0x5a, 0xce, 0x06, 0xc4,
|
0xd5, 0x41, 0x17, 0x1d, 0x14, 0x69, 0x37, 0x17, 0xc9, 0x8b, 0x21, 0x39, 0x4c, 0xef, 0x42, 0x92,
|
||||||
0xdf, 0x23, 0x82, 0x91, 0xd2, 0x8d, 0x66, 0xe6, 0x49, 0xaf, 0xd2, 0x48, 0x37, 0x46, 0xe7, 0x87,
|
0x2a, 0xfe, 0x2b, 0xfe, 0xb5, 0xd2, 0x36, 0xe2, 0x7b, 0x42, 0xb7, 0xcb, 0x7d, 0x7f, 0xe4, 0x93,
|
||||||
0x9c, 0x1c, 0x7c, 0x72, 0x9d, 0x32, 0x0b, 0xab, 0xbf, 0xd9, 0xa6, 0x97, 0x81, 0x39, 0x44, 0x0f,
|
0x30, 0x01, 0xf8, 0x41, 0x60, 0x6c, 0x4e, 0xd1, 0xa8, 0xdf, 0x59, 0xc6, 0x44, 0x85, 0xf8, 0xa0,
|
||||||
0x26, 0x21, 0x18, 0x22, 0x16, 0x23, 0xc8, 0x54, 0xae, 0xfa, 0x37, 0x27, 0xf7, 0x8e, 0x9b, 0x63,
|
0xa3, 0x89, 0x23, 0x47, 0xe4, 0x82, 0x55, 0x3a, 0x82, 0xd2, 0x88, 0x54, 0x74, 0x01, 0xc2, 0x6a,
|
||||||
0xf5, 0x73, 0x28, 0x9c, 0xda, 0x56, 0x8b, 0x76, 0xb4, 0x83, 0xcb, 0x68, 0x7d, 0x8b, 0x14, 0xa2,
|
0x15, 0x5b, 0x29, 0x9a, 0xd5, 0x38, 0xba, 0x66, 0xbb, 0x77, 0x1a, 0x8d, 0x0d, 0x0f, 0xab, 0xf4,
|
||||||
0x2f, 0x93, 0x52, 0x75, 0xa4, 0x17, 0x73, 0x32, 0x8f, 0xde, 0x93, 0xec, 0xcd, 0xd0, 0xe9, 0xff,
|
0x63, 0x76, 0xfc, 0x8c, 0x0d, 0xa3, 0xfe, 0x9a, 0x59, 0x2c, 0xaf, 0x5e, 0x67, 0x7f, 0xd8, 0x9c,
|
||||||
0xd2, 0x95, 0xcf, 0xea, 0x6b, 0xd7, 0x9c, 0xd7, 0x01, 0xa5, 0x1b, 0x6d, 0xed, 0xb8, 0x87, 0x88,
|
0x36, 0xe7, 0xc3, 0xc9, 0xa0, 0xee, 0xee, 0x75, 0xf6, 0xa3, 0x7d, 0xb6, 0xd7, 0x8b, 0x4d, 0x6c,
|
||||||
0xa1, 0x13, 0x42, 0x0a, 0xe4, 0xe5, 0xce, 0xf9, 0x0a, 0x91, 0x2e, 0x30, 0x21, 0x30, 0x33, 0xb5,
|
0x8e, 0x57, 0xdf, 0x0d, 0xdb, 0xac, 0xe7, 0xcc, 0x6f, 0xd8, 0xc1, 0x74, 0xde, 0x66, 0x93, 0xa0,
|
||||||
0xdf, 0x93, 0x47, 0xf3, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x4e, 0x97, 0xf2, 0xdb, 0x00, 0x00,
|
0xb5, 0x53, 0x40, 0x17, 0x6c, 0x95, 0x38, 0x97, 0x01, 0x17, 0x00, 0xcf, 0x7f, 0x7d, 0x62, 0xa7,
|
||||||
0x00,
|
0xee, 0xaa, 0xe7, 0xa2, 0xe1, 0x4f, 0x6c, 0xbb, 0x77, 0x03, 0x3f, 0x96, 0x9d, 0x07, 0xca, 0xff,
|
||||||
|
0xd0, 0xe2, 0x64, 0xbd, 0xbc, 0x80, 0xbb, 0x1d, 0xbf, 0x5c, 0x3a, 0x28, 0x7e, 0xde, 0x4a, 0x43,
|
||||||
|
0x33, 0x15, 0xc0, 0xf9, 0x82, 0x80, 0x0e, 0x6d, 0xf9, 0xa4, 0xf4, 0xae, 0x02, 0xbe, 0xa9, 0x25,
|
||||||
|
0x82, 0xea, 0xd4, 0xb4, 0x1b, 0xcb, 0x4f, 0x1a, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xc3,
|
||||||
|
0x7e, 0x3a, 0x78, 0x01, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,17 @@ service Invoices {
|
|||||||
Initially the current invoice state is always sent out.
|
Initially the current invoice state is always sent out.
|
||||||
*/
|
*/
|
||||||
rpc SubscribeSingleInvoice (lnrpc.PaymentHash) returns (stream lnrpc.Invoice);
|
rpc SubscribeSingleInvoice (lnrpc.PaymentHash) returns (stream lnrpc.Invoice);
|
||||||
|
|
||||||
|
/**
|
||||||
|
CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||||
|
canceled, this call will succeed. If the invoice is already settled, it will
|
||||||
|
fail.
|
||||||
|
*/
|
||||||
|
rpc CancelInvoice(CancelInvoiceMsg) returns (CancelInvoiceResp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CancelInvoiceMsg {
|
||||||
|
/// Hash corresponding to the invoice to cancel.
|
||||||
|
bytes payment_hash = 1;
|
||||||
|
}
|
||||||
|
message CancelInvoiceResp {}
|
||||||
|
@ -8,10 +8,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -26,6 +27,10 @@ var (
|
|||||||
// macaroonOps are the set of capabilities that our minted macaroon (if
|
// macaroonOps are the set of capabilities that our minted macaroon (if
|
||||||
// it doesn't already exist) will have.
|
// it doesn't already exist) will have.
|
||||||
macaroonOps = []bakery.Op{
|
macaroonOps = []bakery.Op{
|
||||||
|
{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "write",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Entity: "invoices",
|
Entity: "invoices",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
@ -38,6 +43,10 @@ var (
|
|||||||
Entity: "invoices",
|
Entity: "invoices",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
}},
|
}},
|
||||||
|
"/invoicesrpc.Invoices/CancelInvoice": {{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultInvoicesMacFilename is the default name of the invoices
|
// DefaultInvoicesMacFilename is the default name of the invoices
|
||||||
@ -181,3 +190,24 @@ func (s *Server) SubscribeSingleInvoice(req *lnrpc.PaymentHash,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||||
|
// canceled, this call will succeed. If the invoice is already settled, it will
|
||||||
|
// fail.
|
||||||
|
func (s *Server) CancelInvoice(ctx context.Context,
|
||||||
|
in *CancelInvoiceMsg) (*CancelInvoiceResp, error) {
|
||||||
|
|
||||||
|
paymentHash, err := lntypes.NewHash(in.PaymentHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.cfg.InvoiceRegistry.CancelInvoice(*paymentHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Canceled invoice %v", paymentHash)
|
||||||
|
|
||||||
|
return &CancelInvoiceResp{}, nil
|
||||||
|
}
|
||||||
|
@ -59,8 +59,11 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
state = lnrpc.Invoice_OPEN
|
state = lnrpc.Invoice_OPEN
|
||||||
case channeldb.ContractSettled:
|
case channeldb.ContractSettled:
|
||||||
state = lnrpc.Invoice_SETTLED
|
state = lnrpc.Invoice_SETTLED
|
||||||
|
case channeldb.ContractCanceled:
|
||||||
|
state = lnrpc.Invoice_CANCELED
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown invoice state")
|
return nil, fmt.Errorf("unknown invoice state %v",
|
||||||
|
invoice.Terms.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lnrpc.Invoice{
|
return &lnrpc.Invoice{
|
||||||
|
1127
lnrpc/rpc.pb.go
1127
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load Diff
@ -1850,6 +1850,7 @@ message Invoice {
|
|||||||
enum InvoiceState {
|
enum InvoiceState {
|
||||||
OPEN = 0;
|
OPEN = 0;
|
||||||
SETTLED = 1;
|
SETTLED = 1;
|
||||||
|
CANCELED = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1141,7 +1141,8 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"OPEN",
|
"OPEN",
|
||||||
"SETTLED"
|
"SETTLED",
|
||||||
|
"CANCELED"
|
||||||
],
|
],
|
||||||
"default": "OPEN"
|
"default": "OPEN"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user