-
Notifications
You must be signed in to change notification settings - Fork 180
/
baseapp_ibc_adapter.go
223 lines (190 loc) · 6.66 KB
/
baseapp_ibc_adapter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package baseapp
import (
"context"
"strconv"
gogogrpc "github.com/gogo/protobuf/grpc"
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"github.com/okex/exchain/libs/cosmos-sdk/codec/types"
sdk "github.com/okex/exchain/libs/cosmos-sdk/types"
sdkerrors "github.com/okex/exchain/libs/cosmos-sdk/types/errors"
grpctypes "github.com/okex/exchain/libs/cosmos-sdk/types/grpc"
abci "github.com/okex/exchain/libs/tendermint/abci/types"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
grpcstatus "google.golang.org/grpc/status"
)
// SetInterfaceRegistry sets the InterfaceRegistry.
func (app *BaseApp) SetInterfaceRegistry(registry types.InterfaceRegistry) {
app.interfaceRegistry = registry
app.grpcQueryRouter.SetInterfaceRegistry(registry)
app.msgServiceRouter.SetInterfaceRegistry(registry)
}
// MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal
// commit multi-store.
func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) {
for _, memKey := range keys {
app.MountStore(memKey, sdk.StoreTypeMemory)
}
}
func (app *BaseApp) handleQueryGRPC(handler GRPCQueryHandler, req abci.RequestQuery) abci.ResponseQuery {
ctx, err := app.createQueryContext(req.Height, req.Prove)
if err != nil {
return sdkerrors.QueryResult(err)
}
res, err := handler(ctx, req)
if err != nil {
res = sdkerrors.QueryResult(gRPCErrorToSDKError(err))
res.Height = req.Height
return res
}
return res
}
func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, error) {
if err := checkNegativeHeight(height); err != nil {
return sdk.Context{}, err
}
// when a client did not provide a query height, manually inject the latest
if height == 0 {
height = app.LastBlockHeight()
}
if height <= 1 && prove {
return sdk.Context{},
sdkerrors.Wrap(
sdkerrors.ErrInvalidRequest,
"cannot query with proof when height <= 1; please provide a valid height",
)
}
cacheMS, err := app.cms.CacheMultiStoreWithVersion(height)
if err != nil {
return sdk.Context{},
sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest,
"failed to load state at height %d; %s (latest height: %d)", height, err, app.LastBlockHeight(),
)
}
// branch the commit-multistore for safety
ctx := sdk.NewContext(
cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger,
)
ctx.SetMinGasPrices(app.minGasPrices)
return ctx, nil
}
func checkNegativeHeight(height int64) error {
if height < 0 {
// Reject invalid heights.
return sdkerrors.Wrap(
sdkerrors.ErrInvalidRequest,
"cannot query with height < 0; please provide a valid height",
)
}
return nil
}
func gRPCErrorToSDKError(err error) error {
status, ok := grpcstatus.FromError(err)
if !ok {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}
switch status.Code() {
case codes.NotFound:
return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, err.Error())
case codes.InvalidArgument:
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
case codes.FailedPrecondition:
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
case codes.Unauthenticated:
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, err.Error())
default:
return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, err.Error())
}
}
func (app *BaseApp) RegisterGRPCServer(server gogogrpc.Server) {
// Define an interceptor for all gRPC queries: this interceptor will create
// a new sdk.Context, and pass it into the query handler.
interceptor := func(grpcCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// If there's some metadata in the context, retrieve it.
md, ok := metadata.FromIncomingContext(grpcCtx)
if !ok {
return nil, status.Error(codes.Internal, "unable to retrieve metadata")
}
// Get height header from the request context, if present.
var height int64
if heightHeaders := md.Get(grpctypes.GRPCBlockHeightHeader); len(heightHeaders) == 1 {
height, err = strconv.ParseInt(heightHeaders[0], 10, 64)
if err != nil {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest,
"Baseapp.RegisterGRPCServer: invalid height header %q: %v", grpctypes.GRPCBlockHeightHeader, err)
}
if err := checkNegativeHeight(height); err != nil {
return nil, err
}
}
// Create the sdk.Context. Passing false as 2nd arg, as we can't
// actually support proofs with gRPC right now.
sdkCtx, err := app.createQueryContext(height, false)
if err != nil {
return nil, err
}
// Add relevant gRPC headers
if height == 0 {
height = sdkCtx.BlockHeight() // If height was not set in the request, set it to the latest
}
// Attach the sdk.Context into the gRPC's context.Context.
grpcCtx = context.WithValue(grpcCtx, sdk.SdkContextKey, sdkCtx)
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(height, 10))
if err = grpc.SetHeader(grpcCtx, md); err != nil {
app.logger.Error("failed to set gRPC header", "err", err)
}
return handler(grpcCtx, req)
}
// Loop through all services and methods, add the interceptor, and register
// the service.
for _, data := range app.GRPCQueryRouter().serviceData {
desc := data.serviceDesc
newMethods := make([]grpc.MethodDesc, len(desc.Methods))
for i, method := range desc.Methods {
methodHandler := method.Handler
newMethods[i] = grpc.MethodDesc{
MethodName: method.MethodName,
Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, _ grpc.UnaryServerInterceptor) (interface{}, error) {
return methodHandler(srv, ctx, dec, grpcmiddleware.ChainUnaryServer(
grpcrecovery.UnaryServerInterceptor(),
interceptor,
))
},
}
}
newDesc := &grpc.ServiceDesc{
ServiceName: desc.ServiceName,
HandlerType: desc.HandlerType,
Methods: newMethods,
Streams: desc.Streams,
Metadata: desc.Metadata,
}
server.RegisterService(newDesc, data.handler)
}
}
// it is like hooker ,grap the request and do sth....(like redirect the path or anything else)
type Interceptor interface {
Intercept(req *abci.RequestQuery)
}
var (
_ Interceptor = (*functionInterceptor)(nil)
)
type functionInterceptor struct {
hookF func(req *abci.RequestQuery)
}
func (f *functionInterceptor) Intercept(req *abci.RequestQuery) {
f.hookF(req)
}
func NewRedirectInterceptor(redirectPath string) Interceptor {
return newFunctionInterceptor(func(req *abci.RequestQuery) {
req.Path = redirectPath
})
}
func newFunctionInterceptor(f func(req *abci.RequestQuery)) *functionInterceptor {
return &functionInterceptor{hookF: f}
}