github.com/koko1123/flow-go-1@v0.29.6/engine/access/secure_grpcr_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  	"github.com/rs/zerolog"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/suite"
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/grpc/credentials"
    17  
    18  	accessmock "github.com/koko1123/flow-go-1/engine/access/mock"
    19  	"github.com/koko1123/flow-go-1/engine/access/rpc"
    20  	"github.com/koko1123/flow-go-1/model/flow"
    21  	"github.com/koko1123/flow-go-1/module/metrics"
    22  	module "github.com/koko1123/flow-go-1/module/mock"
    23  	"github.com/koko1123/flow-go-1/network"
    24  	protocol "github.com/koko1123/flow-go-1/state/protocol/mock"
    25  	storagemock "github.com/koko1123/flow-go-1/storage/mock"
    26  	"github.com/koko1123/flow-go-1/utils/grpcutils"
    27  	"github.com/koko1123/flow-go-1/utils/unittest"
    28  	"github.com/onflow/flow-go/crypto"
    29  )
    30  
    31  // SecureGRPCTestSuite tests that Access node provides a secure GRPC server
    32  type SecureGRPCTestSuite struct {
    33  	suite.Suite
    34  	state      *protocol.State
    35  	snapshot   *protocol.Snapshot
    36  	epochQuery *protocol.EpochQuery
    37  	log        zerolog.Logger
    38  	net        *network.Network
    39  	request    *module.Requester
    40  	collClient *accessmock.AccessAPIClient
    41  	execClient *accessmock.ExecutionAPIClient
    42  	me         *module.Local
    43  	chainID    flow.ChainID
    44  	metrics    *metrics.NoopCollector
    45  	rpcEng     *rpc.Engine
    46  	publicKey  crypto.PublicKey
    47  
    48  	// storage
    49  	blocks       *storagemock.Blocks
    50  	headers      *storagemock.Headers
    51  	collections  *storagemock.Collections
    52  	transactions *storagemock.Transactions
    53  	receipts     *storagemock.ExecutionReceipts
    54  }
    55  
    56  func (suite *SecureGRPCTestSuite) SetupTest() {
    57  	suite.log = zerolog.New(os.Stdout)
    58  	suite.net = new(network.Network)
    59  	suite.state = new(protocol.State)
    60  	suite.snapshot = new(protocol.Snapshot)
    61  
    62  	suite.epochQuery = new(protocol.EpochQuery)
    63  	suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe()
    64  	suite.state.On("Final").Return(suite.snapshot, nil).Maybe()
    65  	suite.snapshot.On("Epochs").Return(suite.epochQuery).Maybe()
    66  	suite.blocks = new(storagemock.Blocks)
    67  	suite.headers = new(storagemock.Headers)
    68  	suite.transactions = new(storagemock.Transactions)
    69  	suite.collections = new(storagemock.Collections)
    70  	suite.receipts = new(storagemock.ExecutionReceipts)
    71  
    72  	suite.collClient = new(accessmock.AccessAPIClient)
    73  	suite.execClient = new(accessmock.ExecutionAPIClient)
    74  
    75  	suite.request = new(module.Requester)
    76  	suite.request.On("EntityByID", mock.Anything, mock.Anything)
    77  
    78  	suite.me = new(module.Local)
    79  
    80  	accessIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess))
    81  	suite.me.
    82  		On("NodeID").
    83  		Return(accessIdentity.NodeID)
    84  
    85  	suite.chainID = flow.Testnet
    86  	suite.metrics = metrics.NewNoopCollector()
    87  
    88  	config := rpc.Config{
    89  		UnsecureGRPCListenAddr: unittest.DefaultAddress,
    90  		SecureGRPCListenAddr:   unittest.DefaultAddress,
    91  		HTTPListenAddr:         unittest.DefaultAddress,
    92  	}
    93  
    94  	// generate a server certificate that will be served by the GRPC server
    95  	networkingKey := unittest.NetworkingPrivKeyFixture()
    96  	x509Certificate, err := grpcutils.X509Certificate(networkingKey)
    97  	assert.NoError(suite.T(), err)
    98  	tlsConfig := grpcutils.DefaultServerTLSConfig(x509Certificate)
    99  	// set the transport credentials for the server to use
   100  	config.TransportCredentials = credentials.NewTLS(tlsConfig)
   101  	// save the public key to use later in tests later
   102  	suite.publicKey = networkingKey.PublicKey()
   103  
   104  	rpcEngBuilder, err := rpc.NewBuilder(suite.log, suite.state, config, suite.collClient, nil, suite.blocks, suite.headers, suite.collections, suite.transactions, nil,
   105  		nil, suite.chainID, suite.metrics, suite.metrics, 0, 0, false, false, nil, nil)
   106  	assert.NoError(suite.T(), err)
   107  	suite.rpcEng, err = rpcEngBuilder.WithLegacy().Build()
   108  	assert.NoError(suite.T(), err)
   109  	unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Ready(), 2*time.Second)
   110  
   111  	// wait for the server to startup
   112  	assert.Eventually(suite.T(), func() bool {
   113  		return suite.rpcEng.SecureGRPCAddress() != nil
   114  	}, 5*time.Second, 10*time.Millisecond)
   115  }
   116  
   117  func TestSecureGRPC(t *testing.T) {
   118  	suite.Run(t, new(SecureGRPCTestSuite))
   119  }
   120  
   121  func (suite *SecureGRPCTestSuite) TestAPICallUsingSecureGRPC() {
   122  
   123  	req := &accessproto.PingRequest{}
   124  	ctx := context.Background()
   125  
   126  	// expect 2 upstream calls
   127  	suite.execClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice()
   128  	suite.collClient.On("Ping", mock.Anything, mock.Anything).Return(nil, nil).Twice()
   129  
   130  	suite.Run("happy path - grpc client can connect successfully with the correct public key", func() {
   131  		client, closer := suite.secureGRPCClient(suite.publicKey)
   132  		defer closer.Close()
   133  		resp, err := client.Ping(ctx, req)
   134  		assert.NoError(suite.T(), err)
   135  		assert.NotNil(suite.T(), resp)
   136  	})
   137  
   138  	suite.Run("happy path - connection fails with an incorrect public key", func() {
   139  		newKey := unittest.NetworkingPrivKeyFixture()
   140  		client, closer := suite.secureGRPCClient(newKey.PublicKey())
   141  		defer closer.Close()
   142  		_, err := client.Ping(ctx, req)
   143  		assert.Error(suite.T(), err)
   144  	})
   145  }
   146  
   147  func (suite *SecureGRPCTestSuite) TearDownTest() {
   148  	// close the server
   149  	if suite.rpcEng != nil {
   150  		unittest.AssertClosesBefore(suite.T(), suite.rpcEng.Done(), 2*time.Second)
   151  	}
   152  }
   153  
   154  // secureGRPCClient creates a secure GRPC client using the given public key
   155  func (suite *SecureGRPCTestSuite) secureGRPCClient(publicKey crypto.PublicKey) (accessproto.AccessAPIClient, io.Closer) {
   156  	tlsConfig, err := grpcutils.DefaultClientTLSConfig(publicKey)
   157  	assert.NoError(suite.T(), err)
   158  
   159  	conn, err := grpc.Dial(
   160  		suite.rpcEng.SecureGRPCAddress().String(),
   161  		grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
   162  	assert.NoError(suite.T(), err)
   163  
   164  	client := accessproto.NewAccessAPIClient(conn)
   165  	closer := io.Closer(conn)
   166  	return client, closer
   167  }