Skip to content

Commit

Permalink
Merge pull request #766 from ellemouton/tracing
Browse files Browse the repository at this point in the history
accounts: address pendingPayments issues & add tracer
  • Loading branch information
ellemouton authored Jun 6, 2024
2 parents 04ef762 + 5e07588 commit 1e1a856
Show file tree
Hide file tree
Showing 10 changed files with 1,261 additions and 94 deletions.
270 changes: 234 additions & 36 deletions accounts/checkers.go

Large diffs are not rendered by default.

631 changes: 623 additions & 8 deletions accounts/checkers_test.go

Large diffs are not rendered by default.

57 changes: 54 additions & 3 deletions accounts/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package accounts
import (
"context"
"fmt"

"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
)

// ContextKey is the type that we use to identify account specific values in the
Expand All @@ -18,18 +21,23 @@ var (
// KeyAccount is the key under which we store the account in the request
// context.
KeyAccount = ContextKey{"account"}

// KeyRequestID is the key under which we store the middleware request
// ID.
KeyRequestID = ContextKey{"request_id"}
)

// FromContext tries to extract a value from the given context.
func FromContext(ctx context.Context, key ContextKey) interface{} {
return ctx.Value(key)
}

// AddToContext adds the given value to the context for easy retrieval later on.
func AddToContext(ctx context.Context, key ContextKey,
// AddAccountToContext adds the given value to the context for easy retrieval
// later on.
func AddAccountToContext(ctx context.Context,
value *OffChainBalanceAccount) context.Context {

return context.WithValue(ctx, key, value)
return context.WithValue(ctx, KeyAccount, value)
}

// AccountFromContext attempts to extract an account from the given context.
Expand All @@ -46,3 +54,46 @@ func AccountFromContext(ctx context.Context) (*OffChainBalanceAccount, error) {

return acct, nil
}

// AddRequestIDToContext adds the given request ID to the context for easy
// retrieval later on.
func AddRequestIDToContext(ctx context.Context, value uint64) context.Context {
return context.WithValue(ctx, KeyRequestID, value)
}

// RequestIDFromContext attempts to extract a request ID from the given context.
func RequestIDFromContext(ctx context.Context) (uint64, error) {
val := FromContext(ctx, KeyRequestID)
if val == nil {
return 0, fmt.Errorf("no request ID found in context")
}

reqID, ok := val.(uint64)
if !ok {
return 0, fmt.Errorf("invalid request ID value in context")
}

return reqID, nil
}

// requestScopedValuesFromCtx is a helper function that can be used to extract
// an account and requestID from the given context. It also creates a new
// prefixed logger that can be used by account request and response handlers.
// Each log line will be prefixed by the account ID and the request ID.
func requestScopedValuesFromCtx(ctx context.Context) (btclog.Logger,
*OffChainBalanceAccount, uint64, error) {

acc, err := AccountFromContext(ctx)
if err != nil {
return nil, nil, 0, err
}

reqID, err := RequestIDFromContext(ctx)
if err != nil {
return nil, nil, 0, err
}

prefix := fmt.Sprintf("[account: %s, request: %d]", acc.ID, reqID)

return build.NewPrefixLog(prefix, log), acc, reqID, nil
}
35 changes: 30 additions & 5 deletions accounts/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,10 @@ func (s *InterceptorService) Intercept(ctx context.Context,
)
}

// We now add the account to the incoming context to give each checker
// access to it if required.
ctxAccount := AddToContext(ctx, KeyAccount, acct)
// We now add the account and request ID to the incoming context to give
// each checker access to them if required.
ctx = AddAccountToContext(ctx, acct)
ctx = AddRequestIDToContext(ctx, req.RequestId)

switch r := req.InterceptType.(type) {
// In the authentication phase we just check that the account hasn't
Expand All @@ -120,18 +121,42 @@ func (s *InterceptorService) Intercept(ctx context.Context,
}

return mid.RPCErr(req, s.checkers.checkIncomingRequest(
ctxAccount, r.Request.MethodFullUri, msg,
ctx, r.Request.MethodFullUri, msg,
))

// Parse and possibly manipulate outgoing responses.
case *lnrpc.RPCMiddlewareRequest_Response:
if r.Response.IsError {
parsedErr := mid.ParseResponseErr(r.Response.Serialized)

replacementErr, err := s.checkers.handleErrorResponse(
ctx, r.Response.MethodFullUri, parsedErr,
)
if err != nil {
return mid.RPCErr(req, err)
}

// No error occurred but the response error should be
// replaced with the given custom error. Wrap it in the
// correct RPC response of the interceptor now.
if replacementErr != nil {
return mid.RPCErrReplacement(
req, replacementErr,
)
}

// No error and no replacement, just return an empty
// response of the correct type.
return mid.RPCOk(req)
}

msg, err := parseRPCMessage(r.Response)
if err != nil {
return mid.RPCErr(req, err)
}

replacement, err := s.checkers.replaceOutgoingResponse(
ctxAccount, r.Response.MethodFullUri, msg,
ctx, r.Response.MethodFullUri, msg,
)
if err != nil {
return mid.RPCErr(req, err)
Expand Down
39 changes: 39 additions & 0 deletions accounts/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func ParseAccountID(idStr string) (*AccountID, error) {
return &id, nil
}

// String returns the string representation of the AccountID.
func (a AccountID) String() string {
return hex.EncodeToString(a[:])
}

// PaymentEntry is the data we track per payment that is associated with an
// account. This basically includes all information required to make sure
// in-flight payments don't exceed the total available account balance.
Expand Down Expand Up @@ -252,4 +257,38 @@ type Service interface {
// restarted.
AssociatePayment(id AccountID, paymentHash lntypes.Hash,
fullAmt lnwire.MilliSatoshi) error

// PaymentErrored removes a pending payment from the accounts
// registered payment list. This should only ever be called if we are
// sure that the payment request errored out.
PaymentErrored(id AccountID, hash lntypes.Hash) error

RequestValuesStore
}

// RequestValues holds various values associated with a specific request that
// we may want access to when handling the response. At the moment this only
// stores payment related data.
type RequestValues struct {
// PaymentHash is the hash of the payment that this request is
// associated with.
PaymentHash lntypes.Hash

// PaymentAmount is the value of the payment being made.
PaymentAmount lnwire.MilliSatoshi
}

// RequestValuesStore is a store that can be used to keep track of the mapping
// between a request ID and various values associated with that request which
// we may want access to when handling the request response.
type RequestValuesStore interface {
// RegisterValues stores values for the given request ID.
RegisterValues(reqID uint64, values *RequestValues) error

// GetValues returns the corresponding request values for the given
// request ID if they exist.
GetValues(reqID uint64) (*RequestValues, bool)

// DeleteValues deletes any values stored for the given request ID.
DeleteValues(reqID uint64)
}
2 changes: 1 addition & 1 deletion accounts/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/lightningnetwork/lnd/build"
)

const Subsystem = "ACCT"
const Subsystem = "ACNT"

// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
Expand Down
Loading

0 comments on commit 1e1a856

Please sign in to comment.