routing: return full htlc attempt from router

This commit is contained in:
Joost Jager 2020-05-06 15:36:51 +02:00
parent cc37485432
commit 674c199047
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
4 changed files with 44 additions and 29 deletions

View File

@ -318,25 +318,30 @@ func (s *Server) SendToRoute(ctx context.Context,
return nil, err return nil, err
} }
preimage, err := s.cfg.Router.SendToRoute(hash, route) // Pass route to the router. This call returns the full htlc attempt
// information as it is stored in the database. It is possible that both
// the attempt return value and err are non-nil. This can happen when
// the attempt was already initiated before the error happened. In that
// case, we give precedence to the attempt information as stored in the
// db.
attempt, err := s.cfg.Router.SendToRoute(hash, route)
if attempt != nil {
rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt(
*attempt,
)
if err != nil {
return nil, err
}
// In the success case, return the preimage. resp := &SendToRouteResponse{
if err == nil { Preimage: rpcAttempt.Preimage,
return &SendToRouteResponse{ Failure: rpcAttempt.Failure,
Preimage: preimage[:], }
}, nil
return resp, nil
} }
// In the failure case, marshall the failure message to the rpc format return nil, err
// before returning it to the caller.
rpcErr, err := marshallError(err)
if err != nil {
return nil, err
}
return &SendToRouteResponse{
Failure: rpcErr,
}, nil
} }
// ResetMissionControl clears all mission control state and starts with a clean // ResetMissionControl clears all mission control state and starts with a clean

View File

@ -1741,10 +1741,12 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) (
} }
// SendToRoute attempts to send a payment with the given hash through the // SendToRoute attempts to send a payment with the given hash through the
// provided route. This function is blocking and will return the obtained // provided route. This function is blocking and will return the attempt
// preimage if the payment is successful or the full error in case of a failure. // information as it is stored in the database. For a successful htlc, this
// information will contain the preimage. If an error occurs after the attempt
// was initiated, both return values will be non-nil.
func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) (
lntypes.Preimage, error) { *channeldb.HTLCAttempt, error) {
// Calculate amount paid to receiver. // Calculate amount paid to receiver.
amt := rt.ReceiverAmt() amt := rt.ReceiverAmt()
@ -1774,7 +1776,7 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) (
// Any other error is not tolerated. // Any other error is not tolerated.
case err != nil: case err != nil:
return [32]byte{}, err return nil, err
} }
log.Tracef("Dispatching SendToRoute for hash %v: %v", log.Tracef("Dispatching SendToRoute for hash %v: %v",
@ -1804,34 +1806,37 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) (
hash, channeldb.FailureReasonError, hash, channeldb.FailureReasonError,
) )
if controlErr != nil { if controlErr != nil {
return [32]byte{}, controlErr return nil, controlErr
} }
} }
// In any case, don't continue if there is an error. // In any case, don't continue if there is an error.
if err != nil { if err != nil {
return lntypes.Preimage{}, err return nil, err
} }
var htlcAttempt *channeldb.HTLCAttempt
switch { switch {
// Failed to launch shard. // Failed to launch shard.
case outcome.err != nil: case outcome.err != nil:
shardError = outcome.err shardError = outcome.err
htlcAttempt = outcome.attempt
// Shard successfully launched, wait for the result to be available. // Shard successfully launched, wait for the result to be available.
default: default:
result, err := sh.collectResult(attempt) result, err := sh.collectResult(attempt)
if err != nil { if err != nil {
return lntypes.Preimage{}, err return nil, err
} }
// We got a successful result. // We got a successful result.
if result.err == nil { if result.err == nil {
return result.attempt.Settle.Preimage, nil return result.attempt, nil
} }
// The shard failed, break switch to handle it. // The shard failed, break switch to handle it.
shardError = result.err shardError = result.err
htlcAttempt = result.attempt
} }
// Since for SendToRoute we won't retry in case the shard fails, we'll // Since for SendToRoute we won't retry in case the shard fails, we'll
@ -1848,10 +1853,10 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) (
err = r.cfg.Control.Fail(hash, *reason) err = r.cfg.Control.Fail(hash, *reason)
if err != nil { if err != nil {
return lntypes.Preimage{}, err return nil, err
} }
return lntypes.Preimage{}, shardError return htlcAttempt, shardError
} }
// sendPayment attempts to send a payment to the passed payment hash. This // sendPayment attempts to send a payment to the passed payment hash. This

View File

@ -2809,13 +2809,13 @@ func TestSendToRouteMultiShardSend(t *testing.T) {
for i := 0; i < numShards; i++ { for i := 0; i < numShards; i++ {
go func() { go func() {
preimg, err := ctx.router.SendToRoute(payment, rt) attempt, err := ctx.router.SendToRoute(payment, rt)
if err != nil { if err != nil {
errChan <- err errChan <- err
return return
} }
successes <- preimg successes <- attempt.Settle.Preimage
}() }()
} }

View File

@ -3978,10 +3978,15 @@ func (r *rpcServer) dispatchPaymentIntent(
payment, payment,
) )
} else { } else {
preImage, routerErr = r.server.chanRouter.SendToRoute( var attempt *channeldb.HTLCAttempt
attempt, routerErr = r.server.chanRouter.SendToRoute(
payIntent.rHash, payIntent.route, payIntent.rHash, payIntent.route,
) )
if routerErr == nil {
preImage = attempt.Settle.Preimage
}
route = payIntent.route route = payIntent.route
} }