mirror of
https://github.com/getAlby/lndhub.go.git
synced 2025-12-18 13:14:56 +01:00
286 lines
8.2 KiB
Go
286 lines
8.2 KiB
Go
package integration_tests
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"log"
|
|
"math/big"
|
|
"time"
|
|
|
|
btcec "github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/getAlby/lndhub.go/lnd"
|
|
"github.com/labstack/gommon/random"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/zpay32"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type MockLND struct {
|
|
Sub *MockSubscribeInvoices
|
|
fee int64
|
|
privKey *btcec.PrivateKey
|
|
pubKey *btcec.PublicKey
|
|
addIndexCounter uint64
|
|
GetInfoError error
|
|
}
|
|
|
|
func NewMockLND(privkey string, fee int64, invoiceChan chan (*lnrpc.Invoice)) (*MockLND, error) {
|
|
privKeyBytes, err := hex.DecodeString(privkey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
privKey, pubKey := btcec.PrivKeyFromBytes(privKeyBytes)
|
|
return &MockLND{
|
|
Sub: &MockSubscribeInvoices{
|
|
invoiceChan: invoiceChan,
|
|
},
|
|
fee: fee,
|
|
privKey: privKey,
|
|
pubKey: pubKey,
|
|
addIndexCounter: 0,
|
|
}, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) signMsg(msg []byte) ([]byte, error) {
|
|
hash := sha256.Sum256(msg)
|
|
return ecdsa.SignCompact(mlnd.privKey, hash[:], true)
|
|
}
|
|
|
|
type MockSubscribeInvoices struct {
|
|
invoiceChan chan (*lnrpc.Invoice)
|
|
}
|
|
|
|
func (mockSub *MockSubscribeInvoices) Recv() (*lnrpc.Invoice, error) {
|
|
inv := <-mockSub.invoiceChan
|
|
return inv, nil
|
|
}
|
|
func (mlnd *MockLND) SubscribePayment(ctx context.Context, req *routerrpc.TrackPaymentRequest, options ...grpc.CallOption) (lnd.SubscribePaymentWrapper, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) ListChannels(ctx context.Context, req *lnrpc.ListChannelsRequest, options ...grpc.CallOption) (*lnrpc.ListChannelsResponse, error) {
|
|
return &lnrpc.ListChannelsResponse{
|
|
Channels: []*lnrpc.Channel{},
|
|
}, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) SendPaymentSync(ctx context.Context, req *lnrpc.SendRequest, options ...grpc.CallOption) (*lnrpc.SendResponse, error) {
|
|
return &lnrpc.SendResponse{
|
|
PaymentError: "",
|
|
PaymentPreimage: []byte("preimage"),
|
|
PaymentRoute: &lnrpc.Route{
|
|
TotalTimeLock: 0,
|
|
TotalFees: mlnd.fee,
|
|
TotalAmt: req.Amt + mlnd.fee,
|
|
Hops: []*lnrpc.Hop{},
|
|
TotalFeesMsat: 1000 * mlnd.fee,
|
|
TotalAmtMsat: 1000 * (req.Amt + mlnd.fee),
|
|
},
|
|
PaymentHash: req.PaymentHash,
|
|
}, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) AddInvoice(ctx context.Context, req *lnrpc.Invoice, options ...grpc.CallOption) (*lnrpc.AddInvoiceResponse, error) {
|
|
pHash := sha256.New()
|
|
pHash.Write(req.RPreimage)
|
|
pHash.Sum(nil)
|
|
msat := lnwire.MilliSatoshi(1000 * req.Value)
|
|
invoice := &zpay32.Invoice{
|
|
Net: &chaincfg.RegressionNetParams,
|
|
MilliSat: &msat,
|
|
Timestamp: time.Now(),
|
|
PaymentHash: &[32]byte{},
|
|
PaymentAddr: &[32]byte{},
|
|
Features: &lnwire.FeatureVector{
|
|
RawFeatureVector: &lnwire.RawFeatureVector{},
|
|
},
|
|
FallbackAddr: nil,
|
|
}
|
|
zpay32.Expiry(time.Duration(req.Expiry))(invoice)
|
|
copy(invoice.PaymentHash[:], pHash.Sum(nil))
|
|
copy(invoice.PaymentAddr[:], req.PaymentAddr)
|
|
if len(req.DescriptionHash) != 0 {
|
|
invoice.DescriptionHash = &[32]byte{}
|
|
copy(req.DescriptionHash, invoice.DescriptionHash[:])
|
|
}
|
|
if req.Memo != "" {
|
|
invoice.Description = &req.Memo
|
|
}
|
|
pr, err := invoice.Encode(zpay32.MessageSigner{
|
|
SignCompact: mlnd.signMsg,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mlnd.addIndexCounter += 1
|
|
return &lnrpc.AddInvoiceResponse{
|
|
RHash: invoice.PaymentHash[:],
|
|
PaymentRequest: pr,
|
|
AddIndex: mlnd.addIndexCounter,
|
|
}, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) mockPaidInvoice(added *ExpectedAddInvoiceResponseBody, amtPaid int64, keysend bool, htlc *lnrpc.InvoiceHTLC) error {
|
|
var incoming *lnrpc.Invoice
|
|
if !keysend {
|
|
rhash, err := hex.DecodeString(added.RHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
inv, err := mlnd.DecodeBolt11(context.Background(), added.PayReq)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
incoming = &lnrpc.Invoice{
|
|
Memo: inv.Description,
|
|
RPreimage: []byte("123preimage"),
|
|
RHash: rhash,
|
|
Value: inv.NumSatoshis,
|
|
ValueMsat: inv.NumMsat,
|
|
Settled: true,
|
|
CreationDate: time.Now().Unix(),
|
|
SettleDate: time.Now().Unix(),
|
|
PaymentRequest: added.PayReq,
|
|
DescriptionHash: []byte(inv.DescriptionHash),
|
|
FallbackAddr: inv.FallbackAddr,
|
|
CltvExpiry: uint64(inv.CltvExpiry),
|
|
AmtPaid: inv.NumSatoshis,
|
|
AmtPaidSat: inv.NumSatoshis,
|
|
AmtPaidMsat: inv.NumMsat,
|
|
State: lnrpc.Invoice_SETTLED,
|
|
Htlcs: []*lnrpc.InvoiceHTLC{},
|
|
IsKeysend: keysend,
|
|
}
|
|
} else {
|
|
preimage, err := makePreimageHex()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pHash := sha256.New()
|
|
pHash.Write(preimage)
|
|
incoming = &lnrpc.Invoice{
|
|
Memo: "",
|
|
RPreimage: preimage,
|
|
RHash: pHash.Sum(nil),
|
|
Value: amtPaid,
|
|
ValueMsat: 1000 * amtPaid,
|
|
Settled: true,
|
|
CreationDate: time.Now().Unix(),
|
|
SettleDate: time.Now().Unix(),
|
|
PaymentRequest: "",
|
|
AmtPaid: amtPaid,
|
|
AmtPaidSat: amtPaid,
|
|
AmtPaidMsat: 1000 * amtPaid,
|
|
State: lnrpc.Invoice_SETTLED,
|
|
Htlcs: []*lnrpc.InvoiceHTLC{htlc},
|
|
IsKeysend: keysend,
|
|
}
|
|
}
|
|
|
|
if amtPaid != 0 {
|
|
incoming.AmtPaidSat = amtPaid
|
|
incoming.AmtPaidMsat = 1000 * amtPaid
|
|
}
|
|
mlnd.Sub.invoiceChan <- incoming
|
|
return nil
|
|
}
|
|
|
|
func (mlnd *MockLND) SubscribeInvoices(ctx context.Context, req *lnrpc.InvoiceSubscription, options ...grpc.CallOption) (lnd.SubscribeInvoicesWrapper, error) {
|
|
return mlnd.Sub, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) GetInfo(ctx context.Context, req *lnrpc.GetInfoRequest, options ...grpc.CallOption) (*lnrpc.GetInfoResponse, error) {
|
|
if mlnd.GetInfoError != nil {
|
|
return nil, mlnd.GetInfoError
|
|
}
|
|
return &lnrpc.GetInfoResponse{
|
|
Version: "v1.0.0",
|
|
CommitHash: "abc123",
|
|
IdentityPubkey: hex.EncodeToString(mlnd.pubKey.SerializeCompressed()),
|
|
Alias: "Mocky McMockface",
|
|
Color: "",
|
|
NumPendingChannels: 1,
|
|
NumActiveChannels: 10,
|
|
NumInactiveChannels: 3,
|
|
NumPeers: 10,
|
|
BlockHeight: 1000,
|
|
BlockHash: "hashhashash",
|
|
BestHeaderTimestamp: 123456,
|
|
SyncedToChain: true,
|
|
SyncedToGraph: true,
|
|
Testnet: false,
|
|
Chains: []*lnrpc.Chain{{
|
|
Chain: "BTC",
|
|
Network: "regtest",
|
|
}},
|
|
Uris: []string{"https://mocky.mcmockface.com"},
|
|
Features: map[uint32]*lnrpc.Feature{},
|
|
}, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) TrackPayment(ctx context.Context, hash []byte, options ...grpc.CallOption) (*lnrpc.Payment, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) {
|
|
inv, err := zpay32.Decode(bolt11, &chaincfg.RegressionNetParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := &lnrpc.PayReq{
|
|
Destination: hex.EncodeToString(inv.Destination.SerializeCompressed()),
|
|
PaymentHash: hex.EncodeToString(inv.PaymentHash[:]),
|
|
NumSatoshis: int64(*inv.MilliSat) / 1000,
|
|
Timestamp: inv.Timestamp.Unix(),
|
|
Expiry: int64(inv.Expiry()),
|
|
Description: *inv.Description,
|
|
CltvExpiry: int64(inv.MinFinalCLTVExpiry()),
|
|
RouteHints: []*lnrpc.RouteHint{},
|
|
PaymentAddr: []byte{},
|
|
NumMsat: int64(*inv.MilliSat),
|
|
Features: map[uint32]*lnrpc.Feature{},
|
|
}
|
|
if inv.DescriptionHash != nil {
|
|
result.DescriptionHash = string(inv.DescriptionHash[:])
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (mlnd *MockLND) IsIdentityPubkey(pubkey string) (isOurPubkey bool) {
|
|
return pubkey == hex.EncodeToString(mlnd.pubKey.SerializeCompressed())
|
|
}
|
|
|
|
func (mlnd *MockLND) GetMainPubkey() (pubkey string) {
|
|
return hex.EncodeToString(mlnd.pubKey.SerializeCompressed())
|
|
}
|
|
|
|
func makePreimageHex() ([]byte, error) {
|
|
return randBytesFromStr(32, random.Hex)
|
|
}
|
|
func randBytesFromStr(length int, from string) ([]byte, error) {
|
|
b := make([]byte, length)
|
|
fromLenBigInt := big.NewInt(int64(len(from)))
|
|
for i := range b {
|
|
r, err := rand.Int(rand.Reader, fromLenBigInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b[i] = from[r.Int64()]
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func newDefaultMockLND() *MockLND {
|
|
mockLND, err := NewMockLND("1234567890abcdef", 0, make(chan (*lnrpc.Invoice)))
|
|
if err != nil {
|
|
log.Fatalf("Error initializing test service: %v", err)
|
|
}
|
|
return mockLND
|
|
}
|