• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

singnet / snet-daemon / #658

17 Aug 2025 09:40PM UTC coverage: 36.256%. First build
#658

Pull #642

semyon-dev
fix: tests
Pull Request #642: release v6.1.0

353 of 631 new or added lines in 43 files covered. (55.94%)

5681 of 15669 relevant lines covered (36.26%)

3.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/handler/unary_interceptor.go
1
package handler
2

3
import (
4
        "context"
5
        "fmt"
6
        "strings"
7

8
        "github.com/ethereum/go-ethereum/common"
9
        "github.com/singnet/snet-daemon/v6/blockchain"
10
        "github.com/singnet/snet-daemon/v6/ctxkeys"
11
        "go.uber.org/zap"
12
        "google.golang.org/grpc"
13
        "google.golang.org/grpc/codes"
14
        "google.golang.org/grpc/metadata"
15
)
16

17
type GrpcUnaryContext struct {
18
        MD   metadata.MD
19
        Info *grpc.UnaryServerInfo
20
}
21

22
// SenderProvider allows retrieving the sender's Ethereum address,
23
// independent of the specific type from pkg/escrow.
24
type SenderProvider interface {
25
        GetSender() common.Address
26
}
27

28
type UnaryPaymentHandler interface {
29
        // Type is a content of PaymentTypeHeader field which triggers usage of the
30
        // payment handler.
31
        Type() (typ string)
32
        // Payment extracts payment data from gRPC request context and checks
33
        // validity of payment data. It returns nil if data is valid or
34
        // appropriate gRPC status otherwise.
35
        Payment(context *GrpcUnaryContext) (payment Payment, err *GrpcError)
36
        // Complete completes payment if gRPC call was successfully proceeded by
37
        // service.
38
        Complete(payment Payment) (err *GrpcError)
39
        // CompleteAfterError completes payment if service returns error.
40
        CompleteAfterError(payment Payment, result error) (err *GrpcError)
41
}
42

43
type paymentValidationUnaryInterceptor struct {
44
        serviceMetadata       *blockchain.ServiceMetadata
45
        defaultPaymentHandler UnaryPaymentHandler
46
        paymentHandlers       map[string]UnaryPaymentHandler
47
}
48

49
func (interceptor *paymentValidationUnaryInterceptor) unaryIntercept(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, e error) {
×
50
        var err *GrpcError
×
51

×
NEW
52
        ctx = context.WithValue(ctx, ctxkeys.MethodKey, info.FullMethod)
×
53

×
54
        lastSlash := strings.LastIndex(info.FullMethod, "/")
×
55
        methodName := info.FullMethod[lastSlash+1:]
×
56

×
NEW
57
        md, ok := metadata.FromIncomingContext(ctx)
×
NEW
58
        if !ok {
×
NEW
59
                zap.L().Error("Invalid metadata", zap.Any("info", info))
×
NEW
60
                return nil, NewGrpcError(codes.InvalidArgument, "missing metadata").Err()
×
NEW
61
        }
×
62

63
        // pass non-training requests and free requests
64
        if methodName != "validate_model" && methodName != "train_model" {
×
NEW
65
                ctx = metadata.NewIncomingContext(ctx, md)
×
66
                resp, e := handler(ctx, req)
×
67
                if e != nil {
×
68
                        zap.L().Warn("gRPC handler returned error", zap.Error(e))
×
69
                        return resp, e
×
70
                }
×
71
                return resp, e
×
72
        }
73

NEW
74
        c := &GrpcUnaryContext{MD: md.Copy(), Info: info}
×
75

×
76
        zap.L().Debug("[unaryIntercept] grpc metadata", zap.Any("md", c.MD))
×
77
        zap.L().Debug("[unaryIntercept] New gRPC call received", zap.Any("context", c))
×
78

×
79
        paymentHandler, err := interceptor.getPaymentHandler(c)
×
80
        if err != nil {
×
81
                return nil, err.Err()
×
82
        }
×
83

84
        payment, err := paymentHandler.Payment(c)
×
85
        if err != nil {
×
86
                return nil, err.Err()
×
87
        }
×
88

NEW
89
        if sp, ok := payment.(SenderProvider); ok {
×
NEW
90
                outMD := c.MD.Copy()
×
NEW
91
                ethAddr := sp.GetSender().Hex()
×
NEW
92
                outMD.Set("user-address", ethAddr)
×
NEW
93
                outMD.Set("daemon-debug", "unaryIntercept")
×
NEW
94
                ctx = metadata.NewIncomingContext(ctx, outMD)
×
NEW
95
        }
×
96

97
        defer func() {
×
98
                if r := recover(); r != nil {
×
99
                        zap.L().Warn("Service handler called panic(panicValue)", zap.Any("panicValue", r))
×
100
                        paymentHandler.CompleteAfterError(payment, fmt.Errorf("service handler called panic(%v)", r))
×
101
                        panic("re-panic after payment handler error handling")
×
102
                } else if e == nil {
×
103
                        err = paymentHandler.Complete(payment)
×
104
                        if err != nil {
×
105
                                // return err.Err()
×
106
                                e = err.Err()
×
107
                        }
×
108
                } else {
×
109
                        err = paymentHandler.CompleteAfterError(payment, e)
×
110
                        if err != nil {
×
111
                                // return err.Err()
×
112
                                e = err.Err()
×
113
                        }
×
114
                }
115
        }()
116

117
        zap.L().Debug("[unaryIntercept] New payment received", zap.Any("payment", payment))
×
118

×
119
        resp, e = handler(ctx, req)
×
120
        if e != nil {
×
121
                zap.L().Warn("gRPC handler returned error", zap.Error(e))
×
122
                return resp, e
×
123
        }
×
124

125
        return resp, e
×
126
}
127

