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