diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 2918a79d..8a3f8ea3 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -30,9 +30,6 @@ import ( ) const ( - // The maximum gas limit that can be specified for a Teleporter message - // Based on the C-Chain 15_000_000 gas limit per block, with other Warp message gas overhead conservatively estimated. - maxTeleporterGasLimit = 12_000_000 defaultBlockAcceptanceTimeout = 30 * time.Second ) @@ -165,15 +162,16 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie if err != nil { return false, fmt.Errorf("failed to calculate Teleporter message ID: %w", err) } - + requiredGasLimit := m.teleporterMessage.RequiredGasLimit.Uint64() + destBlockGasLimit := destinationClient.BlockGasLimit() // Check if the specified gas limit is below the maximum threshold - if m.teleporterMessage.RequiredGasLimit.Uint64() > maxTeleporterGasLimit { + if requiredGasLimit > destBlockGasLimit { m.logger.Info( "Gas limit exceeds maximum threshold", zap.String("destinationBlockchainID", destinationBlockchainID.String()), zap.String("teleporterMessageID", teleporterMessageID.String()), zap.Uint64("requiredGasLimit", m.teleporterMessage.RequiredGasLimit.Uint64()), - zap.Uint64("maxGasLimit", maxTeleporterGasLimit), + zap.Uint64("blockGasLimit", destBlockGasLimit), ) return false, nil } diff --git a/messages/teleporter/message_handler_test.go b/messages/teleporter/message_handler_test.go index 370b5b89..ddc3384b 100644 --- a/messages/teleporter/message_handler_test.go +++ b/messages/teleporter/message_handler_test.go @@ -116,8 +116,9 @@ func TestShouldSendMessage(t *testing.T) { ) require.NoError(t, err) + const blockGasLimit = 10_000 gasLimitExceededTeleporterMessage := validTeleporterMessage - gasLimitExceededTeleporterMessage.RequiredGasLimit = big.NewInt(maxTeleporterGasLimit + 1) + gasLimitExceededTeleporterMessage.RequiredGasLimit = big.NewInt(blockGasLimit + 1) gasLimitExceededTeleporterMessageBytes, err := gasLimitExceededTeleporterMessage.Pack() require.NoError(t, err) @@ -233,6 +234,7 @@ func TestShouldSendMessage(t *testing.T) { SenderAddress(). Return(test.senderAddressResult). Times(test.senderAddressTimes) + mockClient.EXPECT().BlockGasLimit().Return(uint64(blockGasLimit)).AnyTimes() mockClient.EXPECT().DestinationBlockchainID().Return(destinationBlockchainID).AnyTimes() if test.messageReceivedCall != nil { messageReceivedInput := interfaces.CallMsg{ diff --git a/relayer/config/destination_blockchain.go b/relayer/config/destination_blockchain.go index 2828468f..2e0ef31e 100644 --- a/relayer/config/destination_blockchain.go +++ b/relayer/config/destination_blockchain.go @@ -13,6 +13,12 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +const ( + // The block gas limit that can be specified for a Teleporter message + // Based on the C-Chain 15_000_000 gas limit per block, with other Warp message gas overhead conservatively estimated. + defaultBlockGasLimit = 12_000_000 +) + // Destination blockchain configuration. Specifies how to connect to and issue // transactions on the destination blockchain. type DestinationBlockchain struct { @@ -23,6 +29,7 @@ type DestinationBlockchain struct { KMSKeyID string `mapstructure:"kms-key-id" json:"kms-key-id"` KMSAWSRegion string `mapstructure:"kms-aws-region" json:"kms-aws-region"` AccountPrivateKey string `mapstructure:"account-private-key" json:"account-private-key"` + BlockGasLimit uint64 `mapstructure:"block-gas-limit" json:"block-gas-limit"` // Fetched from the chain after startup warpConfig WarpConfig @@ -34,6 +41,9 @@ type DestinationBlockchain struct { // Validates the destination subnet configuration func (s *DestinationBlockchain) Validate() error { + if s.BlockGasLimit == 0 { + s.BlockGasLimit = defaultBlockGasLimit + } if err := s.RPCEndpoint.Validate(); err != nil { return fmt.Errorf("invalid rpc-endpoint in destination subnet configuration: %w", err) } @@ -68,6 +78,11 @@ func (s *DestinationBlockchain) Validate() error { } s.subnetID = subnetID + if s.subnetID == constants.PrimaryNetworkID && + s.BlockGasLimit > defaultBlockGasLimit { + return fmt.Errorf("C-Chain block-gas-limit '%d' exceeded", s.BlockGasLimit) + } + return nil } diff --git a/vms/destination_client.go b/vms/destination_client.go index 81103b01..d2300996 100644 --- a/vms/destination_client.go +++ b/vms/destination_client.go @@ -34,9 +34,14 @@ type DestinationClient interface { // DestinationBlockchainID returns the ID of the destination chain DestinationBlockchainID() ids.ID + + // BlockGasLimit returns destination blockchain block gas limit + BlockGasLimit() uint64 } -func NewDestinationClient(logger logging.Logger, subnetInfo *config.DestinationBlockchain) (DestinationClient, error) { +func NewDestinationClient( + logger logging.Logger, subnetInfo *config.DestinationBlockchain, +) (DestinationClient, error) { switch config.ParseVM(subnetInfo.VM) { case config.EVM: return evm.NewDestinationClient(logger, subnetInfo) diff --git a/vms/evm/destination_client.go b/vms/evm/destination_client.go index 9c1579b8..6c57e93d 100644 --- a/vms/evm/destination_client.go +++ b/vms/evm/destination_client.go @@ -44,6 +44,7 @@ type destinationClient struct { signer signer.Signer evmChainID *big.Int currentNonce uint64 + blockGasLimit uint64 logger logging.Logger } @@ -51,34 +52,34 @@ func NewDestinationClient( logger logging.Logger, destinationBlockchain *config.DestinationBlockchain, ) (*destinationClient, error) { - // Dial the destination RPC endpoint - client, err := utils.NewEthClientWithConfig( - context.Background(), - destinationBlockchain.RPCEndpoint.BaseURL, - destinationBlockchain.RPCEndpoint.HTTPHeaders, - destinationBlockchain.RPCEndpoint.QueryParams, - ) + destinationID, err := ids.FromString(destinationBlockchain.BlockchainID) if err != nil { logger.Error( - "Failed to dial rpc endpoint", + "Could not decode destination chain ID from string", zap.Error(err), ) return nil, err } - destinationID, err := ids.FromString(destinationBlockchain.BlockchainID) + sgnr, err := signer.NewSigner(destinationBlockchain) if err != nil { logger.Error( - "Could not decode destination chain ID from string", + "Failed to create signer", zap.Error(err), ) return nil, err } - sgnr, err := signer.NewSigner(destinationBlockchain) + // Dial the destination RPC endpoint + client, err := utils.NewEthClientWithConfig( + context.Background(), + destinationBlockchain.RPCEndpoint.BaseURL, + destinationBlockchain.RPCEndpoint.HTTPHeaders, + destinationBlockchain.RPCEndpoint.QueryParams, + ) if err != nil { logger.Error( - "Failed to create signer", + "Failed to dial rpc endpoint", zap.Error(err), ) return nil, err @@ -117,6 +118,7 @@ func NewDestinationClient( evmChainID: evmChainID, currentNonce: nonce, logger: logger, + blockGasLimit: destinationBlockchain.BlockGasLimit, }, nil } @@ -210,3 +212,7 @@ func (c *destinationClient) SenderAddress() common.Address { func (c *destinationClient) DestinationBlockchainID() ids.ID { return c.destinationBlockchainID } + +func (c *destinationClient) BlockGasLimit() uint64 { + return c.blockGasLimit +} diff --git a/vms/mocks/mock_destination_client.go b/vms/mocks/mock_destination_client.go index 3fdf0305..43b9852d 100644 --- a/vms/mocks/mock_destination_client.go +++ b/vms/mocks/mock_destination_client.go @@ -42,6 +42,20 @@ func (m *MockDestinationClient) EXPECT() *MockDestinationClientMockRecorder { return m.recorder } +// BlockGasLimit mocks base method. +func (m *MockDestinationClient) BlockGasLimit() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockGasLimit") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// BlockGasLimit indicates an expected call of BlockGasLimit. +func (mr *MockDestinationClientMockRecorder) BlockGasLimit() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockGasLimit", reflect.TypeOf((*MockDestinationClient)(nil).BlockGasLimit)) +} + // Client mocks base method. func (m *MockDestinationClient) Client() any { m.ctrl.T.Helper()