128
func (interceptor *paymentValidationUnaryInterceptor) getPaymentHandler(context *GrpcUnaryContext) (handler UnaryPaymentHandler, err *GrpcError) {
×
129
        paymentTypeMd, ok := context.MD[PaymentTypeHeader]
×
130
        if !ok || len(paymentTypeMd) == 0 {
×
131
                zap.L().Debug("Payment type was not set by caller, return default payment handler",
×
132
                        zap.String("defaultPaymentHandlerType", interceptor.defaultPaymentHandler.Type()))
×
133
                return interceptor.defaultPaymentHandler, nil
×
134
        }
×
135

136
        paymentType := paymentTypeMd[0]
×
137
        zap.L().Debug("Payment metadata", zap.String("paymentType", paymentType), zap.Any("paymentTypeMd", paymentTypeMd))
×
138
        paymentHandler, ok := interceptor.paymentHandlers[paymentType]
×
139
        if !ok {
×
140
                zap.L().Error("Unexpected payment type", zap.String("paymentType", paymentType))
×
141
                return nil, NewGrpcErrorf(codes.InvalidArgument, "unexpected \"%v\", value: \"%v\"", PaymentTypeHeader, paymentType)
×
142
        }
×
143

144
        zap.L().Debug("Return payment handler by type", zap.Any("paymentType", paymentType))
×
145
        return paymentHandler, nil
×
146
}
147

148
func GrpcPaymentValidationUnaryInterceptor(serviceData *blockchain.ServiceMetadata, defaultPaymentHandler UnaryPaymentHandler, paymentHandler ...UnaryPaymentHandler) grpc.UnaryServerInterceptor {
×
149
        interceptor := &paymentValidationUnaryInterceptor{
×
150
                defaultPaymentHandler: defaultPaymentHandler,
×
151
                paymentHandlers:       make(map[string]UnaryPaymentHandler),
×
152
                serviceMetadata:       serviceData,
×
153
        }
×
154

×
155
        interceptor.paymentHandlers[defaultPaymentHandler.Type()] = defaultPaymentHandler
×
156
        zap.L().Info("Default payment handler registered", zap.Any("defaultPaymentType", defaultPaymentHandler.Type()))
×
157
        for _, handler := range paymentHandler {
×
158
                interceptor.paymentHandlers[handler.Type()] = handler
×
159
                zap.L().Info("Payment handler for type registered", zap.Any("paymentType", handler.Type()))
×
160
        }
×
161
        return interceptor.unaryIntercept
×
162
}
163

164
// NoOpUnaryInterceptor is a gRPC interceptor which doesn't do payment checking.
165
func NoOpUnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
×
166
        return handler(ctx, req)
×
167
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc