github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_rpc_middleware_interceptor_test.go (about) 1 package itest 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/decred/dcrd/dcrutil/v4" 10 "github.com/decred/dcrlnd/lnrpc" 11 "github.com/decred/dcrlnd/lntest" 12 "github.com/decred/dcrlnd/macaroons" 13 "github.com/stretchr/testify/require" 14 "google.golang.org/protobuf/proto" 15 "gopkg.in/macaroon.v2" 16 ) 17 18 // testRPCMiddlewareInterceptor tests that the RPC middleware interceptor can 19 // be used correctly and in a safe way. 20 func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) { 21 // Let's first enable the middleware interceptor. 22 aliceOriginalArgs := net.Alice.Cfg.ExtraArgs 23 net.Alice.Cfg.ExtraArgs = append( 24 net.Alice.Cfg.ExtraArgs, "--rpcmiddleware.enable", 25 ) 26 err := net.RestartNode(net.Alice, nil) 27 require.NoError(t.t, err) 28 29 // Let's set up a channel between Alice and Bob, just to get some useful 30 // data to inspect when doing RPC calls to Alice later. 31 net.EnsureConnected(t.t, net.Alice, net.Bob) 32 net.SendCoins(t.t, dcrutil.AtomsPerCoin, net.Alice) 33 chanAlice := openChannelAndAssert( 34 t, net, net.Alice, net.Bob, lntest.OpenChannelParams{ 35 Amt: 1_234_567, 36 }, 37 ) 38 defer closeChannelAndAssert(t, net, net.Alice, chanAlice, false) 39 40 // Load or bake the macaroons that the simulated users will use to 41 // access the RPC. 42 readonlyMac, err := net.Alice.ReadMacaroon( 43 net.Alice.ReadMacPath(), defaultTimeout, 44 ) 45 require.NoError(t.t, err) 46 47 customCaveatMac, err := macaroons.SafeCopyMacaroon(readonlyMac) 48 require.NoError(t.t, err) 49 addConstraint := macaroons.CustomConstraint( 50 "itest-caveat", "itest-value", 51 ) 52 require.NoError(t.t, addConstraint(customCaveatMac)) 53 54 // Run all sub-tests now. We can't run anything in parallel because that 55 // would cause the main test function to exit and the nodes being 56 // cleaned up. 57 t.t.Run("registration restrictions", func(tt *testing.T) { 58 middlewareRegistrationRestrictionTests(tt, net.Alice) 59 }) 60 t.t.Run("read-only intercept", func(tt *testing.T) { 61 registration := registerMiddleware( 62 tt, net.Alice, &lnrpc.MiddlewareRegistration{ 63 MiddlewareName: "itest-interceptor", 64 ReadOnlyMode: true, 65 }, 66 ) 67 defer registration.cancel() 68 69 middlewareInterceptionTest( 70 tt, net.Alice, net.Bob, registration, readonlyMac, 71 customCaveatMac, true, 72 ) 73 }) 74 75 // We've manually disconnected Bob from Alice in the previous test, make 76 // sure they're connected again. 77 net.EnsureConnected(t.t, net.Alice, net.Bob) 78 t.t.Run("encumbered macaroon intercept", func(tt *testing.T) { 79 registration := registerMiddleware( 80 tt, net.Alice, &lnrpc.MiddlewareRegistration{ 81 MiddlewareName: "itest-interceptor", 82 CustomMacaroonCaveatName: "itest-caveat", 83 }, 84 ) 85 defer registration.cancel() 86 87 middlewareInterceptionTest( 88 tt, net.Alice, net.Bob, registration, customCaveatMac, 89 readonlyMac, false, 90 ) 91 }) 92 93 // Next, run the response manipulation tests. 94 net.EnsureConnected(t.t, net.Alice, net.Bob) 95 t.t.Run("read-only not allowed to manipulate", func(tt *testing.T) { 96 registration := registerMiddleware( 97 tt, net.Alice, &lnrpc.MiddlewareRegistration{ 98 MiddlewareName: "itest-interceptor", 99 ReadOnlyMode: true, 100 }, 101 ) 102 defer registration.cancel() 103 104 middlewareManipulationTest( 105 tt, net.Alice, net.Bob, registration, readonlyMac, true, 106 ) 107 }) 108 net.EnsureConnected(t.t, net.Alice, net.Bob) 109 t.t.Run("encumbered macaroon manipulate", func(tt *testing.T) { 110 registration := registerMiddleware( 111 tt, net.Alice, &lnrpc.MiddlewareRegistration{ 112 MiddlewareName: "itest-interceptor", 113 CustomMacaroonCaveatName: "itest-caveat", 114 }, 115 ) 116 defer registration.cancel() 117 118 middlewareManipulationTest( 119 tt, net.Alice, net.Bob, registration, customCaveatMac, 120 false, 121 ) 122 }) 123 124 // And finally make sure mandatory middleware is always checked for any 125 // RPC request. 126 t.t.Run("mandatory middleware", func(tt *testing.T) { 127 if net.Alice.Cfg.RemoteWallet { 128 tt.Skipf("Skipping test because we cannot init a new " + 129 "remote wallet when the required middleware is " + 130 "offline") 131 } 132 alice := net.NewNode(t.t, "alice", []string{"--rpcmiddleware.enable"}) 133 middlewareMandatoryTest(tt, alice, net) 134 }) 135 136 net.Alice.Cfg.ExtraArgs = aliceOriginalArgs 137 err = net.RestartNode(net.Alice, nil) 138 require.NoError(t.t, err) 139 } 140 141 // middlewareRegistrationRestrictionTests tests all restrictions that apply to 142 // registering a middleware. 143 func middlewareRegistrationRestrictionTests(t *testing.T, 144 node *lntest.HarnessNode) { 145 146 testCases := []struct { 147 registration *lnrpc.MiddlewareRegistration 148 expectedErr string 149 }{{ 150 registration: &lnrpc.MiddlewareRegistration{ 151 MiddlewareName: "foo", 152 }, 153 expectedErr: "invalid middleware name", 154 }, { 155 registration: &lnrpc.MiddlewareRegistration{ 156 MiddlewareName: "itest-interceptor", 157 CustomMacaroonCaveatName: "foo", 158 }, 159 expectedErr: "custom caveat name of at least", 160 }, { 161 registration: &lnrpc.MiddlewareRegistration{ 162 MiddlewareName: "itest-interceptor", 163 CustomMacaroonCaveatName: "itest-caveat", 164 ReadOnlyMode: true, 165 }, 166 expectedErr: "cannot set read-only and custom caveat name", 167 }} 168 169 for idx, tc := range testCases { 170 tc := tc 171 172 t.Run(fmt.Sprintf("%d", idx), func(tt *testing.T) { 173 invalidName := registerMiddleware( 174 tt, node, tc.registration, 175 ) 176 _, err := invalidName.stream.Recv() 177 require.Error(tt, err) 178 require.Contains(tt, err.Error(), tc.expectedErr) 179 180 invalidName.cancel() 181 }) 182 } 183 } 184 185 // middlewareInterceptionTest tests that unary and streaming requests can be 186 // intercepted. It also makes sure that depending on the mode (read-only or 187 // custom macaroon caveat) a middleware only gets access to the requests it 188 // should be allowed access to. 189 func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode, 190 peer *lntest.HarnessNode, registration *middlewareHarness, 191 userMac *macaroon.Macaroon, disallowedMac *macaroon.Macaroon, 192 readOnly bool) { 193 194 // Everything we test here should be executed in a matter of 195 // milliseconds, so we can use one single timeout context for all calls. 196 ctxb := context.Background() 197 ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout) 198 defer cancel() 199 200 // Create a client connection that we'll use to simulate user requests 201 // to lnd with. 202 cleanup, client := macaroonClient(t, node, userMac) 203 defer cleanup() 204 205 // We're going to send a simple RPC request to list all channels. 206 // We need to invoke the intercept logic in a goroutine because we'd 207 // block the execution of the main task otherwise. 208 req := &lnrpc.ListChannelsRequest{ActiveOnly: true} 209 go registration.interceptUnary( 210 "/lnrpc.Lightning/ListChannels", req, nil, readOnly, 211 ) 212 213 // Do the actual call now and wait for the interceptor to do its thing. 214 resp, err := client.ListChannels(ctxc, req) 215 require.NoError(t, err) 216 217 // Did we receive the correct intercept message? 218 assertInterceptedType(t, resp, <-registration.responsesChan) 219 220 // Let's test the same for a streaming endpoint. 221 req2 := &lnrpc.PeerEventSubscription{} 222 go registration.interceptStream( 223 "/lnrpc.Lightning/SubscribePeerEvents", req2, nil, readOnly, 224 ) 225 226 // Do the actual call now and wait for the interceptor to do its thing. 227 peerCtx, peerCancel := context.WithCancel(ctxb) 228 resp2, err := client.SubscribePeerEvents(peerCtx, req2) 229 require.NoError(t, err) 230 231 // Disconnect Bob to trigger a peer event without using Alice's RPC 232 // interface itself. 233 _, err = peer.DisconnectPeer(ctxc, &lnrpc.DisconnectPeerRequest{ 234 PubKey: node.PubKeyStr, 235 }) 236 require.NoError(t, err) 237 peerEvent, err := resp2.Recv() 238 require.NoError(t, err) 239 require.Equal(t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType()) 240 241 // Stop the peer stream again, otherwise we'll produce more events. 242 peerCancel() 243 244 // Did we receive the correct intercept message? 245 assertInterceptedType(t, peerEvent, <-registration.responsesChan) 246 247 // Make sure that with the other macaroon we aren't allowed to access 248 // the interceptor. If we registered for read-only access then there is 249 // no middleware that handles the custom macaroon caveat. If we 250 // registered for a custom caveat then there is no middleware that 251 // handles unencumbered read-only access. 252 cleanup, client = macaroonClient(t, node, disallowedMac) 253 defer cleanup() 254 255 // We need to make sure we don't get any interception messages for 256 // requests made with the disallowed macaroon. 257 var ( 258 errChan = make(chan error, 1) 259 msgChan = make(chan *lnrpc.RPCMiddlewareRequest, 1) 260 ) 261 go func() { 262 req, err := registration.stream.Recv() 263 if err != nil { 264 errChan <- err 265 return 266 } 267 268 msgChan <- req 269 }() 270 271 // Let's invoke the same request again but with the other macaroon. 272 resp, err = client.ListChannels(ctxc, req) 273 274 // Depending on what mode we're in, we expect something different. If we 275 // are in read-only mode then an encumbered macaroon cannot be used 276 // since there is no middleware registered for it. If we registered for 277 // a custom macaroon caveat and a request with anon-encumbered macaroon 278 // comes in, we expect to just not get any intercept messages. 279 if readOnly { 280 require.Error(t, err) 281 require.Contains( 282 t, err.Error(), "cannot accept macaroon with custom "+ 283 "caveat 'itest-caveat', no middleware "+ 284 "registered", 285 ) 286 } else { 287 require.NoError(t, err) 288 289 // We disconnected Bob so there should be no active channels. 290 require.Len(t, resp.Channels, 0) 291 } 292 293 // There should be neither an error nor any interception messages in the 294 // channels. 295 select { 296 case err := <-errChan: 297 t.Fatalf("Unexpected error, not expecting messages: %v", err) 298 299 case msg := <-msgChan: 300 t.Fatalf("Unexpected intercept message: %v", msg) 301 302 case <-time.After(time.Second): 303 // Nothing came in for a second, we're fine. 304 } 305 } 306 307 // middlewareManipulationTest tests that unary and streaming requests can be 308 // intercepted and also manipulated, at least if the middleware didn't register 309 // for read-only access. 310 func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode, 311 peer *lntest.HarnessNode, registration *middlewareHarness, 312 userMac *macaroon.Macaroon, readOnly bool) { 313 314 // Everything we test here should be executed in a matter of 315 // milliseconds, so we can use one single timeout context for all calls. 316 ctxb := context.Background() 317 ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout) 318 defer cancel() 319 320 // Create a client connection that we'll use to simulate user requests 321 // to lnd with. 322 cleanup, client := macaroonClient(t, node, userMac) 323 defer cleanup() 324 325 // We're going to attempt to replace the response with our own. But 326 // since we only registered for read-only access, our replacement should 327 // just be ignored. 328 replacementResponse := &lnrpc.ListChannelsResponse{ 329 Channels: []*lnrpc.Channel{{ 330 ChannelPoint: "f000:0", 331 }, { 332 ChannelPoint: "f000:1", 333 }}, 334 } 335 336 // We're going to send a simple RPC request to list all channels. 337 // We need to invoke the intercept logic in a goroutine because we'd 338 // block the execution of the main task otherwise. 339 req := &lnrpc.ListChannelsRequest{ActiveOnly: true} 340 go registration.interceptUnary( 341 "/lnrpc.Lightning/ListChannels", req, replacementResponse, 342 readOnly, 343 ) 344 345 // Do the actual call now and wait for the interceptor to do its thing. 346 resp, err := client.ListChannels(ctxc, req) 347 require.NoError(t, err) 348 349 // Did we get the manipulated response (2 fake channels) or the original 350 // one (1 channel)? 351 if readOnly { 352 require.Len(t, resp.Channels, 1) 353 } else { 354 require.Len(t, resp.Channels, 2) 355 } 356 357 // Let's test the same for a streaming endpoint. 358 replacementResponse2 := &lnrpc.PeerEvent{ 359 Type: lnrpc.PeerEvent_PEER_ONLINE, 360 PubKey: "foo", 361 } 362 req2 := &lnrpc.PeerEventSubscription{} 363 go registration.interceptStream( 364 "/lnrpc.Lightning/SubscribePeerEvents", req2, 365 replacementResponse2, readOnly, 366 ) 367 368 // Do the actual call now and wait for the interceptor to do its thing. 369 peerCtx, peerCancel := context.WithCancel(ctxb) 370 resp2, err := client.SubscribePeerEvents(peerCtx, req2) 371 require.NoError(t, err) 372 373 // Disconnect Bob to trigger a peer event without using Alice's RPC 374 // interface itself. 375 _, err = peer.DisconnectPeer(ctxc, &lnrpc.DisconnectPeerRequest{ 376 PubKey: node.PubKeyStr, 377 }) 378 require.NoError(t, err) 379 peerEvent, err := resp2.Recv() 380 require.NoError(t, err) 381 382 // Did we get the correct, original response? 383 if readOnly { 384 require.Equal( 385 t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType(), 386 ) 387 require.Equal(t, peer.PubKeyStr, peerEvent.PubKey) 388 } else { 389 require.Equal( 390 t, lnrpc.PeerEvent_PEER_ONLINE, peerEvent.GetType(), 391 ) 392 require.Equal(t, "foo", peerEvent.PubKey) 393 } 394 395 // Stop the peer stream again, otherwise we'll produce more events. 396 peerCancel() 397 } 398 399 // middlewareMandatoryTest tests that all RPC requests are blocked if there is 400 // a mandatory middleware declared that's currently not registered. 401 func middlewareMandatoryTest(t *testing.T, node *lntest.HarnessNode, 402 net *lntest.NetworkHarness) { 403 404 // Let's declare our itest interceptor as mandatory but don't register 405 // it just yet. That should cause all RPC requests to fail, except for 406 // the registration itself. 407 node.Cfg.ExtraArgs = append( 408 node.Cfg.ExtraArgs, 409 "--rpcmiddleware.addmandatory=itest-interceptor", 410 ) 411 err := net.RestartNodeNoUnlock(node, nil, false) 412 require.NoError(t, err) 413 414 // The "wait for node to start" flag of the above restart does too much 415 // and has a call to GetInfo built in, which will fail in this special 416 // test case. So we need to do the wait and client setup manually here. 417 conn, err := node.ConnectRPC(true) 418 require.NoError(t, err) 419 node.InitRPCClients(conn) 420 err = node.WaitUntilStateReached(lnrpc.WalletState_RPC_ACTIVE) 421 require.NoError(t, err) 422 node.LightningClient = lnrpc.NewLightningClient(conn) 423 424 ctxb := context.Background() 425 ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout) 426 defer cancel() 427 428 // Test a unary request first. 429 _, err = node.ListChannels(ctxc, &lnrpc.ListChannelsRequest{}) 430 require.Error(t, err) 431 require.Contains( 432 t, err.Error(), "middleware 'itest-interceptor' is "+ 433 "currently not registered", 434 ) 435 436 // Then a streaming one. 437 stream, err := node.SubscribeInvoices(ctxc, &lnrpc.InvoiceSubscription{}) 438 require.NoError(t, err) 439 _, err = stream.Recv() 440 require.Error(t, err) 441 require.Contains( 442 t, err.Error(), "middleware 'itest-interceptor' is "+ 443 "currently not registered", 444 ) 445 446 // Now let's register the middleware and try again. 447 registration := registerMiddleware( 448 t, node, &lnrpc.MiddlewareRegistration{ 449 MiddlewareName: "itest-interceptor", 450 CustomMacaroonCaveatName: "itest-caveat", 451 }, 452 ) 453 defer registration.cancel() 454 455 // Both the unary and streaming requests should now be allowed. 456 time.Sleep(500 * time.Millisecond) 457 _, err = node.ListChannels(ctxc, &lnrpc.ListChannelsRequest{}) 458 require.NoError(t, err) 459 _, err = node.SubscribeInvoices(ctxc, &lnrpc.InvoiceSubscription{}) 460 require.NoError(t, err) 461 462 // We now shut down the node manually to prevent the test from failing 463 // because we can't call the stop RPC if we unregister the middleware in 464 // the defer statement above. 465 err = net.ShutdownNode(node) 466 require.NoError(t, err) 467 } 468 469 // assertInterceptedType makes sure that the intercept message sent by the RPC 470 // interceptor is correct for a proto message that was sent or received over the 471 // RPC interface. 472 func assertInterceptedType(t *testing.T, rpcMessage proto.Message, 473 interceptMessage *lnrpc.RPCMessage) { 474 475 t.Helper() 476 477 require.Equal( 478 t, string(proto.MessageName(rpcMessage)), 479 interceptMessage.TypeName, 480 ) 481 rawRequest, err := proto.Marshal(rpcMessage) 482 require.NoError(t, err) 483 484 // Make sure we don't trip over nil vs. empty slice in the equality 485 // check below. 486 if len(rawRequest) == 0 { 487 rawRequest = nil 488 } 489 490 require.Equal(t, rawRequest, interceptMessage.Serialized) 491 } 492 493 // middlewareStream is a type alias to shorten the long definition. 494 type middlewareStream lnrpc.Lightning_RegisterRPCMiddlewareClient 495 496 // middlewareHarness is a test harness that holds one instance of a simulated 497 // middleware. 498 type middlewareHarness struct { 499 t *testing.T 500 cancel func() 501 stream middlewareStream 502 503 responsesChan chan *lnrpc.RPCMessage 504 } 505 506 // registerMiddleware creates a new middleware harness and sends the initial 507 // register message to the RPC server. 508 func registerMiddleware(t *testing.T, node *lntest.HarnessNode, 509 registration *lnrpc.MiddlewareRegistration) *middlewareHarness { 510 511 ctxc, cancel := context.WithCancel(context.Background()) 512 513 middlewareStream, err := node.RegisterRPCMiddleware(ctxc) 514 require.NoError(t, err) 515 516 err = middlewareStream.Send(&lnrpc.RPCMiddlewareResponse{ 517 MiddlewareMessage: &lnrpc.RPCMiddlewareResponse_Register{ 518 Register: registration, 519 }, 520 }) 521 require.NoError(t, err) 522 523 return &middlewareHarness{ 524 t: t, 525 cancel: cancel, 526 stream: middlewareStream, 527 responsesChan: make(chan *lnrpc.RPCMessage), 528 } 529 } 530 531 // interceptUnary intercepts a unary call, optionally requesting to replace the 532 // response sent to the client. A unary call is expected to receive one 533 // intercept message for the request and one for the response. 534 // 535 // NOTE: Must be called in a goroutine as this will block until the response is 536 // read from the response channel. 537 func (h *middlewareHarness) interceptUnary(methodURI string, 538 expectedRequest proto.Message, responseReplacement proto.Message, 539 readOnly bool) { 540 541 // Read intercept message and make sure it's for an RPC request. 542 reqIntercept, err := h.stream.Recv() 543 require.NoError(h.t, err) 544 545 // Make sure the custom condition is populated correctly (if we're using 546 // a macaroon with a custom condition). 547 if !readOnly { 548 require.Equal( 549 h.t, "itest-value", reqIntercept.CustomCaveatCondition, 550 ) 551 } 552 553 req := reqIntercept.GetRequest() 554 require.NotNil(h.t, req) 555 556 // We know the request we're going to send so make sure we get the right 557 // type and content from the interceptor. 558 require.Equal(h.t, methodURI, req.MethodFullUri) 559 assertInterceptedType(h.t, expectedRequest, req) 560 561 // We need to accept the request. 562 h.sendAccept(reqIntercept.MsgId, nil) 563 564 // Now read the intercept message for the response. 565 respIntercept, err := h.stream.Recv() 566 require.NoError(h.t, err) 567 res := respIntercept.GetResponse() 568 require.NotNil(h.t, res) 569 570 // We expect the request ID to be the same for the request intercept 571 // and the response intercept messages. But the message IDs must be 572 // different/unique. 573 require.Equal(h.t, reqIntercept.RequestId, respIntercept.RequestId) 574 require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId) 575 576 // We need to accept the response as well. 577 h.sendAccept(respIntercept.MsgId, responseReplacement) 578 579 h.responsesChan <- res 580 } 581 582 // interceptStream intercepts a streaming call, optionally requesting to replace 583 // the (first) response sent to the client. A streaming call is expected to 584 // receive one intercept message for the stream authentication, one for the 585 // first request and one for the first response. 586 // 587 // NOTE: Must be called in a goroutine as this will block until the first 588 // response is read from the response channel. 589 func (h *middlewareHarness) interceptStream(methodURI string, 590 expectedRequest proto.Message, responseReplacement proto.Message, 591 readOnly bool) { 592 593 // Read intercept message and make sure it's for an RPC stream auth. 594 authIntercept, err := h.stream.Recv() 595 require.NoError(h.t, err) 596 597 // Make sure the custom condition is populated correctly (if we're using 598 // a macaroon with a custom condition). 599 if !readOnly { 600 require.Equal( 601 h.t, "itest-value", authIntercept.CustomCaveatCondition, 602 ) 603 } 604 605 auth := authIntercept.GetStreamAuth() 606 require.NotNil(h.t, auth) 607 608 // This is just the authentication, so we can only look at the URI. 609 require.Equal(h.t, methodURI, auth.MethodFullUri) 610 611 // We need to accept the auth. 612 h.sendAccept(authIntercept.MsgId, nil) 613 614 // Read intercept message and make sure it's for an RPC request. 615 reqIntercept, err := h.stream.Recv() 616 require.NoError(h.t, err) 617 req := reqIntercept.GetRequest() 618 require.NotNil(h.t, req) 619 620 // We know the request we're going to send so make sure we get the right 621 // type and content from the interceptor. 622 require.Equal(h.t, methodURI, req.MethodFullUri) 623 assertInterceptedType(h.t, expectedRequest, req) 624 625 // We need to accept the request. 626 h.sendAccept(reqIntercept.MsgId, nil) 627 628 // Now read the intercept message for the response. 629 respIntercept, err := h.stream.Recv() 630 require.NoError(h.t, err) 631 res := respIntercept.GetResponse() 632 require.NotNil(h.t, res) 633 634 // We expect the request ID to be the same for the auth intercept, 635 // request intercept and the response intercept messages. But the 636 // message IDs must be different/unique. 637 require.Equal(h.t, authIntercept.RequestId, respIntercept.RequestId) 638 require.Equal(h.t, reqIntercept.RequestId, respIntercept.RequestId) 639 require.NotEqual(h.t, authIntercept.MsgId, reqIntercept.MsgId) 640 require.NotEqual(h.t, authIntercept.MsgId, respIntercept.MsgId) 641 require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId) 642 643 // We need to accept the response as well. 644 h.sendAccept(respIntercept.MsgId, responseReplacement) 645 646 h.responsesChan <- res 647 } 648 649 // sendAccept sends an accept feedback to the RPC server. 650 func (h *middlewareHarness) sendAccept(msgID uint64, 651 responseReplacement proto.Message) { 652 653 var replacementBytes []byte 654 if responseReplacement != nil { 655 var err error 656 replacementBytes, err = proto.Marshal(responseReplacement) 657 require.NoError(h.t, err) 658 } 659 660 err := h.stream.Send(&lnrpc.RPCMiddlewareResponse{ 661 MiddlewareMessage: &lnrpc.RPCMiddlewareResponse_Feedback{ 662 Feedback: &lnrpc.InterceptFeedback{ 663 ReplaceResponse: len(replacementBytes) > 0, 664 ReplacementSerialized: replacementBytes, 665 }, 666 }, 667 RefMsgId: msgID, 668 }) 669 require.NoError(h.t, err) 670 }