diff --git a/protocol/daemons/bridge/client/client.go b/protocol/daemons/bridge/client/client.go index ef47e1fae9..4cad0cc8c0 100644 --- a/protocol/daemons/bridge/client/client.go +++ b/protocol/daemons/bridge/client/client.go @@ -55,16 +55,19 @@ func Start( } }() + // Initialize gRPC clients from query connection and daemon server connection. queryClient := bridgetypes.NewQueryClient(queryConn) serviceClient := api.NewBridgeServiceClient(daemonConn) + // Initialize an Ethereum client from an RPC endpoint. ethClient, err := ethclient.Dial(flags.Bridge.EthRpcEndpoint) if err != nil { - logger.Error("Failed to establish connection to Ethereum Node", "error", err) + logger.Error("Failed to establish connection to Ethereum node", "error", err) return err } defer func() { ethClient.Close() }() + // Run the main task loop at an interval. ticker := time.NewTicker(time.Duration(flags.Bridge.LoopDelayMs) * time.Millisecond) for ; true; <-ticker.C { if err := RunBridgeDaemonTaskLoop( @@ -84,7 +87,7 @@ func Start( // RunBridgeDaemonTaskLoop does the following: // 1) Fetches configuration information by querying the gRPC server. -// 2) Fetches Ethereum events from a configured node. +// 2) Fetches Ethereum events from a configured Ethereum client. // 3) Sends newly-recognized bridge events to the gRPC server. func RunBridgeDaemonTaskLoop( ctx context.Context, @@ -100,7 +103,15 @@ func RunBridgeDaemonTaskLoop( metrics.Latency, ) - // Fetch parameters from x/bridge module. + // Fetch parameters from x/bridge module. Relevant ones to bridge daemon are: + // - EventParams + // - ChainId: Ethereum chain ID that bridge contract resides on. + // - EthAddress: Address of the bridge contract to query events from. + // - ProposeParams + // - MaxBridgesPerBlock: Number of bridge events to query for. + // - RecognizedEventInfo + // - EthBlockHeight: Ethereum block height from which to start querying events. + // - NextId: Next bridge event ID to query for. eventParams, err := queryClient.EventParams(ctx, &bridgetypes.QueryEventParamsRequest{}) if err != nil { return err @@ -146,7 +157,7 @@ func RunBridgeDaemonTaskLoop( ) // Parse logs into bridge events. - newBridgeEvents := []bridgetypes.BridgeEvent{} + newBridgeEvents := make([]bridgetypes.BridgeEvent, 0, len(logs)) for _, log := range logs { newBridgeEvents = append( newBridgeEvents, @@ -161,20 +172,21 @@ func RunBridgeDaemonTaskLoop( return err } - // Success + // Success. return nil } -// getFilterQuery returns a FilterQuery for fetching logs for the next `numIds` -// bridge events after block height `fromBlock` and before current finalized -// block height. +// getFilterQuery returns a query to fetch logs of bridge events with following filters: +// - logs are emitted by contract at address `contractAddressHex`. +// - block height is between `fromBlock` and current finalized block height (both inclusive). +// - event IDs are sequential integers between `firstId` and `firstId + numIds - 1` (both inclusive). func getFilterQuery( contractAddressHex string, fromBlock uint64, firstId uint32, numIds uint32, ) eth.FilterQuery { - // Generate bytes32 of the next x ids. + // Generate `ethcommon.Hash`s of the next `numIds` event IDs. eventIdHashes := make([]ethcommon.Hash, numIds) for i := uint32(0); i < numIds; i++ { h := ethcommon.BigToHash(big.NewInt(int64(firstId + i))) diff --git a/protocol/daemons/bridge/client/client_test.go b/protocol/daemons/bridge/client/client_test.go index bb58922d38..2fb3800782 100644 --- a/protocol/daemons/bridge/client/client_test.go +++ b/protocol/daemons/bridge/client/client_test.go @@ -3,16 +3,16 @@ package client_test import ( "errors" "fmt" - appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" - "github.com/dydxprotocol/v4-chain/protocol/testutil/appoptions" "math/big" "testing" "github.com/cometbft/cometbft/libs/log" + appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" "github.com/dydxprotocol/v4-chain/protocol/daemons/bridge/client" d_constants "github.com/dydxprotocol/v4-chain/protocol/daemons/constants" "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" "github.com/dydxprotocol/v4-chain/protocol/mocks" + "github.com/dydxprotocol/v4-chain/protocol/testutil/appoptions" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" "github.com/dydxprotocol/v4-chain/protocol/testutil/grpc" bridgetypes "github.com/dydxprotocol/v4-chain/protocol/x/bridge/types" @@ -24,6 +24,7 @@ import ( func TestStart_TcpConnectionFails(t *testing.T) { errorMsg := "Failed to create connection" + // Mock the gRPC client to return an error when creating a TCP connection. mockGrpcClient := &mocks.GrpcClient{} mockGrpcClient.On("NewTcpConnection", grpc.Ctx, d_constants.DefaultGrpcEndpoint).Return(nil, errors.New(errorMsg)) @@ -46,6 +47,10 @@ func TestStart_TcpConnectionFails(t *testing.T) { func TestStart_UnixSocketConnectionFails(t *testing.T) { errorMsg := "Failed to create connection" + // Mock the gRPC client to + // - return a successful TCP connection. + // - return an error when creating a gRPC connection. + // - successfully close the TCP connection. mockGrpcClient := &mocks.GrpcClient{} mockGrpcClient.On("NewTcpConnection", grpc.Ctx, d_constants.DefaultGrpcEndpoint).Return(grpc.GrpcConn, nil) mockGrpcClient.On("NewGrpcConnection", grpc.Ctx, grpc.SocketPath).Return(nil, errors.New(errorMsg)) @@ -64,7 +69,10 @@ func TestStart_UnixSocketConnectionFails(t *testing.T) { ) mockGrpcClient.AssertCalled(t, "NewTcpConnection", grpc.Ctx, d_constants.DefaultGrpcEndpoint) mockGrpcClient.AssertCalled(t, "NewGrpcConnection", grpc.Ctx, grpc.SocketPath) + + // Assert that the connection from NewTcpConnection is closed. mockGrpcClient.AssertNumberOfCalls(t, "CloseConnection", 1) + mockGrpcClient.AssertCalled(t, "CloseConnection", grpc.GrpcConn) } func TestRunBridgeDaemonTaskLoop(t *testing.T) {