lnd.xprv/routing/mock_test.go
yyforyongyu f31001e103
routing: make shardHandler aware of payment session
This commit adds payment session to shardHandler to enable private edge
policies being updated in shardHandler. The relevant interface and mock
are updated. From now on, upon seeing a ChannelUpdate message,
shardHandler will first try to find the target policy in additionalEdges
and update it. If nothing found, it will then check the database for
edge policy to update.
2021-06-23 18:13:04 +08:00

529 lines
11 KiB
Go

package routing
import (
"fmt"
"sync"
"github.com/btcsuite/btcd/btcec"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
type mockPaymentAttemptDispatcher struct {
onPayment func(firstHop lnwire.ShortChannelID) ([32]byte, error)
results map[uint64]*htlcswitch.PaymentResult
sync.Mutex
}
var _ PaymentAttemptDispatcher = (*mockPaymentAttemptDispatcher)(nil)
func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
pid uint64,
_ *lnwire.UpdateAddHTLC) error {
if m.onPayment == nil {
return nil
}
var result *htlcswitch.PaymentResult
preimage, err := m.onPayment(firstHop)
if err != nil {
rtErr, ok := err.(htlcswitch.ClearTextError)
if !ok {
return err
}
result = &htlcswitch.PaymentResult{
Error: rtErr,
}
} else {
result = &htlcswitch.PaymentResult{Preimage: preimage}
}
m.Lock()
if m.results == nil {
m.results = make(map[uint64]*htlcswitch.PaymentResult)
}
m.results[pid] = result
m.Unlock()
return nil
}
func (m *mockPaymentAttemptDispatcher) GetPaymentResult(paymentID uint64,
_ lntypes.Hash, _ htlcswitch.ErrorDecrypter) (
<-chan *htlcswitch.PaymentResult, error) {
c := make(chan *htlcswitch.PaymentResult, 1)
m.Lock()
res, ok := m.results[paymentID]
m.Unlock()
if !ok {
return nil, htlcswitch.ErrPaymentIDNotFound
}
c <- res
return c, nil
}
func (m *mockPaymentAttemptDispatcher) CleanStore(map[uint64]struct{}) error {
return nil
}
func (m *mockPaymentAttemptDispatcher) setPaymentResult(
f func(firstHop lnwire.ShortChannelID) ([32]byte, error)) {
m.onPayment = f
}
type mockPaymentSessionSource struct {
routes []*route.Route
routeRelease chan struct{}
}
var _ PaymentSessionSource = (*mockPaymentSessionSource)(nil)
func (m *mockPaymentSessionSource) NewPaymentSession(
_ *LightningPayment) (PaymentSession, error) {
return &mockPaymentSession{
routes: m.routes,
release: m.routeRelease,
}, nil
}
func (m *mockPaymentSessionSource) NewPaymentSessionForRoute(
preBuiltRoute *route.Route) PaymentSession {
return nil
}
func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession {
return &mockPaymentSession{}
}
type mockMissionControl struct {
MissionControl
}
var _ MissionController = (*mockMissionControl)(nil)
func (m *mockMissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
failureSourceIdx *int, failure lnwire.FailureMessage) (
*channeldb.FailureReason, error) {
// Report a permanent failure if this is an error caused
// by incorrect details.
if failure.Code() == lnwire.CodeIncorrectOrUnknownPaymentDetails {
reason := channeldb.FailureReasonPaymentDetails
return &reason, nil
}
return nil, nil
}
func (m *mockMissionControl) ReportPaymentSuccess(paymentID uint64,
rt *route.Route) error {
return nil
}
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
amt lnwire.MilliSatoshi) float64 {
return 0
}
type mockPaymentSession struct {
routes []*route.Route
// release is a channel that optionally blocks requesting a route
// from our mock payment channel. If this value is nil, we will just
// release the route automatically.
release chan struct{}
}
var _ PaymentSession = (*mockPaymentSession)(nil)
func (m *mockPaymentSession) RequestRoute(_, _ lnwire.MilliSatoshi,
_, height uint32) (*route.Route, error) {
if m.release != nil {
m.release <- struct{}{}
}
if len(m.routes) == 0 {
return nil, errNoPathFound
}
r := m.routes[0]
m.routes = m.routes[1:]
return r, nil
}
func (m *mockPaymentSession) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate,
_ *btcec.PublicKey, _ *channeldb.ChannelEdgePolicy) bool {
return false
}
func (m *mockPaymentSession) GetAdditionalEdgePolicy(_ *btcec.PublicKey,
_ uint64) *channeldb.ChannelEdgePolicy {
return nil
}
type mockPayer struct {
sendResult chan error
paymentResult chan *htlcswitch.PaymentResult
quit chan struct{}
}
var _ PaymentAttemptDispatcher = (*mockPayer)(nil)
func (m *mockPayer) SendHTLC(_ lnwire.ShortChannelID,
paymentID uint64,
_ *lnwire.UpdateAddHTLC) error {
select {
case res := <-m.sendResult:
return res
case <-m.quit:
return fmt.Errorf("test quitting")
}
}
func (m *mockPayer) GetPaymentResult(paymentID uint64, _ lntypes.Hash,
_ htlcswitch.ErrorDecrypter) (<-chan *htlcswitch.PaymentResult, error) {
select {
case res, ok := <-m.paymentResult:
resChan := make(chan *htlcswitch.PaymentResult, 1)
if !ok {
close(resChan)
} else {
resChan <- res
}
return resChan, nil
case <-m.quit:
return nil, fmt.Errorf("test quitting")
}
}
func (m *mockPayer) CleanStore(pids map[uint64]struct{}) error {
return nil
}
type initArgs struct {
c *channeldb.PaymentCreationInfo
}
type registerAttemptArgs struct {
a *channeldb.HTLCAttemptInfo
}
type settleAttemptArgs struct {
preimg lntypes.Preimage
}
type failAttemptArgs struct {
reason *channeldb.HTLCFailInfo
}
type failPaymentArgs struct {
reason channeldb.FailureReason
}
type testPayment struct {
info channeldb.PaymentCreationInfo
attempts []channeldb.HTLCAttempt
}
type mockControlTower struct {
payments map[lntypes.Hash]*testPayment
successful map[lntypes.Hash]struct{}
failed map[lntypes.Hash]channeldb.FailureReason
init chan initArgs
registerAttempt chan registerAttemptArgs
settleAttempt chan settleAttemptArgs
failAttempt chan failAttemptArgs
failPayment chan failPaymentArgs
fetchInFlight chan struct{}
sync.Mutex
}
var _ ControlTower = (*mockControlTower)(nil)
func makeMockControlTower() *mockControlTower {
return &mockControlTower{
payments: make(map[lntypes.Hash]*testPayment),
successful: make(map[lntypes.Hash]struct{}),
failed: make(map[lntypes.Hash]channeldb.FailureReason),
}
}
func (m *mockControlTower) InitPayment(phash lntypes.Hash,
c *channeldb.PaymentCreationInfo) error {
if m.init != nil {
m.init <- initArgs{c}
}
m.Lock()
defer m.Unlock()
// Don't allow re-init a successful payment.
if _, ok := m.successful[phash]; ok {
return channeldb.ErrAlreadyPaid
}
_, failed := m.failed[phash]
_, ok := m.payments[phash]
// If the payment is known, only allow re-init if failed.
if ok && !failed {
return channeldb.ErrPaymentInFlight
}
delete(m.failed, phash)
m.payments[phash] = &testPayment{
info: *c,
}
return nil
}
func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash,
a *channeldb.HTLCAttemptInfo) error {
if m.registerAttempt != nil {
m.registerAttempt <- registerAttemptArgs{a}
}
m.Lock()
defer m.Unlock()
// Lookup payment.
p, ok := m.payments[phash]
if !ok {
return channeldb.ErrPaymentNotInitiated
}
var inFlight bool
for _, a := range p.attempts {
if a.Settle != nil {
continue
}
if a.Failure != nil {
continue
}
inFlight = true
}
// Cannot register attempts for successful or failed payments.
_, settled := m.successful[phash]
_, failed := m.failed[phash]
if settled || failed {
return channeldb.ErrPaymentTerminal
}
if settled && !inFlight {
return channeldb.ErrPaymentAlreadySucceeded
}
if failed && !inFlight {
return channeldb.ErrPaymentAlreadyFailed
}
// Add attempt to payment.
p.attempts = append(p.attempts, channeldb.HTLCAttempt{
HTLCAttemptInfo: *a,
})
m.payments[phash] = p
return nil
}
func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
pid uint64, settleInfo *channeldb.HTLCSettleInfo) (
*channeldb.HTLCAttempt, error) {
if m.settleAttempt != nil {
m.settleAttempt <- settleAttemptArgs{settleInfo.Preimage}
}
m.Lock()
defer m.Unlock()
// Only allow setting attempts if the payment is known.
p, ok := m.payments[phash]
if !ok {
return nil, channeldb.ErrPaymentNotInitiated
}
// Find the attempt with this pid, and set the settle info.
for i, a := range p.attempts {
if a.AttemptID != pid {
continue
}
if a.Settle != nil {
return nil, channeldb.ErrAttemptAlreadySettled
}
if a.Failure != nil {
return nil, channeldb.ErrAttemptAlreadyFailed
}
p.attempts[i].Settle = settleInfo
// Mark the payment successful on first settled attempt.
m.successful[phash] = struct{}{}
return &channeldb.HTLCAttempt{
Settle: settleInfo,
}, nil
}
return nil, fmt.Errorf("pid not found")
}
func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) {
if m.failAttempt != nil {
m.failAttempt <- failAttemptArgs{failInfo}
}
m.Lock()
defer m.Unlock()
// Only allow failing attempts if the payment is known.
p, ok := m.payments[phash]
if !ok {
return nil, channeldb.ErrPaymentNotInitiated
}
// Find the attempt with this pid, and set the failure info.
for i, a := range p.attempts {
if a.AttemptID != pid {
continue
}
if a.Settle != nil {
return nil, channeldb.ErrAttemptAlreadySettled
}
if a.Failure != nil {
return nil, channeldb.ErrAttemptAlreadyFailed
}
p.attempts[i].Failure = failInfo
return &channeldb.HTLCAttempt{
Failure: failInfo,
}, nil
}
return nil, fmt.Errorf("pid not found")
}
func (m *mockControlTower) Fail(phash lntypes.Hash,
reason channeldb.FailureReason) error {
m.Lock()
defer m.Unlock()
if m.failPayment != nil {
m.failPayment <- failPaymentArgs{reason}
}
// Payment must be known.
if _, ok := m.payments[phash]; !ok {
return channeldb.ErrPaymentNotInitiated
}
m.failed[phash] = reason
return nil
}
func (m *mockControlTower) FetchPayment(phash lntypes.Hash) (
*channeldb.MPPayment, error) {
m.Lock()
defer m.Unlock()
return m.fetchPayment(phash)
}
func (m *mockControlTower) fetchPayment(phash lntypes.Hash) (
*channeldb.MPPayment, error) {
p, ok := m.payments[phash]
if !ok {
return nil, channeldb.ErrPaymentNotInitiated
}
mp := &channeldb.MPPayment{
Info: &p.info,
}
reason, ok := m.failed[phash]
if ok {
mp.FailureReason = &reason
}
// Return a copy of the current attempts.
mp.HTLCs = append(mp.HTLCs, p.attempts...)
return mp, nil
}
func (m *mockControlTower) FetchInFlightPayments() (
[]*channeldb.MPPayment, error) {
if m.fetchInFlight != nil {
m.fetchInFlight <- struct{}{}
}
m.Lock()
defer m.Unlock()
// In flight are all payments not successful or failed.
var fl []*channeldb.MPPayment
for hash := range m.payments {
if _, ok := m.successful[hash]; ok {
continue
}
if _, ok := m.failed[hash]; ok {
continue
}
mp, err := m.fetchPayment(hash)
if err != nil {
return nil, err
}
fl = append(fl, mp)
}
return fl, nil
}
func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) (
*ControlTowerSubscriber, error) {
return nil, errors.New("not implemented")
}