From 32d8d82795e4f7ad0383c7534d642672bd29b8d6 Mon Sep 17 00:00:00 2001 From: Antonio Mindov Date: Wed, 30 Nov 2022 14:11:42 +0200 Subject: [PATCH] Added fix for INVALID_NODE_ACCOUNT (#780) (#783) Signed-off-by: Antonio Mindov Signed-off-by: Antonio Mindov --- app/clients/hedera/client.go | 123 ++++++++++++++++++++---------- app/clients/hedera/client_test.go | 41 ++++++++++ 2 files changed, 123 insertions(+), 41 deletions(-) create mode 100644 app/clients/hedera/client_test.go diff --git a/app/clients/hedera/client.go b/app/clients/hedera/client.go index 78050ef64..d4a28b210 100644 --- a/app/clients/hedera/client.go +++ b/app/clients/hedera/client.go @@ -18,7 +18,6 @@ package hedera import ( "fmt" - "github.com/hashgraph/hedera-sdk-go/v2" "github.com/limechain/hedera-eth-bridge-validator/app/model/transfer" "github.com/limechain/hedera-eth-bridge-validator/config" @@ -125,50 +124,69 @@ func (hc Node) SubmitScheduledTokenBurnTransaction(tokenID hedera.TokenID, amoun // SubmitTopicConsensusMessage submits the provided message bytes to the // specified HCS `topicId` func (hc Node) SubmitTopicConsensusMessage(topicId hedera.TopicID, message []byte) (*hedera.TransactionID, error) { - tx, err := hedera.NewTopicMessageSubmitTransaction(). - SetTopicID(topicId). - SetMessage(message). - SetMaxRetry(hc.maxRetry). - FreezeWith(hc.client) + attempt := 0 + for attempt <= hc.maxRetry { + attempt++ + tx, err := hedera.NewTopicMessageSubmitTransaction(). + SetTopicID(topicId). + SetMessage(message). + SetMaxRetry(hc.maxRetry). + FreezeWith(hc.client) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - hc.logger.Debugf("Submit Topic Consensus Message, with transaction ID [%s] and Node Account IDs: %v", - tx.GetTransactionID(), - tx.GetNodeAccountIDs(), - ) + hc.logger.Debugf("Submit Topic Consensus Message, with transaction ID [%s] and Node Account IDs: %v", + tx.GetTransactionID(), + tx.GetNodeAccountIDs(), + ) - response, err := tx.Execute(hc.GetClient()) + response, err := tx.Execute(hc.GetClient()) + if shouldRetryTransaction(err) { + continue + } - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - _, err = hc.checkTransactionReceipt(response) + _, err = hc.checkTransactionReceipt(response) - return &response.TransactionID, err + return &response.TransactionID, err + } + + return nil, fmt.Errorf("Too many retries to submit topic message to %s", topicId.String()) } // SubmitScheduleSign submits a ScheduleSign transaction for a given ScheduleID func (hc Node) SubmitScheduleSign(scheduleID hedera.ScheduleID) (*hedera.TransactionResponse, error) { - tx, err := hedera.NewScheduleSignTransaction(). - SetScheduleID(scheduleID). - SetMaxRetry(hc.maxRetry). - FreezeWith(hc.GetClient()) + attempt := 0 + for attempt <= hc.maxRetry { + attempt++ + tx, err := hedera.NewScheduleSignTransaction(). + SetScheduleID(scheduleID). + SetMaxRetry(hc.maxRetry). + FreezeWith(hc.GetClient()) - if err != nil { - return nil, err + if err != nil { + return nil, err + } + + hc.logger.Debugf("Submit Schedule Sign, with transaction ID [%s], schedule ID: [%s] and Node Account IDs: %v", + tx.GetTransactionID(), + tx.GetScheduleID(), + tx.GetNodeAccountIDs(), + ) + response, err := tx.Execute(hc.GetClient()) + if shouldRetryTransaction(err) { + continue + } + + return &response, err } - hc.logger.Debugf("Submit Schedule Sign, with transaction ID [%s], schedule ID: [%s] and Node Account IDs: %v", - tx.GetTransactionID(), - tx.GetScheduleID(), - tx.GetNodeAccountIDs(), - ) - response, err := tx.Execute(hc.GetClient()) - return &response, err + return nil, fmt.Errorf("[%s] Too many retries", scheduleID) } // SubmitScheduledTokenTransferTransaction creates a token transfer transaction and submits it as a scheduled transaction @@ -241,17 +259,27 @@ func (hc Node) submitScheduledTransferTransaction(payerAccountID hedera.AccountI } func (hc Node) submitScheduledTransaction(signedTransaction hedera.ITransaction, payerAccountID hedera.AccountID, memo string) (*hedera.TransactionResponse, error) { - scheduledTx, err := hedera.NewScheduleCreateTransaction(). - SetScheduledTransaction(signedTransaction) - if err != nil { - return nil, err + attempt := 0 + for attempt <= hc.maxRetry { + attempt++ + scheduledTx, err := hedera.NewScheduleCreateTransaction(). + SetScheduledTransaction(signedTransaction) + if err != nil { + return nil, err + } + scheduledTx = scheduledTx. + SetPayerAccountID(payerAccountID). + SetScheduleMemo(memo) + + response, err := scheduledTx.Execute(hc.GetClient()) + if shouldRetryTransaction(err) { + continue + } + + return &response, err } - scheduledTx = scheduledTx. - SetPayerAccountID(payerAccountID). - SetScheduleMemo(memo) - response, err := scheduledTx.Execute(hc.GetClient()) - return &response, err + return nil, fmt.Errorf("[%s] Too many retries", memo) } func (hc Node) checkTransactionReceipt(txResponse hedera.TransactionResponse) (*hedera.TransactionReceipt, error) { @@ -266,3 +294,16 @@ func (hc Node) checkTransactionReceipt(txResponse hedera.TransactionResponse) (* return &receipt, err } + +func shouldRetryTransaction(err error) bool { + if err == nil { + return false + } + + preCheckError, ok := err.(*hedera.ErrHederaPreCheckStatus) + if ok && preCheckError.Status == hedera.StatusInvalidNodeAccount { + return true + } + + return false +} diff --git a/app/clients/hedera/client_test.go b/app/clients/hedera/client_test.go new file mode 100644 index 000000000..76ec6bbf2 --- /dev/null +++ b/app/clients/hedera/client_test.go @@ -0,0 +1,41 @@ +/* + * Copyright 2022 LimeChain Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hedera + +import ( + "fmt" + "github.com/hashgraph/hedera-sdk-go/v2" + "github.com/stretchr/testify/assert" + "testing" +) + +func Test_shouldRetryTransactionInvalidNodeAccount(t *testing.T) { + err := hedera.ErrHederaPreCheckStatus{Status: hedera.StatusInvalidNodeAccount} + assert.True(t, shouldRetryTransaction(&err)) +} + +func Test_shouldRetryNoError(t *testing.T) { + assert.False(t, shouldRetryTransaction(nil)) +} + +func Test_shouldRetryTransactionUnknownError(t *testing.T) { + err := fmt.Errorf("some error") + assert.False(t, shouldRetryTransaction(err)) + + err = &hedera.ErrHederaPreCheckStatus{Status: hedera.StatusAccountDeleted} + assert.False(t, shouldRetryTransaction(err)) +}