diff --git a/bridge/setu/listener/tron.go b/bridge/setu/listener/tron.go index 7e9fa088..5bbfed6d 100644 --- a/bridge/setu/listener/tron.go +++ b/bridge/setu/listener/tron.go @@ -221,6 +221,18 @@ func (tl *TronListener) queryAndBroadcastEvents(chainManagerParams *chainmanager logBytes, _ := json.Marshal(vLog) if selectedEvent != nil { tl.Logger.Debug("ReceivedTronEvent", "eventname", selectedEvent.Name) + + receipt, err := tl.contractConnector.GetTronTransactionReceipt(vLog.TxHash.Hex()) + if receipt != nil && !helper.IsTronTransactionReceiptSuccessful(receipt) { + tl.Logger.Error( + "Skip failed tron transaction event", + "eventname", selectedEvent.Name, + "txHash", vLog.TxHash.Hex(), + "error", err, + ) + continue + } + switch selectedEvent.Name { case "NewHeaderBlock": if isCurrentValidator, delay := util.CalculateTaskDelay(tl.cliCtx); isCurrentValidator { diff --git a/bridge/setu/processor/clerk.go b/bridge/setu/processor/clerk.go index 0ea9ccbd..f4e24464 100644 --- a/bridge/setu/processor/clerk.go +++ b/bridge/setu/processor/clerk.go @@ -84,7 +84,27 @@ func (cp *ClerkProcessor) sendStateSyncedToHeimdall(eventName string, logBytes s return nil } - cp.Logger.Debug( + if helper.GetConfig().CloseOriginTokenDeposit { + shouldBroadcast, err := cp.shouldBroadcastStateSyncedEvent(event.Data, rootChainType) + if err != nil { + cp.Logger.Error("Error while checking state sync token type", "error", err) + return err + } + if !shouldBroadcast { + cp.Logger.Info("Ignoring deposit event for non-mintable ERC20 token", + "event", eventName, + "id", event.Id, + "contract", event.ContractAddress, + "data", hex.EncodeToString(event.Data), + "txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()), + "logIndex", uint64(vLog.Index), + "rootChainType", rootChainType, + ) + return nil + } + } + + cp.Logger.Info( "⬜ New event found", "event", eventName, "id", event.Id, @@ -118,6 +138,28 @@ func (cp *ClerkProcessor) sendStateSyncedToHeimdall(eventName string, logBytes s return nil } +func (cp *ClerkProcessor) shouldBroadcastStateSyncedEvent(data []byte, rootChainType string) (bool, error) { + stateData, err := helper.ParseStateSyncData(data) + if err != nil { + return false, err + } + if stateData.EventType != helper.StateSyncEventDeposit { + return true, nil + } + + return false, nil + // rootChainManagerProxy, err := helper.GetRootChainManagerProxy(rootChainType) + // if err != nil { + // return false, err + // } + // tokenType, err := cp.contractConnector.GetRootTokenType(rootChainType, rootChainManagerProxy, stateData.RootToken) + // if err != nil { + // return false, err + // } + + // return tokenType == helper.MintableERC20TokenHash, nil +} + // isOldTx checks if tx is already processed or not func (cp *ClerkProcessor) isOldTx(cliCtx cliContext.CLIContext, txHash string, logIndex uint64, rootChainType string) (bool, error) { queryParam := map[string]interface{}{ diff --git a/chainmanager/side_handler.go b/chainmanager/side_handler.go index 1415e55a..3feab1d1 100644 --- a/chainmanager/side_handler.go +++ b/chainmanager/side_handler.go @@ -49,10 +49,14 @@ func SideHandleMsgNewChain(ctx sdk.Context, msg types.MsgNewChain, k Keeper, con err error ) // get event log on tron - receipt, err = contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err = contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return common.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return common.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } contractAddress = hmTypes.HexToTronAddress(chainParams.TronChainAddress) // decode validator join event eventLog, err := contractCaller.DecodeNewChainEvent(contractAddress, receipt, msg.LogIndex) diff --git a/checkpoint/client/cli/tx.go b/checkpoint/client/cli/tx.go index ae074ead..d230683e 100644 --- a/checkpoint/client/cli/tx.go +++ b/checkpoint/client/cli/tx.go @@ -240,10 +240,16 @@ func SendCheckpointACKTx(cdc *codec.Codec) *cobra.Command { } rootChainAddress = chainmanagerParams.ChainParams.RootChainAddress.EthAddress() case hmTypes.RootChainTypeTron: - receipt, err = contractCallerObj.GetTronTransactionReceipt(txHash.Hex()) + receipt, err = contractCallerObj.GetTronConfirmedTxReceipt( + txHash.Hex(), + chainmanagerParams.TronchainTxConfirmations, + ) if err != nil || receipt == nil { return errors.New("transaction is not confirmed yet. Please wait for sometime and try again") } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + return errors.New("tron transaction failed") + } rootChainAddress = hmTypes.HexToTronAddress(chainmanagerParams.ChainParams.TronChainAddress) default: return fmt.Errorf("wrong root chain %v", rootChain) diff --git a/clerk/querier.go b/clerk/querier.go index 4531571e..8725569f 100644 --- a/clerk/querier.go +++ b/clerk/querier.go @@ -119,7 +119,10 @@ func handleQueryRecordSequence(ctx sdk.Context, req abci.RequestQuery, keeper Ke receipt, err = contractCallerObj.GetConfirmedTxReceipt(hmTypes.HexToHeimdallHash(params.TxHash).EthHash(), bscChain.TxConfirmations, hmTypes.RootChainTypeBsc, false) case hmTypes.RootChainTypeTron: - receipt, err = contractCallerObj.GetTronTransactionReceipt(hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex()) + receipt, err = contractCallerObj.GetTronConfirmedTxReceipt( + hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex(), + chainParams.TronchainTxConfirmations, + ) default: return nil, sdk.ErrInternal(fmt.Sprintf("wrong chain type = " + params.RootChainType + "please pass correct chainType like eth or tron")) } diff --git a/clerk/querier_test.go b/clerk/querier_test.go index 3416ced1..ecd0f2c5 100644 --- a/clerk/querier_test.go +++ b/clerk/querier_test.go @@ -246,7 +246,7 @@ func (suite *QuerierTestSuite) TestHandleQueryRecordSequence() { // tron testSeq = helper.CalculateSequence(big.NewInt(1), 1, hmTypes.RootChainTypeTron).String() ck.SetRecordSequence(ctx, testSeq) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.TronHash().String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.TronHash().String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) req = abci.RequestQuery{ Path: route, Data: app.Codec().MustMarshalJSON(types.NewQueryRecordSequenceParams("12345", logIndex, hmTypes.RootChainTypeTron)), diff --git a/clerk/side_handler.go b/clerk/side_handler.go index 4111a96c..e399b3b9 100644 --- a/clerk/side_handler.go +++ b/clerk/side_handler.go @@ -93,10 +93,14 @@ func SideHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecor } contractAddress = bscChain.StateSenderAddress.EthAddress() case hmTypes.RootChainTypeTron: - receipt, err = contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err = contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } contractAddress = hmTypes.HexToTronAddress(chainParams.TronStateSenderAddress) default: k.Logger(ctx).Error("RootChain type: ", msg.RootChainType, " does not match eth or tron") @@ -138,6 +142,18 @@ func SideHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecor return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) } + if helper.GetConfig().CloseOriginTokenDeposit { + shouldVote, err := shouldVoteStateSyncedEvent(contractCaller, msg.RootChainType, msg.Data) + if err != nil { + k.Logger(ctx).Error("Error parsing state sync data", "error", err) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeErrDecodeEvent) + } + if !shouldVote { + k.Logger(ctx).Error("Deposit token type is not mintable ERC20", "rootChainType", msg.RootChainType, "txHash", msg.TxHash.Hex()) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } + } + result.Result = abci.SideTxResultType_Yes return } @@ -211,3 +227,25 @@ func PostHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecor Events: ctx.EventManager().Events(), } } + +func shouldVoteStateSyncedEvent(contractCaller helper.IContractCaller, rootChainType string, data []byte) (bool, error) { + stateData, err := helper.ParseStateSyncData(data) + if err != nil { + return false, err + } + if stateData.EventType != helper.StateSyncEventDeposit { + return true, nil + } + return false, nil + + // rootChainManagerProxy, err := helper.GetRootChainManagerProxy(rootChainType) + // if err != nil { + // return false, err + // } + // tokenType, err := contractCaller.GetRootTokenType(rootChainType, rootChainManagerProxy, stateData.RootToken) + // if err != nil { + // return false, err + // } + + // return tokenType == helper.MintableERC20TokenHash, nil +} diff --git a/clerk/side_handler_test.go b/clerk/side_handler_test.go index 4d5a256e..ca8a6dff 100644 --- a/clerk/side_handler_test.go +++ b/clerk/side_handler_test.go @@ -1,8 +1,10 @@ package clerk_test import ( + "encoding/hex" "math/big" "math/rand" + "strings" "testing" "time" @@ -14,6 +16,7 @@ import ( "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + ethCommon "github.com/ethereum/go-ethereum/common" ethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/maticnetwork/heimdall/app" "github.com/maticnetwork/heimdall/clerk" @@ -62,9 +65,7 @@ func TestSideHandlerTestSuite(t *testing.T) { suite.Run(t, new(SideHandlerTestSuite)) } -// // Test cases -// func (suite *SideHandlerTestSuite) TestSideHandler() { t, ctx := suite.T(), suite.ctx @@ -88,6 +89,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -131,6 +133,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("hello tron") @@ -147,7 +150,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { suite.chainID, hmTypes.RootChainTypeTron, ) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.Hex()).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.Hex(), chainParams.TronchainTxConfirmations).Return(txReceipt, nil) event := &statesender.StatesenderStateSynced{ Id: new(big.Int).SetUint64(msg.ID), ContractAddress: msg.ContractAddress.TronAddress(), @@ -166,6 +169,95 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { require.Error(t, err) }) + + t.Run("CloseOriginTokenDeposit", func(t *testing.T) { + suite.contractCaller = mocks.IContractCaller{} + suite.sideHandler = clerk.NewSideTxHandler(suite.app.ClerkKeeper, &suite.contractCaller) + conf := helper.GetDefaultHeimdallConfig() + conf.CloseOriginTokenDeposit = true + conf.EthRootChainManagerProxy = "0x0000000000000000000000000000000000000001" + helper.SetTestConfig(conf) + defer helper.SetTestConfig(helper.GetDefaultHeimdallConfig()) + + logIndex := uint64(11) + blockNumber := uint64(600) + txReceipt := ðTypes.Receipt{ + BlockNumber: new(big.Int).SetUint64(blockNumber), + } + txHash := hmTypes.HexToHeimdallHash("mintable deposit") + data, err := hex.DecodeString(strings.TrimPrefix(depositStateSyncData, "0x")) + require.NoError(t, err) + rootToken := ethCommon.HexToAddress("0x032017411f4663b317fe77c257d28d5cd1b26e3d") + + msg := types.NewMsgEventRecord( + hmTypes.BytesToHeimdallAddress(addr1.Bytes()), + txHash, + logIndex, + blockNumber, + id, + hmTypes.BytesToHeimdallAddress(addr1.Bytes()), + data, + suite.chainID, + hmTypes.RootChainTypeEth, + ) + + suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth).Return(txReceipt, nil) + event := &statesender.StatesenderStateSynced{ + Id: new(big.Int).SetUint64(msg.ID), + ContractAddress: msg.ContractAddress.EthAddress(), + Data: msg.Data, + } + suite.contractCaller.On("DecodeStateSyncedEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) + suite.contractCaller.On("GetRootTokenType", hmTypes.RootChainTypeEth, conf.EthRootChainManagerProxy, rootToken).Return(helper.MintableERC20TokenHash, nil) + + result := suite.sideHandler(ctx, msg) + require.Equal(t, uint32(common.CodeInvalidMsg), result.Code) + require.Equal(t, abci.SideTxResultType_Skip, result.Result) + }) + t.Run("CloseOriginTokenDeposit", func(t *testing.T) { + suite.contractCaller = mocks.IContractCaller{} + suite.sideHandler = clerk.NewSideTxHandler(suite.app.ClerkKeeper, &suite.contractCaller) + conf := helper.GetDefaultHeimdallConfig() + conf.CloseOriginTokenDeposit = true + conf.EthRootChainManagerProxy = "0x0000000000000000000000000000000000000001" + helper.SetTestConfig(conf) + defer helper.SetTestConfig(helper.GetDefaultHeimdallConfig()) + + logIndex := uint64(12) + blockNumber := uint64(601) + txReceipt := ðTypes.Receipt{ + BlockNumber: new(big.Int).SetUint64(blockNumber), + } + txHash := hmTypes.HexToHeimdallHash("non mintable deposit") + data, err := hex.DecodeString(strings.TrimPrefix(depositStateSyncData, "0x")) + require.NoError(t, err) + rootToken := ethCommon.HexToAddress("0x032017411f4663b317fe77c257d28d5cd1b26e3d") + + msg := types.NewMsgEventRecord( + hmTypes.BytesToHeimdallAddress(addr1.Bytes()), + txHash, + logIndex, + blockNumber, + id, + hmTypes.BytesToHeimdallAddress(addr1.Bytes()), + data, + suite.chainID, + hmTypes.RootChainTypeEth, + ) + + suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth).Return(txReceipt, nil) + event := &statesender.StatesenderStateSynced{ + Id: new(big.Int).SetUint64(msg.ID), + ContractAddress: msg.ContractAddress.EthAddress(), + Data: msg.Data, + } + suite.contractCaller.On("DecodeStateSyncedEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) + suite.contractCaller.On("GetRootTokenType", hmTypes.RootChainTypeEth, conf.EthRootChainManagerProxy, rootToken).Return(ethCommon.HexToHash("0x01"), nil) + + result := suite.sideHandler(ctx, msg) + require.NotEqual(t, uint32(sdk.CodeOK), result.Code) + require.Equal(t, abci.SideTxResultType_Skip, result.Result) + }) t.Run("NoReceipt", func(t *testing.T) { suite.contractCaller = mocks.IContractCaller{} @@ -201,6 +293,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { logIndex := uint64(100) blockNumber := uint64(510) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("no log hash") @@ -228,6 +321,8 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgEventRecord() { }) } +const depositStateSyncData = "0x87a7811f4bfedea3d341ad165680ae306b01aaeacc205d227629cf157dd9f821000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000a9635197462ba512b47d19399017f6857888bc27000000000000000000000000032017411f4663b317fe77c257d28d5cd1b26e3d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000056bc75e2d63100000" + func (suite *SideHandlerTestSuite) TestPostHandler() { t, ctx := suite.T(), suite.ctx diff --git a/helper/call.go b/helper/call.go index 56d6aaef..4b5a7125 100644 --- a/helper/call.go +++ b/helper/call.go @@ -11,10 +11,7 @@ import ( "strings" "time" - "github.com/ethereum/go-ethereum" - - "github.com/maticnetwork/heimdall/tron" - + eth "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" ethTypes "github.com/ethereum/go-ethereum/core/types" @@ -29,6 +26,7 @@ import ( "github.com/maticnetwork/heimdall/contracts/statereceiver" "github.com/maticnetwork/heimdall/contracts/statesender" "github.com/maticnetwork/heimdall/contracts/validatorset" + "github.com/maticnetwork/heimdall/tron" "github.com/maticnetwork/heimdall/types" hmTypes "github.com/maticnetwork/heimdall/types" @@ -64,6 +62,7 @@ type IContractCaller interface { DecodeSignerUpdateEvent(common.Address, *ethTypes.Receipt, uint64) (*stakinginfo.StakinginfoSignerChange, error) // decode state events DecodeStateSyncedEvent(common.Address, *ethTypes.Receipt, uint64) (*statesender.StatesenderStateSynced, error) + GetRootTokenType(string, string, common.Address) (common.Hash, error) // decode slashing events DecodeSlashedEvent(common.Address, *ethTypes.Receipt, uint64) (*stakinginfo.StakinginfoSlashed, error) @@ -99,6 +98,7 @@ type IContractCaller interface { GetTronHeaderInfo(headerID uint64, rootChainAddress string, childBlockInterval uint64) (root common.Hash, start, end, createdAt uint64, proposer types.HeimdallAddress, err error) GetTronEventsByContractAddress(address []string, from, to int64) ([]ethTypes.Log, error) GetTronTransactionReceipt(txID string) (*ethTypes.Receipt, error) + GetTronConfirmedTxReceipt(txID string, requiredConfirmations uint64) (*ethTypes.Receipt, error) GetTronLatestBlockNumber() (int64, error) // checkpoint sync @@ -164,7 +164,7 @@ func NewContractCaller() (contractCallerObj ContractCaller, err error) { if err != nil { return contractCallerObj, err } - + contractCallerObj.LatestBlockCache = make(map[string]uint64) contractCallerObj.ContractInstanceCache = make(map[string]interface{}) // package global cache (string->ABI) @@ -288,6 +288,51 @@ func (c *ContractCaller) GetMaticTokenInstance(maticTokenAddress common.Address) return contractInstance.(*erc20.Erc20), nil } +func (c *ContractCaller) GetRootTokenType(rootChainType string, rootChainManagerProxy string, rootToken common.Address) (common.Hash, error) { + data, err := rootChainManagerProxyABI.Pack("tokenToType", rootToken) + if err != nil { + return common.Hash{}, err + } + + var result []byte + switch rootChainType { + case hmTypes.RootChainTypeEth: + contractAddress := common.HexToAddress(rootChainManagerProxy) + result, err = c.MainChainClient.CallContract(context.Background(), eth.CallMsg{ + To: &contractAddress, + Data: data, + }, nil) + case hmTypes.RootChainTypeBsc: + contractAddress := common.HexToAddress(rootChainManagerProxy) + result, err = c.BscChainClient.CallContract(context.Background(), eth.CallMsg{ + To: &contractAddress, + Data: data, + }, nil) + case hmTypes.RootChainTypeTron: + result, err = c.TronChainRPC.TriggerConstantContract(rootChainManagerProxy, data) + default: + return common.Hash{}, errors.New("unknown root chain type") + } + if err != nil { + return common.Hash{}, err + } + + outputs, err := rootChainManagerProxyABI.Unpack("tokenToType", result) + if err != nil { + return common.Hash{}, err + } + if len(outputs) != 1 { + return common.Hash{}, errors.New("invalid tokenToType response") + } + + tokenType, ok := outputs[0].([32]byte) + if !ok { + return common.Hash{}, errors.New("invalid token type") + } + + return common.BytesToHash(tokenType[:]), nil +} + // NewLru create instance of lru func NewLru(size int) (*lru.Cache, error) { lruObj, err := lru.New(size) @@ -470,7 +515,7 @@ func (c *ContractCaller) GetLogs(fromBlock *big.Int, toBlock *big.Int, addrs []c ctx, cancel := context.WithTimeout(context.Background(), c.MaticChainTimeout) defer cancel() - logs, err := c.MaticChainClient.FilterLogs(ctx, ethereum.FilterQuery{ //nolint:typecheck + logs, err := c.MaticChainClient.FilterLogs(ctx, eth.FilterQuery{ FromBlock: fromBlock, ToBlock: toBlock, Addresses: addrs, @@ -527,7 +572,7 @@ func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmat } } else { latestBlkNumber := c.LatestBlockCache[rootChain] - if latestBlkNumber-receipt.BlockNumber.Uint64() >= requiredConfirmations { + if latestBlkNumber >= receiptBlockNumber && latestBlkNumber-receiptBlockNumber >= requiredConfirmations { Logger.Debug("receipt block is confirmed by cache", "root", rootChain, "latestBlockCached", latestBlkNumber, "receiptBlock", receipt.BlockNumber.Uint64()) @@ -541,9 +586,9 @@ func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmat return nil, err } Logger.Debug("Latest block on main chain obtained", "root", rootChain, "Block", latestBlk.Number.Uint64()) - c.LatestBlockCache[rootChain] = latestBlk.Number.Uint64() - diff := latestBlk.Number.Uint64() - receipt.BlockNumber.Uint64() - if diff < requiredConfirmations { + latestBlkNumber = latestBlk.Number.Uint64() + c.LatestBlockCache[rootChain] = latestBlkNumber + if latestBlkNumber < receiptBlockNumber || latestBlkNumber-receiptBlockNumber < requiredConfirmations { return nil, errors.New("not enough confirmations") } } @@ -883,6 +928,70 @@ func (c *ContractCaller) GetTronTransactionReceipt(txID string) (*ethTypes.Recei return &transactionReceipt.Result, nil } +// GetTronConfirmedTxReceipt returns confirmed tron tx receipt. +func (c *ContractCaller) GetTronConfirmedTxReceipt(txID string, requiredConfirmations uint64) (*ethTypes.Receipt, error) { + var receipt *ethTypes.Receipt + cacheKey := hmTypes.RootChainTypeTron + ":" + txID + + if c.ReceiptCache != nil { + if receiptCache, ok := c.ReceiptCache.Get(cacheKey); ok { + receipt, _ = receiptCache.(*ethTypes.Receipt) + } + } + + if receipt == nil { + var err error + + receipt, err = c.GetTronTransactionReceipt(txID) + if err != nil { + Logger.Error("Error while fetching tron receipt", "error", err, "txHash", txID) + return nil, err + } + if receipt == nil || receipt.BlockNumber == nil { + return nil, errors.New("not enough confirmations") + } + + if c.ReceiptCache != nil { + c.ReceiptCache.Add(cacheKey, receipt) + } + } + + receiptBlockNumber := receipt.BlockNumber.Uint64() + Logger.Debug("Tron tx included in block", "root", hmTypes.RootChainTypeTron, "block", receiptBlockNumber, "tx", txID) + + latestBlkNumber := c.LatestBlockCache[hmTypes.RootChainTypeTron] + if latestBlkNumber >= receiptBlockNumber && latestBlkNumber-receiptBlockNumber >= requiredConfirmations { + Logger.Debug("tron receipt block is confirmed by cache", + "root", hmTypes.RootChainTypeTron, "latestBlockCached", latestBlkNumber, "receiptBlock", receiptBlockNumber) + + return receipt, nil + } + + latestBlk, err := c.GetTronLatestBlockNumber() + if err != nil { + Logger.Error("error getting latest block from tron chain", "Error", err) + return nil, err + } + if latestBlk < 0 { + return nil, errors.New("invalid latest tron block number") + } + + latestBlkNumber = uint64(latestBlk) + Logger.Debug("Latest block on tron chain obtained", "root", hmTypes.RootChainTypeTron, "Block", latestBlkNumber) + c.LatestBlockCache[hmTypes.RootChainTypeTron] = latestBlkNumber + + if latestBlkNumber < receiptBlockNumber || latestBlkNumber-receiptBlockNumber < requiredConfirmations { + return nil, errors.New("not enough confirmations") + } + + return receipt, nil +} + +// IsTronTransactionReceiptSuccessful returns true when a Tron transaction receipt indicates success. +func IsTronTransactionReceiptSuccessful(receipt *ethTypes.Receipt) bool { + return receipt != nil && receipt.Status == ethTypes.ReceiptStatusSuccessful +} + // utility and helper methods // populateABIs fills the package level cache for contracts' ABIs diff --git a/helper/call_tron_test.go b/helper/call_tron_test.go new file mode 100644 index 00000000..104ee7ac --- /dev/null +++ b/helper/call_tron_test.go @@ -0,0 +1,41 @@ +package helper + +import ( + "testing" + + ethTypes "github.com/ethereum/go-ethereum/core/types" +) + +func TestIsTronTransactionReceiptSuccessful(t *testing.T) { + testCases := []struct { + name string + receipt *ethTypes.Receipt + want bool + }{ + { + name: "successful receipt", + receipt: ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, + }, + want: true, + }, + { + name: "failed receipt", + receipt: ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusFailed, + }, + }, + { + name: "nil receipt", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := IsTronTransactionReceiptSuccessful(tc.receipt) + if got != tc.want { + t.Fatalf("expected %t, got %t", tc.want, got) + } + }) + } +} diff --git a/helper/config.go b/helper/config.go index 52db40d4..b5af0424 100644 --- a/helper/config.go +++ b/helper/config.go @@ -2,6 +2,7 @@ package helper import ( "crypto/ecdsa" + "errors" "log" "math/big" "os" @@ -10,6 +11,7 @@ import ( "time" "github.com/maticnetwork/heimdall/helper/fork" + hmTypes "github.com/maticnetwork/heimdall/types" "github.com/maticnetwork/heimdall/tron" @@ -105,7 +107,8 @@ const ( DefaultLogsType = "json" DefaultChain = "mainnet" - secretFilePerm = 0600 + secretFilePerm = 0600 + DefaultCloseOriginTokenDeposit = false ) var ( @@ -165,6 +168,12 @@ type Configuration struct { EthMaxQueryBlocks int64 `mapstructure:"eth_max_query_blocks"` // eth max number of blocks in one query logs BscMaxQueryBlocks int64 `mapstructure:"bsc_max_query_blocks"` // bsc max number of blocks in one query logs TronMaxQueryBlocks int64 `mapstructure:"tron_max_query_blocks"` // tron max number of blocks in one query logs + + EthRootChainManagerProxy string `mapstructure:"eth_root_chain_manager_proxy"` // root chain manager proxy for eth + BscRootChainManagerProxy string `mapstructure:"bsc_root_chain_manager_proxy"` // root chain manager proxy for bsc + TronRootChainManagerProxy string `mapstructure:"tron_root_chain_manager_proxy"` // root chain manager proxy for tron + + CloseOriginTokenDeposit bool `mapstructure:"close_origin_token_deposit"` // only allow mintable ERC20 deposits when it is true } var conf Configuration @@ -325,6 +334,8 @@ func GetDefaultHeimdallConfig() Configuration { EthMaxQueryBlocks: DefaultEthMaxQueryBlocks, BscMaxQueryBlocks: DefaultBscMaxQueryBlocks, TronMaxQueryBlocks: DefaultTronMaxQueryBlocks, + + CloseOriginTokenDeposit: DefaultCloseOriginTokenDeposit, } } @@ -333,6 +344,25 @@ func GetConfig() Configuration { return conf } +func GetRootChainManagerProxy(rootChainType string) (string, error) { + var proxy string + switch rootChainType { + case hmTypes.RootChainTypeEth: + proxy = conf.EthRootChainManagerProxy + case hmTypes.RootChainTypeBsc: + proxy = conf.BscRootChainManagerProxy + case hmTypes.RootChainTypeTron: + proxy = conf.TronRootChainManagerProxy + default: + return "", errors.New("unknown root chain type") + } + if proxy == "" { + return "", errors.New("root chain manager proxy is not configured") + } + + return proxy, nil +} + func GetGenesisDoc() tmTypes.GenesisDoc { return GenesisDoc } diff --git a/helper/mocks/IContractCaller.go b/helper/mocks/IContractCaller.go index 2a6d84c3..67519fa9 100644 --- a/helper/mocks/IContractCaller.go +++ b/helper/mocks/IContractCaller.go @@ -727,6 +727,27 @@ func (_m *IContractCaller) GetRootHash(start uint64, end uint64, checkpointLengt return r0, r1 } +// GetRootTokenType provides a mock function with given fields: _a0, _a1, _a2 +func (_m *IContractCaller) GetRootTokenType(_a0 string, _a1 string, _a2 common.Address) (common.Hash, error) { + ret := _m.Mock.Called(_a0, _a1, _a2) + + var r0 common.Hash + if rf, ok := ret.Get(0).(func(string, string, common.Address) common.Hash); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(common.Hash) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, string, common.Address) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetSlashManagerInstance provides a mock function with given fields: slashManagerAddress func (_m *IContractCaller) GetSlashManagerInstance(slashManagerAddress common.Address) (*slashmanager.Slashmanager, error) { ret := _m.Called(slashManagerAddress) @@ -1052,6 +1073,29 @@ func (_m *IContractCaller) GetTronTransactionReceipt(txID string) (*types.Receip return r0, r1 } +// GetTronConfirmedTxReceipt provides a mock function with given fields: txID, requiredConfirmations +func (_m *IContractCaller) GetTronConfirmedTxReceipt(txID string, requiredConfirmations uint64) (*types.Receipt, error) { + ret := _m.Mock.Called(txID, requiredConfirmations) + + var r0 *types.Receipt + if rf, ok := ret.Get(0).(func(string, uint64) *types.Receipt); ok { + r0 = rf(txID, requiredConfirmations) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Receipt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, uint64) error); ok { + r1 = rf(txID, requiredConfirmations) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetValidatorInfo provides a mock function with given fields: valID, stakingInfoInstance func (_m *IContractCaller) GetValidatorInfo(valID heimdalltypes.ValidatorID, stakingInfoInstance *stakinginfo.Stakinginfo) (heimdalltypes.Validator, error) { ret := _m.Called(valID, stakingInfoInstance) diff --git a/helper/state_sync.go b/helper/state_sync.go new file mode 100644 index 00000000..1d46ae42 --- /dev/null +++ b/helper/state_sync.go @@ -0,0 +1,171 @@ +package helper + +import ( + "errors" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +type StateSyncEventType int + +const ( + StateSyncEventUnknown StateSyncEventType = iota + StateSyncEventDeposit + StateSyncEventMapToken +) + +const MintableERC20TokenType = "0x5ffef61af1560b9aefc0e42aaa0f9464854ab113ab7b8bfab271be94cdb1d053" + +var ( + StateSyncDepositTypeHash = crypto.Keccak256Hash([]byte("DEPOSIT")) + StateSyncMapTokenTypeHash = crypto.Keccak256Hash([]byte("MAP_TOKEN")) + MintableERC20TokenHash = common.HexToHash(MintableERC20TokenType) + + stateSyncBytes32Type, _ = abi.NewType("bytes32", "", nil) + stateSyncBytesType, _ = abi.NewType("bytes", "", nil) + stateSyncAddressType, _ = abi.NewType("address", "", nil) + stateSyncUint256Type, _ = abi.NewType("uint256", "", nil) + + rootChainManagerProxyABI, _ = abi.JSON(strings.NewReader(`[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"tokenToType","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}]`)) +) + +type StateSyncData struct { + EventType StateSyncEventType + RootToken common.Address + ChildToken common.Address +} + +// ParseStateSyncData decodes StateSender data encoded as abi.encode(eventType, syncData). +func ParseStateSyncData(data []byte) (*StateSyncData, error) { + stateData, err := parseStateSyncPayload(data) + if err != nil { + return nil, err + } + + return stateData, nil +} + +func parseStateSyncPayload(data []byte) (*StateSyncData, error) { + arguments := abi.Arguments{ + {Type: stateSyncBytes32Type}, + {Type: stateSyncBytesType}, + } + + stateData, err := arguments.Unpack(data) + if err != nil { + return nil, err + } + if len(stateData) != len(arguments) { + return nil, errors.New("invalid state sync data") + } + + eventTypeBytes, ok := stateData[0].([32]byte) + if !ok { + return nil, errors.New("invalid state sync event type") + } + + syncData, ok := stateData[1].([]byte) + if !ok { + return nil, errors.New("invalid state sync payload") + } + + eventType := common.BytesToHash(eventTypeBytes[:]) + switch eventType { + case StateSyncDepositTypeHash: + return parseDepositStateSyncData(syncData) + case StateSyncMapTokenTypeHash: + return parseMapTokenStateSyncData(syncData) + default: + return &StateSyncData{EventType: StateSyncEventUnknown}, errors.New("invalid state sync type") + } +} + +func unwrapStateSyncBytes(data []byte) ([]byte, error) { + arguments := abi.Arguments{ + {Type: stateSyncBytesType}, + } + + stateData, err := arguments.Unpack(data) + if err != nil { + return nil, err + } + if len(stateData) != len(arguments) { + return nil, errors.New("invalid wrapped state sync data") + } + + wrappedData, ok := stateData[0].([]byte) + if !ok { + return nil, errors.New("invalid wrapped state sync payload") + } + + return wrappedData, nil +} + +func parseDepositStateSyncData(data []byte) (*StateSyncData, error) { + arguments := abi.Arguments{ + {Type: stateSyncAddressType}, + {Type: stateSyncAddressType}, + {Type: stateSyncUint256Type}, + {Type: stateSyncBytesType}, + } + + ret, err := arguments.Unpack(data) + if err != nil { + return nil, err + } + if len(ret) != len(arguments) { + return nil, errors.New("invalid deposit state sync data") + } + + rootToken, ok := ret[1].(common.Address) + if !ok { + return nil, errors.New("invalid deposit root token") + } + if _, ok := ret[2].(*big.Int); !ok { + return nil, errors.New("invalid deposit chain id") + } + + return &StateSyncData{ + EventType: StateSyncEventDeposit, + RootToken: rootToken, + }, nil +} + +func parseMapTokenStateSyncData(data []byte) (*StateSyncData, error) { + arguments := abi.Arguments{ + {Type: stateSyncAddressType}, + {Type: stateSyncAddressType}, + {Type: stateSyncUint256Type}, + {Type: stateSyncBytes32Type}, + } + + ret, err := arguments.Unpack(data) + if err != nil { + return nil, err + } + if len(ret) != len(arguments) { + return nil, errors.New("invalid map token state sync data") + } + + rootToken, ok := ret[0].(common.Address) + if !ok { + return nil, errors.New("invalid map token root token") + } + childToken, ok := ret[1].(common.Address) + if !ok { + return nil, errors.New("invalid map token child token") + } + if _, ok := ret[2].(*big.Int); !ok { + return nil, errors.New("invalid map token chain id") + } + + return &StateSyncData{ + EventType: StateSyncEventMapToken, + RootToken: rootToken, + ChildToken: childToken, + }, nil +} diff --git a/helper/state_sync_test.go b/helper/state_sync_test.go new file mode 100644 index 00000000..8a4508ae --- /dev/null +++ b/helper/state_sync_test.go @@ -0,0 +1,32 @@ +package helper + +import ( + "encoding/hex" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestParseStateSyncDataDeposit(t *testing.T) { + rawData := "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000012087a7811f4bfedea3d341ad165680ae306b01aaeacc205d227629cf157dd9f821000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000a9635197462ba512b47d19399017f6857888bc27000000000000000000000000032017411f4663b317fe77c257d28d5cd1b26e3d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000056bc75e2d63100000" + data, err := hex.DecodeString(strings.TrimPrefix(rawData, "0x")) + require.NoError(t, err) + + stateData, err := ParseStateSyncData(data) + require.NoError(t, err) + require.Equal(t, StateSyncEventDeposit, stateData.EventType) + require.Equal(t, common.HexToAddress("0x032017411f4663b317fe77c257d28d5cd1b26e3d"), stateData.RootToken) +} + +func TestParseStateSyncDataMap(t *testing.T) { + rawData := "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e02cef46a936bdc5b7e6e8c71aa04560c41cf7d88bb26901a7e7f4936ff02accad0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000017ad5ac0aae981970a924b6fb409b35e0a53e57d000000000000000000000000483e7435aaeaf229ae53e3c8ae04bb6e5b48e60100000000000000000000000000000000000000000000000000000000000000015ffef61af1560b9aefc0e42aaa0f9464854ab113ab7b8bfab271be94cdb1d053" + data, err := hex.DecodeString(strings.TrimPrefix(rawData, "0x")) + require.NoError(t, err) + + stateData, err := ParseStateSyncData(data) + require.NoError(t, err) + require.Equal(t, StateSyncEventMapToken, stateData.EventType) + //require.Equal(t, common.HexToAddress("0x032017411f4663b317fe77c257d28d5cd1b26e3d"), stateData.RootToken) +} diff --git a/helper/toml.go b/helper/toml.go index 5ba948e6..3dce619d 100644 --- a/helper/toml.go +++ b/helper/toml.go @@ -65,6 +65,14 @@ eth_max_query_blocks = "{{ .EthMaxQueryBlocks }}" bsc_max_query_blocks = "{{ .BscMaxQueryBlocks }}" tron_max_query_blocks = "{{ .TronMaxQueryBlocks }}" +#### root chain manager proxy #### +eth_root_chain_manager_proxy = "{{ .EthRootChainManagerProxy }}" +bsc_root_chain_manager_proxy = "{{ .BscRootChainManagerProxy }}" +tron_root_chain_manager_proxy = "{{ .TronRootChainManagerProxy }}" + +#### state sync filters #### +close_origin_token_deposit = {{ .CloseOriginTokenDeposit }} + ##### Timeout Config ##### no_ack_wait_time = "{{ .NoACKWaitTime }}" diff --git a/slashing/querier.go b/slashing/querier.go index ac4c8ad8..b9010aae 100644 --- a/slashing/querier.go +++ b/slashing/querier.go @@ -223,8 +223,13 @@ func querySlashingSequence(ctx sdk.Context, req abci.RequestQuery, keeper Keeper return nil, sdk.ErrInternal(fmt.Sprintf(err.Error())) } + chainParams := keeper.chainKeeper.GetParams(ctx) + // get main tx receipt - receipt, err := contractCallerObj.GetTronTransactionReceipt(hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex()) + receipt, err := contractCallerObj.GetTronConfirmedTxReceipt( + hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex(), + chainParams.TronchainTxConfirmations, + ) if err != nil || receipt == nil { return nil, sdk.ErrInternal(fmt.Sprintf("Transaction is not confirmed yet. Please wait for sometime and try again")) diff --git a/slashing/side_handler.go b/slashing/side_handler.go index 250be116..c93019e5 100644 --- a/slashing/side_handler.go +++ b/slashing/side_handler.go @@ -74,10 +74,14 @@ func SideHandleMsgTickAck(ctx sdk.Context, k Keeper, msg types.MsgTickAck, contr chainParams := params.ChainParams // get main tx receipt - receipt, err := contractCaller.GetTronTransactionReceipt(msg.TxHash.TronHash().Hex()) + receipt, err := contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.TronHash().Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.TronHash().Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } // get event log for slashed event eventLog, err := contractCaller.DecodeSlashedEvent(chainParams.StakingInfoAddress.EthAddress(), receipt, msg.LogIndex) @@ -115,10 +119,14 @@ func SideHandleMsgUnjail(ctx sdk.Context, k Keeper, msg types.MsgUnjail, contrac chainParams := params.ChainParams // get main tx receipt - receipt, err := contractCaller.GetTronTransactionReceipt(msg.TxHash.TronHash().Hex()) + receipt, err := contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.TronHash().Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.TronHash().Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } // get unjail event eventLog, err := contractCaller.DecodeUnJailedEvent(chainParams.StakingInfoAddress.EthAddress(), receipt, msg.LogIndex) diff --git a/staking/handler_test.go b/staking/handler_test.go index ebe5a244..463a1f7e 100644 --- a/staking/handler_test.go +++ b/staking/handler_test.go @@ -72,6 +72,7 @@ func (suite *HandlerTestSuite) TestHandleMsgValidatorJoin() { chainParams := app.ChainKeeper.GetParams(ctx) txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10), } @@ -127,7 +128,7 @@ func (suite *HandlerTestSuite) TestHandleMsgValidatorUpdate() { msgTxHash := hmTypes.HexToHeimdallHash("123") msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(newSigner[0].ID), newSigner[0].PubKey, msgTxHash, 0, 0, 1) - txreceipt := ðTypes.Receipt{BlockNumber: big.NewInt(10)} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10)} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ @@ -169,6 +170,7 @@ func (suite *HandlerTestSuite) TestHandleMsgValidatorExit() { logIndex := uint64(0) txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10), } @@ -218,7 +220,7 @@ func (suite *HandlerTestSuite) TestHandleMsgStakeUpdate() { msgTxHash := hmTypes.HexToHeimdallHash("123") msg := types.NewMsgStakeUpdate(oldVal.Signer, oldVal.ID.Uint64(), sdk.NewInt(2000000000000000000), msgTxHash, 0, 0, 1) - txreceipt := ðTypes.Receipt{BlockNumber: big.NewInt(10)} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10)} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) stakinginfoStakeUpdate := &stakinginfo.StakinginfoStakeUpdate{ @@ -277,6 +279,7 @@ func (suite *HandlerTestSuite) TestExitedValidatorJoiningAgain() { chainParams := app.ChainKeeper.GetParams(ctx) txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10), } msgValJoin := types.NewMsgValidatorJoin( @@ -330,6 +333,7 @@ func (suite *HandlerTestSuite) TestTopupSuccessBeforeValidatorJoin() { } txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: big.NewInt(10), } diff --git a/staking/querier.go b/staking/querier.go index d1577def..fb20699b 100644 --- a/staking/querier.go +++ b/staking/querier.go @@ -170,8 +170,13 @@ func handleQueryStakingSequence(ctx sdk.Context, req abci.RequestQuery, keeper K return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } + chainParams := keeper.chainKeeper.GetParams(ctx) + // get main tx receipt - receipt, err := contractCallerObj.GetTronTransactionReceipt(hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex()) + receipt, err := contractCallerObj.GetTronConfirmedTxReceipt( + hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex(), + chainParams.TronchainTxConfirmations, + ) if err != nil || receipt == nil { return nil, sdk.ErrInternal("Transaction is not confirmed yet. Please wait for sometime and try again") } diff --git a/staking/querier_test.go b/staking/querier_test.go index 4d086e3e..bfc2f78d 100644 --- a/staking/querier_test.go +++ b/staking/querier_test.go @@ -210,7 +210,7 @@ func (suite *QuerierTestSuite) TestHandleQueryStakingSequence() { app.StakingKeeper.SetStakingSequence(ctx, sequence.String()) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), app.ChainKeeper.GetParams(ctx).TronchainTxConfirmations).Return(txreceipt, nil) path := []string{types.QueryStakingSequence} diff --git a/staking/side_handler.go b/staking/side_handler.go index effeffff..0dd28b6f 100644 --- a/staking/side_handler.go +++ b/staking/side_handler.go @@ -87,10 +87,14 @@ func SideHandleMsgValidatorJoin(ctx sdk.Context, msg types.MsgValidatorJoin, k K err error ) // get event log on tron - receipt, err = contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err = contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } contractAddress = hmTypes.HexToTronAddress(chainParams.TronStakingInfoAddress) // decode validator join event eventLog, err := contractCaller.DecodeValidatorJoinEvent(contractAddress, receipt, msg.LogIndex) @@ -226,10 +230,14 @@ func SideHandleMsgSignerUpdate(ctx sdk.Context, msg types.MsgSignerUpdate, k Kee err error ) // get event log on tron - receipt, err = contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err = contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } contractAddress = hmTypes.HexToTronAddress(chainParams.TronStakingInfoAddress) newPubKey := msg.NewSignerPubKey @@ -291,10 +299,14 @@ func SideHandleMsgValidatorExit(ctx sdk.Context, msg types.MsgValidatorExit, k K err error ) // get event log on tron - receipt, err = contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err = contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } contractAddress = hmTypes.HexToTronAddress(chainParams.TronStakingInfoAddress) // decode validator exit diff --git a/staking/side_handler_test.go b/staking/side_handler_test.go index 0650085e..e108d4de 100644 --- a/staking/side_handler_test.go +++ b/staking/side_handler_test.go @@ -93,6 +93,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Success", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -119,7 +120,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -131,6 +132,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("No receipt", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -157,7 +159,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(nil, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(nil, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(nil, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -170,6 +172,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("No EventLog", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -186,7 +189,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { ) suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(nil, nil) @@ -199,6 +202,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Signer pubkey", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -225,7 +229,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -238,6 +242,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Signer address", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -264,7 +269,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -277,6 +282,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Validator Id", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -303,7 +309,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -316,6 +322,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Activation Epoch", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -342,7 +349,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -355,6 +362,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Amount", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -381,7 +389,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -394,6 +402,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid Block Number", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -420,7 +429,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -433,6 +442,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { suite.Run("Invalid nonce", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } @@ -459,7 +469,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorJoin() { } suite.contractCaller.On("GetConfirmedTxReceipt", txHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", txHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", txHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeValidatorJoinEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, msgValJoin.LogIndex).Return(stakinginfoStaked, nil) @@ -491,9 +501,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { suite.Run("Success", func() { msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(oldSigner.ID), newSigner[0].PubKey, msgTxHash, 0, blockNumber.Uint64(), nonce.Uint64()) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -515,10 +525,10 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(oldSigner.ID), newSigner[0].PubKey, msgTxHash, 0, blockNumber.Uint64(), nonce.Uint64()) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) suite.contractCaller.On("DecodeSignerUpdateEvent", chainParams.ChainParams.StakingInfoAddress.EthAddress(), txreceipt, uint64(0)).Return(nil, nil) result := suite.sideHandler(ctx, msg) @@ -539,9 +549,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { nonce.Uint64(), ) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -563,9 +573,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(6), newSigner[0].PubKey, msgTxHash, 0, blockNumber.Uint64(), nonce.Uint64()) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -587,9 +597,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(oldSigner.ID), hmTypes.NewPubKey([]byte{123}), msgTxHash, 0, blockNumber.Uint64(), nonce.Uint64()) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -611,9 +621,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { msg := types.NewMsgSignerUpdate(hmTypes.ZeroHeimdallAddress, uint64(oldSigner.ID), newSigner[0].PubKey, msgTxHash, 0, blockNumber.Uint64(), nonce.Uint64()) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -635,9 +645,9 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgSignerUpdate() { msg := types.NewMsgSignerUpdate(newSigner[0].Signer, uint64(oldSigner.ID), newSigner[0].PubKey, msgTxHash, 0, blockNumber.Uint64(), uint64(12)) - txreceipt := ðTypes.Receipt{BlockNumber: blockNumber} + txreceipt := ðTypes.Receipt{Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber} suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) signerUpdateEvent := &stakinginfo.StakinginfoSignerChange{ ValidatorId: new(big.Int).SetUint64(oldSigner.ID.Uint64()), @@ -670,11 +680,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("Success", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ @@ -706,11 +717,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("No Receipt", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(nil, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(nil, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(nil, nil) amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ @@ -742,11 +754,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("No Eventlog", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) validators[0].EndEpoch = 10 @@ -772,11 +785,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ User: validators[0].Signer.EthAddress(), @@ -807,11 +821,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("Invalid validatorId", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ @@ -843,11 +858,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("Invalid DeactivationEpoch", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ @@ -878,11 +894,12 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgValidatorExit() { suite.Run("Invalid Nonce", func() { suite.contractCaller = mocks.IContractCaller{} txreceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: blockNumber, } suite.contractCaller.On("GetConfirmedTxReceipt", msgTxHash.EthHash(), chainParams.MainchainTxConfirmations).Return(txreceipt, nil) - suite.contractCaller.On("GetTronTransactionReceipt", msgTxHash.String()).Return(txreceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", msgTxHash.String(), chainParams.TronchainTxConfirmations).Return(txreceipt, nil) amount, _ := big.NewInt(0).SetString("10000000000000000000", 10) stakinginfoUnstakeInit := &stakinginfo.StakinginfoUnstakeInit{ diff --git a/topup/querier.go b/topup/querier.go index ef67284a..c46cd144 100644 --- a/topup/querier.go +++ b/topup/querier.go @@ -42,8 +42,13 @@ func querySequence(ctx sdk.Context, req abci.RequestQuery, k Keeper, contractCal return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) } + chainParams := k.chainKeeper.GetParams(ctx) + // get main tx receipt - receipt, err := contractCallerObj.GetTronTransactionReceipt(hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex()) + receipt, err := contractCallerObj.GetTronConfirmedTxReceipt( + hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex(), + chainParams.TronchainTxConfirmations, + ) if err != nil || receipt == nil { return nil, sdk.ErrInternal(fmt.Sprintf("Transaction is not confirmed yet. Please wait for sometime and try again")) } diff --git a/topup/querier_test.go b/topup/querier_test.go index 2f9697f8..035c4b08 100644 --- a/topup/querier_test.go +++ b/topup/querier_test.go @@ -88,7 +88,7 @@ func (suite *QuerierTestSuite) TestQuerySequence() { app.TopupKeeper.SetTopupSequence(ctx, sequence.String()) // mock external calls - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, suite.chainParams.TronchainTxConfirmations).Return(txReceipt, nil) path := []string{types.QuerySequence} route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySequence) diff --git a/topup/side_handler.go b/topup/side_handler.go index 52adf685..6a95708d 100644 --- a/topup/side_handler.go +++ b/topup/side_handler.go @@ -59,10 +59,14 @@ func SideHandleMsgTopup(ctx sdk.Context, k Keeper, msg types.MsgTopup, contractC chainParams := params.ChainParams // get main tx receipt - receipt, err := contractCaller.GetTronTransactionReceipt(msg.TxHash.Hex()) + receipt, err := contractCaller.GetTronConfirmedTxReceipt(msg.TxHash.Hex(), params.TronchainTxConfirmations) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } + if !helper.IsTronTransactionReceiptSuccessful(receipt) { + k.Logger(ctx).Error("Tron transaction failed", "txHash", msg.TxHash.Hex(), "status", receipt.Status) + return hmCommon.ErrorSideTx(k.Codespace(), common.CodeInvalidMsg) + } // get event log for topup contractAddress := hmTypes.HexToTronAddress(chainParams.TronStakingInfoAddress) diff --git a/topup/side_handler_test.go b/topup/side_handler_test.go index 551a022e..469395d7 100644 --- a/topup/side_handler_test.go +++ b/topup/side_handler_test.go @@ -85,6 +85,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -113,7 +114,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { Fee: coins.AmountOf(authTypes.FeeToken).BigInt(), } - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(txReceipt, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) // execute handler @@ -146,7 +147,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { blockNumber, ) - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(nil, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(nil, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), nil, logIndex).Return(nil, nil) // execute handler @@ -162,6 +163,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -179,7 +181,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { blockNumber, ) - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(txReceipt, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(nil, nil) // execute handler @@ -195,6 +197,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber + 1), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -217,7 +220,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { User: ethCommon.BytesToAddress(addr1.Bytes()), Fee: coins.AmountOf(authTypes.FeeToken).BigInt(), } - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(txReceipt, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) // execute handler @@ -233,6 +236,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -255,7 +259,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { User: ethCommon.BytesToAddress(addr2.Bytes()), Fee: coins.AmountOf(authTypes.FeeToken).BigInt(), } - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(txReceipt, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) // execute handler @@ -271,6 +275,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { logIndex := uint64(10) blockNumber := uint64(599) txReceipt := ðTypes.Receipt{ + Status: ethTypes.ReceiptStatusSuccessful, BlockNumber: new(big.Int).SetUint64(blockNumber), } txHash := hmTypes.HexToHeimdallHash("success hash") @@ -293,7 +298,7 @@ func (suite *SideHandlerTestSuite) TestSideHandleMsgTopup() { User: ethCommon.BytesToAddress(addr1.Bytes()), Fee: big.NewInt(1), // different fee } - suite.contractCaller.On("GetTronTransactionReceipt", mock.Anything).Return(txReceipt, nil) + suite.contractCaller.On("GetTronConfirmedTxReceipt", mock.Anything, chainParams.TronchainTxConfirmations).Return(txReceipt, nil) suite.contractCaller.On("DecodeValidatorTopupFeesEvent", chainParams.ChainParams.StateSenderAddress.EthAddress(), txReceipt, logIndex).Return(event, nil) // execute handler