github.com/onflow/flow-go@v0.33.17/engine/access/integration_unsecure_grpc_server_test.go (about)

     1  package access
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	accessproto "github.com/onflow/flow/protobuf/go/flow/access"
    11  	executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata"
    12  	"github.com/rs/zerolog"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/mock"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/stretchr/testify/suite"
    17  	"google.golang.org/grpc"
    18  	"google.golang.org/grpc/credentials"
    19  	"google.golang.org/grpc/credentials/insecure"
    20  
    21  	"github.com/onflow/flow-go/engine"
    22  	accessmock "github.com/onflow/flow-go/engine/access/mock"
    23  	"github.com/onflow/flow-go/engine/access/rpc"
    24  	"github.com/onflow/flow-go/engine/access/rpc/backend"
    25  	"github.com/onflow/flow-go/engine/access/state_stream"
    26  	statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend"
    27  	"github.com/onflow/flow-go/model/flow"
    28  	"github.com/onflow/flow-go/module/blobs"
    29  	"github.com/onflow/flow-go/module/execution"
    30  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    31  	"github.com/onflow/flow-go/module/executiondatasync/execution_data/cache"
    32  	"github.com/onflow/flow-go/module/grpcserver"
    33  	"github.com/onflow/flow-go/module/irrecoverable"
    34  	"github.com/onflow/flow-go/module/mempool/herocache"
    35  	"github.com/onflow/flow-go/module/metrics"
    36  	module "github.com/onflow/flow-go/module/mock"
    37  	"github.com/onflow/flow-go/network"
    38  	protocol "github.com/onflow/flow-go/state/protocol/mock"
    39  	"github.com/onflow/flow-go/storage"
    40  	storagemock "github.com/onflow/flow-go/storage/mock"
    41  	"github.com/onflow/flow-go/utils/grpcutils"
    42  	"github.com/onflow/flow-go/utils/unittest"
    43  )
    44  
    45  // SameGRPCPortTestSuite verifies both AccessAPI and ExecutionDataAPI client continue to work when configured
    46  // on the same port
    47  type SameGRPCPortTestSuite struct {
    48  	suite.Suite
    49  	state          *protocol.State
    50  	snapshot       *protocol.Snapshot
    51  	epochQuery     *protocol.EpochQuery
    52  	log            zerolog.Logger
    53  	net            *network.EngineRegistry
    54  	request        *module.Requester
    55  	collClient     *accessmock.AccessAPIClient
    56  	execClient     *accessmock.ExecutionAPIClient
    57  	me             *module.Local
    58  	chainID        flow.ChainID
    59  	metrics        *metrics.NoopCollector
    60  	rpcEng         *rpc.Engine
    61  	stateStreamEng *statestreambackend.Engine
    62  
    63  	// storage
    64  	blocks       *storagemock.Blocks
    65  	headers      *storagemock.Headers
    66  	events       *storagemock.Events
    67  	collections  *storagemock.Collections
    68  	transactions *storagemock.Transactions
    69  	receipts     *storagemock.ExecutionReceipts
    70  	seals        *storagemock.Seals
    71  	results      *storagemock.ExecutionResults
    72  	registers    *execution.RegistersAsyncStore
    73  
    74  	ctx    irrecoverable.SignalerContext
    75  	cancel context.CancelFunc
    76  
    77  	// grpc servers
    78  	secureGrpcServer   *grpcserver.GrpcServer
    79  	unsecureGrpcServer *grpcserver.GrpcServer
    80  
    81  	bs                blobs.Blobstore
    82  	eds               execution_data.ExecutionDataStore
    83  	broadcaster       *engine.Broadcaster
    84  	execDataCache     *cache.ExecutionDataCache
    85  	execDataHeroCache *herocache.BlockExecutionData
    86  
    87  	blockMap map[uint64]*flow.Block
    88  }
    89  
    90  func (suite *SameGRPCPortTestSuite) SetupTest() {
    91  	suite.log = zerolog.New(os.Stdout)
    92  	suite.net = new(network.EngineRegistry)
    93  	suite.state = new(protocol.State)
    94  	suite.snapshot = new(protocol.Snapshot)
    95  	params := new(protocol.Params)
    96  	suite.registers = execution.NewRegistersAsyncStore()
    97  
    98  	suite.epochQuery = new(protocol.EpochQuery)
    99  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
   100  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
   101  	suite.state.On("Params").Return(params)
   102  	suite.snapshot.On("Epochs").Return(suite.epochQuery).Maybe()
   103  	suite.blocks = new(storagemock.Blocks)
   104  	suite.headers = new(storagemock.Headers)
   105  	suite.events = new(storagemock.Events)
   106  	suite.transactions = new(storagemock.Transactions)
   107  	suite.collections = new(storagemock.Collections)
   108  	suite.receipts = new(storagemock.ExecutionReceipts)
   109  	suite.results = new(storagemock.ExecutionResults)
   110  	suite.seals = new(storagemock.Seals)
   111  
   112  	suite.collClient = new(accessmock.AccessAPIClient)
   113  	suite.execClient = new(accessmock.ExecutionAPIClient)
   114  
   115  	suite.request = new(module.Requester)
   116  	suite.request.On("EntityByID", mock.Anything, mock.Anything)
   117  
   118  	suite.me = new(module.Local)
   119  	suite.eds = execution_data.NewExecutionDataStore(suite.bs, execution_data.DefaultSerializer)
   120  
   121  	suite.broadcaster = engine.NewBroadcaster()
   122  
   123  	suite.execDataHeroCache = herocache.NewBlockExecutionData(state_stream.DefaultCacheSize, suite.log, metrics.NewNoopCollector())
   124  	suite.execDataCache = cache.NewExecutionDataCache(suite.eds, suite.headers, suite.seals, suite.results, suite.execDataHeroCache)
   125  
   126  	accessIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess))
   127  	suite.me.
   128  		On("NodeID").
   129  		Return(accessIdentity.NodeID)
   130  
   131  	suite.chainID = flow.Testnet
   132  	suite.metrics = metrics.NewNoopCollector()
   133  
   134  	config := rpc.Config{
   135  		UnsecureGRPCListenAddr: unittest.DefaultAddress,
   136  		SecureGRPCListenAddr:   unittest.DefaultAddress,
   137  		HTTPListenAddr:         unittest.DefaultAddress,
   138  	}
   139  
   140  	blockCount := 5
   141  	suite.blockMap = make(map[uint64]*flow.Block, blockCount)
   142  	// generate blockCount consecutive blocks with associated seal, result and execution data
   143  	rootBlock := unittest.BlockFixture()
   144  	parent := rootBlock.Header
   145  	suite.blockMap[rootBlock.Header.Height] = &rootBlock
   146  
   147  	for i := 0; i < blockCount; i++ {
   148  		block := unittest.BlockWithParentFixture(parent)
   149  		suite.blockMap[block.Header.Height] = block
   150  	}
   151  
   152  	params.On("SporkID").Return(unittest.IdentifierFixture(), nil)
   153  	params.On("ProtocolVersion").Return(uint(unittest.Uint64InRange(10, 30)), nil)
   154  	params.On("SporkRootBlockHeight").Return(rootBlock.Header.Height, nil)
   155  	params.On("SealedRoot").Return(rootBlock.Header, nil)
   156  
   157  	// generate a server certificate that will be served by the GRPC server
   158  	networkingKey := unittest.NetworkingPrivKeyFixture()
   159  	x509Certificate, err := grpcutils.X509Certificate(networkingKey)
   160  	assert.NoError(suite.T(), err)
   161  	tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate)
   162  	// set the transport credentials for the server to use
   163  	config.TransportCredentials = credentials.NewTLS(tlsConfig)
   164  
   165  	suite.secureGrpcServer = grpcserver.NewGrpcServerBuilder(suite.log,
   166  		config.SecureGRPCListenAddr,
   167  		grpcutils.DefaultMaxMsgSize,
   168  		false,
   169  		nil,
   170  		nil,
   171  		grpcserver.WithTransportCredentials(config.TransportCredentials)).Build()
   172  
   173  	suite.unsecureGrpcServer = grpcserver.NewGrpcServerBuilder(suite.log,
   174  		config.UnsecureGRPCListenAddr,
   175  		grpcutils.DefaultMaxMsgSize,
   176  		false,
   177  		nil,
   178  		nil).Build()
   179  
   180  	block := unittest.BlockHeaderFixture()
   181  	suite.snapshot.On("Head").Return(block, nil)
   182  
   183  	bnd, err := backend.New(backend.Params{
   184  		State:                suite.state,
   185  		CollectionRPC:        suite.collClient,
   186  		Blocks:               suite.blocks,
   187  		Headers:              suite.headers,
   188  		Collections:          suite.collections,
   189  		Transactions:         suite.transactions,
   190  		ChainID:              suite.chainID,
   191  		AccessMetrics:        suite.metrics,
   192  		MaxHeightRange:       0,
   193  		Log:                  suite.log,
   194  		SnapshotHistoryLimit: 0,
   195  		Communicator:         backend.NewNodeCommunicator(false),
   196  	})
   197  	require.NoError(suite.T(), err)
   198  
   199  	stateStreamConfig := statestreambackend.Config{}
   200  	// create rpc engine builder
   201  	rpcEngBuilder, err := rpc.NewBuilder(
   202  		suite.log,
   203  		suite.state,
   204  		config,
   205  		suite.chainID,
   206  		suite.metrics,
   207  		false,
   208  		suite.me,
   209  		bnd,
   210  		bnd,
   211  		suite.secureGrpcServer,
   212  		suite.unsecureGrpcServer,
   213  		nil,
   214  		stateStreamConfig,
   215  	)
   216  	assert.NoError(suite.T(), err)
   217  	suite.rpcEng, err = rpcEngBuilder.WithLegacy().Build()
   218  	assert.NoError(suite.T(), err)
   219  	suite.ctx, suite.cancel = irrecoverable.NewMockSignalerContextWithCancel(suite.T(), context.Background())
   220  
   221  	suite.headers.On("BlockIDByHeight", mock.AnythingOfType("uint64")).Return(
   222  		func(height uint64) flow.Identifier {
   223  			if block, ok := suite.blockMap[height]; ok {
   224  				return block.Header.ID()
   225  			}
   226  			return flow.ZeroID
   227  		},
   228  		func(height uint64) error {
   229  			if _, ok := suite.blockMap[height]; ok {
   230  				return nil
   231  			}
   232  			return storage.ErrNotFound
   233  		},
   234  	).Maybe()
   235  
   236  	conf := statestreambackend.Config{
   237  		ClientSendTimeout:    state_stream.DefaultSendTimeout,
   238  		ClientSendBufferSize: state_stream.DefaultSendBufferSize,
   239  	}
   240  
   241  	stateStreamBackend, err := statestreambackend.New(
   242  		suite.log,
   243  		conf,
   244  		suite.state,
   245  		suite.headers,
   246  		suite.seals,
   247  		suite.results,
   248  		nil,
   249  		suite.execDataCache,
   250  		nil,
   251  		rootBlock.Header.Height,
   252  		rootBlock.Header.Height,
   253  		suite.registers,
   254  		backend.NewEventsIndex(suite.events),
   255  		false,
   256  	)
   257  	assert.NoError(suite.T(), err)
   258  
   259  	// create state stream engine
   260  	suite.stateStreamEng, err = statestreambackend.NewEng(
   261  		suite.log,
   262  		conf,
   263  		suite.execDataCache,
   264  		suite.headers,
   265  		suite.chainID,
   266  		suite.unsecureGrpcServer,
   267  		stateStreamBackend,
   268  		nil,
   269  	)
   270  	assert.NoError(suite.T(), err)
   271  
   272  	suite.rpcEng.Start(suite.ctx)
   273  	suite.stateStreamEng.Start(suite.ctx)
   274  
   275  	suite.secureGrpcServer.Start(suite.ctx)
   276  	suite.unsecureGrpcServer.Start(suite.ctx)
   277  
   278  	// wait for the servers to startup
   279  	unittest.AssertClosesBefore(suite.T(), suite.secureGrpcServer.Ready(), 2*time.Second)
   280  	unittest.AssertClosesBefore(suite.T(), suite.unsecureGrpcServer.Ready(), 2*time.Second)
   281  
   282  	// wait for the rpc engine to startup
   283  	unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Ready(), 2*time.Second)
   284  	// wait for the state stream engine to startup
   285  	unittest.AssertClosesBefore(suite.T(), suite.stateStreamEng.Ready(), 2*time.Second)
   286  }
   287  
   288  // TestEnginesOnTheSameGrpcPort verifies if both AccessAPI and ExecutionDataAPI client successfully connect and continue
   289  // to work when configured on the same port
   290  func (suite *SameGRPCPortTestSuite) TestEnginesOnTheSameGrpcPort() {
   291  	ctx := context.Background()
   292  
   293  	conn, err := grpc.Dial(
   294  		suite.unsecureGrpcServer.GRPCAddress().String(),
   295  		grpc.WithTransportCredentials(insecure.NewCredentials()))
   296  	assert.NoError(suite.T(), err)
   297  	closer := io.Closer(conn)
   298  
   299  	suite.Run("happy path - grpc access api client can connect successfully", func() {
   300  		req := &accessproto.GetNetworkParametersRequest{}
   301  
   302  		// expect 2 upstream calls
   303  		suite.execClient.On("GetNetworkParameters", mock.Anything, mock.Anything).Return(nil, nil).Twice()
   304  		suite.collClient.On("GetNetworkParameters", mock.Anything, mock.Anything).Return(nil, nil).Twice()
   305  
   306  		client := suite.unsecureAccessAPIClient(conn)
   307  
   308  		_, err := client.GetNetworkParameters(ctx, req)
   309  		assert.NoError(suite.T(), err, "failed to get network")
   310  	})
   311  
   312  	suite.Run("happy path - grpc execution data api client can connect successfully", func() {
   313  		req := &executiondataproto.SubscribeEventsRequest{}
   314  
   315  		client := suite.unsecureExecutionDataAPIClient(conn)
   316  
   317  		_, err := client.SubscribeEvents(ctx, req)
   318  		assert.NoError(suite.T(), err, "failed to subscribe events")
   319  	})
   320  	defer closer.Close()
   321  }
   322  
   323  func TestSameGRPCTestSuite(t *testing.T) {
   324  	suite.Run(t, new(SameGRPCPortTestSuite))
   325  }
   326  
   327  // unsecureAccessAPIClient creates an unsecure grpc AccessAPI client
   328  func (suite *SameGRPCPortTestSuite) unsecureAccessAPIClient(conn *grpc.ClientConn) accessproto.AccessAPIClient {
   329  	client := accessproto.NewAccessAPIClient(conn)
   330  	return client
   331  }
   332  
   333  // unsecureExecutionDataAPIClient creates an unsecure ExecutionDataAPI client
   334  func (suite *SameGRPCPortTestSuite) unsecureExecutionDataAPIClient(conn *grpc.ClientConn) executiondataproto.ExecutionDataAPIClient {
   335  	client := executiondataproto.NewExecutionDataAPIClient(conn)
   336  	return client
   337  }