From 7fb0764f952775e1e1556d922d2fbdc6f0a3dadf Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 9 Feb 2024 11:37:21 +0200 Subject: [PATCH 1/4] - fixed transaction status for relayed v1 intra-shard transactions --- process/testdata/executingSCCall.json | 2 + .../finishedFailedRelayedTxIntraShard.json | 54 +++++++++++++++++++ .../finishedOKRelayedTxIntraShard.json | 46 ++++++++++++++++ process/testdata/pendingNewMoveBalance.json | 2 + process/testdata/pendingNewSCCall.json | 2 + process/transactionProcessor.go | 36 +++++++++++++ process/transactionProcessor_test.go | 44 +++++++++++++++ 7 files changed, 186 insertions(+) create mode 100644 process/testdata/finishedFailedRelayedTxIntraShard.json create mode 100644 process/testdata/finishedOKRelayedTxIntraShard.json diff --git a/process/testdata/executingSCCall.json b/process/testdata/executingSCCall.json index 67e700f8..eef99321 100644 --- a/process/testdata/executingSCCall.json +++ b/process/testdata/executingSCCall.json @@ -8,6 +8,8 @@ "NotarizedAtSourceInMetaHash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", "miniblockType": "TxBlock", "miniblockHash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + "sender": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", "smartContractResults": [ { "hash": "SCR-hash1", diff --git a/process/testdata/finishedFailedRelayedTxIntraShard.json b/process/testdata/finishedFailedRelayedTxIntraShard.json new file mode 100644 index 00000000..53bd8040 --- /dev/null +++ b/process/testdata/finishedFailedRelayedTxIntraShard.json @@ -0,0 +1,54 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sender": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMxMzczODMyMmMyMjc2NjE2Yzc1NjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMmMyMjcyNjU2MzY1Njk3NjY1NzIyMjNhMjI3MzU0NmY0MjY0NDM1MDQ0NWE3MzcyMmY2YTRmN2EzNzY0MzY0NTZkNDU0YjQ1NzczOTQ5Njk0MjRlNDI0OTczNjU1NDY2MmI3MjY3MzE3NDY2NTI2MzNkMjIyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNDE1NDZjNDg0Yzc2Mzk2ZjY4NmU2MzYxNmQ0MzM4Nzc2NzM5NzA2NDUxNjgzODZiNzc3MDQ3NDIzNTZhNjk0OTQ5NmYzMzQ5NDg0YjU5NGU2MTY1NDUzZDIyMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzNTMwMzAzMDMwMmMyMjYzNjg2MTY5NmU0OTQ0MjIzYTIyNTY0MTNkM2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMTJjMjI3MzY5Njc2ZTYxNzQ3NTcyNjUyMjNhMjI1ODU0NGE3OTJiNTE3MzZhNmY0NzczNDE0YzMzNTI3NjQ4NGU3NTUyNjEzMDYyNDg3OTcyNTg3ODY2Nzg1MDYxMmI2ODUzNjE0MjU0NDQ1MDQzNjg1MTYxNmQ1OTc4MzU0MzU3NTA0OTQxNzM1YTM0NDczOTU3NGE3NTU5NjEzNTRlNzQ2ZDUzN2E2YjZiNzQ2ZDMyNmUyZjZhMzk2YjQ5NjM1MzcwNGM0MTQxM2QzZDIyN2Q=", + "sourceShard": 1, + "destinationShard": 1, + "notarizedAtSourceInMetaNonce": 1093826, + "NotarizedAtSourceInMetaHash": "d95037ae22a5c0e0fe1be6d20d45b891e3b47c5bdd3388aca0c722e093c8577f", + "notarizedAtDestinationInMetaNonce": 1093826, + "notarizedAtDestinationInMetaHash": "d95037ae22a5c0e0fe1be6d20d45b891e3b47c5bdd3388aca0c722e093c8577f", + "smartContractResults": [ + { + "hash": "SCR-hash1", + "nonce": 86, + "value": 0, + "receiver": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "returnMessage": "lower nonce in transaction", + "operation": "transfer" + } + ], + "logs": { + "address": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "events": [ + { + "address": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "identifier": "signalError", + "topics": null, + "data": null, + "additionalData": null + } + ] + }, + "status": "success", + "receivers": [ + "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7" + ], + "receiversShardIDs": [ + 1 + ], + "operation": "transfer", + "initiallyPaidFee": "1052500000000000", + "fee": "1052500000000000", + "isRelayed": true, + "chainID": "T", + "version": 1, + "options": 0 + } +} \ No newline at end of file diff --git a/process/testdata/finishedOKRelayedTxIntraShard.json b/process/testdata/finishedOKRelayedTxIntraShard.json new file mode 100644 index 00000000..0c4ddabd --- /dev/null +++ b/process/testdata/finishedOKRelayedTxIntraShard.json @@ -0,0 +1,46 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sender": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "gasPrice": 1000000000, + "gasLimit": 11052000, + "gasUsed": 11052000, + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTM4MzUzODJjMjI3NjYxNmM3NTY1MjIzYTMxMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMmMyMjcyNjU2MzY1Njk3NjY1NzIyMjNhMjI3MzU0NmY0MjY0NDM1MDQ0NWE3MzcyMmY2YTRmN2EzNzY0MzY0NTZkNDU0YjQ1NzczOTQ5Njk0MjRlNDI0OTczNjU1NDY2MmI3MjY3MzE3NDY2NTI2MzNkMjIyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNDE1NDZjNDg0Yzc2Mzk2ZjY4NmU2MzYxNmQ0MzM4Nzc2NzM5NzA2NDUxNjgzODZiNzc3MDQ3NDIzNTZhNjk0OTQ5NmYzMzQ5NDg0YjU5NGU2MTY1NDUzZDIyMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzMTMwMzAzMDMwMzAzMDMwMmMyMjYzNjg2MTY5NmU0OTQ0MjIzYTIyNTI0MTNkM2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMTJjMjI3MzY5Njc2ZTYxNzQ3NTcyNjUyMjNhMjI1NjRjNGY1ODQ5NTI3YTZkNDY0ZTMwNTg1MDYxNTc3MDM4NWEzMDUwNjM3NzY5MzkzOTRmNTQyYjZiNmI3Nzc0Mzg1MTU3Mzc2NjM0NzU2ZDVhNjI3ODU0NmEzNzQyNzc0NTU5NTg2MzRiMzM0NjU2NGMzNTY3NDM2YTUyNzk3MTcwMzc2ZTYzNTg2YzU1NDM3Mzc3NmY0ZTZiNmQ2MzUyNzI1MjM5NTM0MjQxM2QzZDIyN2Q=", + "notarizedAtSourceInMetaNonce":2171094, + "NotarizedAtSourceInMetaHash":"02d38e178073d1af3b77c2dc87da50f8139e00b468c436fc04f22879acf5a45f", + "notarizedAtDestinationInMetaNonce":2171094, + "notarizedAtDestinationInMetaHash":"02d38e178073d1af3b77c2dc87da50f8139e00b468c436fc04f22879acf5a45f", + "smartContractResults": [ + { + "hash": "SCR-hash1", + "value": 1000000000000000000, + "receiver": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "relayerAddress": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "relayedValue": 1000000000000000000, + "gasLimit": 9950000, + "gasPrice": 1000000000, + "callType": 0, + "operation": "transfer" + } + ], + "status": "success", + "receivers": [ + "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7" + ], + "receiversShardIDs": [ + 1 + ], + "operation": "transfer", + "initiallyPaidFee": "1152000000000000", + "fee": "1152000000000000", + "isRelayed": true, + "chainID": "D", + "version": 1, + "options": 0 + } +} \ No newline at end of file diff --git a/process/testdata/pendingNewMoveBalance.json b/process/testdata/pendingNewMoveBalance.json index e0e88223..b2396334 100644 --- a/process/testdata/pendingNewMoveBalance.json +++ b/process/testdata/pendingNewMoveBalance.json @@ -4,6 +4,8 @@ "processingTypeOnSource": "MoveBalance", "processingTypeOnDestination": "MoveBalance", "hash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sender": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", "value": "1500000000000000000", "gasPrice": 1000000000, "gasLimit": 50000, diff --git a/process/testdata/pendingNewSCCall.json b/process/testdata/pendingNewSCCall.json index c824e4e4..8ff054a8 100644 --- a/process/testdata/pendingNewSCCall.json +++ b/process/testdata/pendingNewSCCall.json @@ -4,6 +4,8 @@ "processingTypeOnSource": "MoveBalance", "processingTypeOnDestination": "SCInvoking", "hash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + "sender": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", "value": "0", "gasPrice": 1000000000, "gasLimit": 40000000, diff --git a/process/transactionProcessor.go b/process/transactionProcessor.go index 64328750..75ec2752 100644 --- a/process/transactionProcessor.go +++ b/process/transactionProcessor.go @@ -39,6 +39,7 @@ const ( nonceGapsParam = "?nonce-gaps=true" internalVMErrorsEventIdentifier = "internalVMErrors" // TODO export this in mx-chain-core-go, remove unexported definitions from mx-chain-vm's moveBalanceDescriptor = "MoveBalance" + relayedTransactionDescriptor = "RelayedTx" ) type requestType int @@ -421,6 +422,16 @@ func (tp *TransactionProcessor) computeTransactionStatus(tx *transaction.ApiTran return data.TxStatusUnknown } + // sanity checks + senderAddress, err := tp.pubKeyConverter.Decode(tx.Sender) + if err != nil { + return transaction.TxStatusFail + } + receiverAddress, err := tp.pubKeyConverter.Decode(tx.Receiver) + if err != nil { + return transaction.TxStatusFail + } + if tx.Status == transaction.TxStatusInvalid { return transaction.TxStatusFail } @@ -437,6 +448,11 @@ func (tp *TransactionProcessor) computeTransactionStatus(tx *transaction.ApiTran return transaction.TxStatusFail } + isIntraShardRelayed := tp.checkIfIntraShardRelayedTransaction(tx, senderAddress, receiverAddress) + if isIntraShardRelayed { + return tx.Status + } + allLogs, err := tp.gatherAllLogs(tx) if err != nil { log.Warn("error in TransactionProcessor.computeTransactionStatus", "error", err) @@ -484,6 +500,26 @@ func checkIfMoveBalanceNotarized(tx *transaction.ApiTransactionResult) bool { return true } +func (tp *TransactionProcessor) checkIfIntraShardRelayedTransaction( + tx *transaction.ApiTransactionResult, + senderAddress []byte, + receiverAddress []byte, +) bool { + isNotarized := tx.NotarizedAtSourceInMetaNonce > 0 && tx.NotarizedAtDestinationInMetaNonce > 0 + if !isNotarized { + return false + } + + isRelayedTransaction := tx.ProcessingTypeOnSource == relayedTransactionDescriptor && tx.ProcessingTypeOnDestination == relayedTransactionDescriptor + if !isRelayedTransaction { + return false + } + + isSameShard := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) + + return isSameShard +} + func findIdentifierInLogs(logs []*transaction.ApiLogs, identifier string) bool { if len(logs) == 0 { return false diff --git a/process/transactionProcessor_test.go b/process/transactionProcessor_test.go index 966aeff4..8e37a2dd 100644 --- a/process/transactionProcessor_test.go +++ b/process/transactionProcessor_test.go @@ -1907,6 +1907,24 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusFail, status) }) + t.Run("failed relayed move balance intra shard transaction", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedFailedRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + t.Run("ok relayed move balance intra shard transaction", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusSuccess, status) + }) t.Run("tx ok", func(t *testing.T) { t.Parallel() @@ -1926,6 +1944,32 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusFail, status) }) + t.Run("malformed transactions", func(t *testing.T) { + t.Parallel() + + t.Run("malformed relayed v1 inner transaction - wrong sender", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Sender = "not a sender" + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + t.Run("malformed relayed v1 inner transaction - wrong receiver", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Receiver = "not a sender" + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + }) } func TestTransactionProcessor_GetProcessedTransactionStatus(t *testing.T) { From 8cf644673048301901ff438d5da5c584479c3044 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 9 Feb 2024 15:17:18 +0200 Subject: [PATCH 2/4] - more fixes & tests --- .../finishedFailedRelayedTxIntraShard.json | 24 ++++- .../finishedOKRelayedTxCrossShard.json | 90 +++++++++++++++++++ .../finishedOKRelayedTxIntraShard.json | 23 ++++- process/testdata/finishedOKRewardTx.json | 24 +++++ process/transactionProcessor.go | 66 +++++++++----- process/transactionProcessor_test.go | 51 +++++++++++ 6 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 process/testdata/finishedOKRelayedTxCrossShard.json create mode 100644 process/testdata/finishedOKRewardTx.json diff --git a/process/testdata/finishedFailedRelayedTxIntraShard.json b/process/testdata/finishedFailedRelayedTxIntraShard.json index 53bd8040..b6683e7c 100644 --- a/process/testdata/finishedFailedRelayedTxIntraShard.json +++ b/process/testdata/finishedFailedRelayedTxIntraShard.json @@ -50,5 +50,25 @@ "chainID": "T", "version": 1, "options": 0 - } -} \ No newline at end of file + }, + "scrs": [ + { + "type": "unsigned", + "processingTypeOnSource": "MoveBalance", + "processingTypeOnDestination": "MoveBalance", + "hash": "SCR-hash1", + "receiver": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "returnMessage": "lower nonce in transaction", + "sourceShard": 1, + "destinationShard": 1, + "miniblockType": "SmartContractResultBlock", + "timestamp": 1707470912, + "status": "success", + "operation": "transfer", + "fee": "0", + "callType": "directCall", + "options": 0 + } + ] +} diff --git a/process/testdata/finishedOKRelayedTxCrossShard.json b/process/testdata/finishedOKRelayedTxCrossShard.json new file mode 100644 index 00000000..80dbc4e8 --- /dev/null +++ b/process/testdata/finishedOKRelayedTxCrossShard.json @@ -0,0 +1,90 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "e789cab27227ea2805c2445cbdcf742a1126e30e5c38a1cf5cb94fae3804a4e8", + "receiver": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sender": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMyMzEzMDMyMzQyYzIyNzY2MTZjNzU2NTIyM2EzMTMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDJjMjI3MjY1NjM2NTY5NzY2NTcyMjIzYTIyNjc0NTZlNTc0ZjY1NTc2ZDZkNDEzMDYzMzA2YTZiNzE3NjRkMzU0MjQxNzA3YTYxNjQ0YjQ2NTc0ZTUzNGY2OTQxNzY0MzU3NTE2Mzc3NmQ0NzUwNjczZDIyMmMyMjczNjU2ZTY0NjU3MjIyM2EyMjQxNTQ2YzQ4NGM3NjM5NmY2ODZlNjM2MTZkNDMzODc3NjczOTcwNjQ1MTY4Mzg2Yjc3NzA0NzQyMzU2YTY5NDk0OTZmMzM0OTQ4NGI1OTRlNjE2NTQ1M2QyMjJjMjI2NzYxNzM1MDcyNjk2MzY1MjIzYTMxMzAzMDMwMzAzMDMwMzAzMDMwMmMyMjY3NjE3MzRjNjk2ZDY5NzQyMjNhMzUzMDMwMzAzMDJjMjI2MzY4NjE2OTZlNDk0NDIyM2EyMjU2NDEzZDNkMjIyYzIyNzY2NTcyNzM2OTZmNmUyMjNhMzEyYzIyNzM2OTY3NmU2MTc0NzU3MjY1MjIzYTIyNGE3ODM3NTk2ZDQyNGU2Zjc5Njc0ZDZjNTA0ODVhMzU2Mzc3NmY0NzY5NTMzODRlNTM3NzY4NGUyZjMyNGY0MjQxMzc0ODJiNDI0NjRjNmY1Njc0MzM3MDM2NjM1MzY1NjE3MTZmNmY0YjVhNDk0ZDY3NDE1MDQ2NDU0ZjQ2NGE0ZTc0NGU1NTUxNTYzODY5NDM1MTQ4NzI0YzYzNzc3YTZlNmI2NDY0NDQ0MTNkM2QyMjdk", + "signature": "8b3560fb9720bc32c417f53ee59c021848f3debcd75e3db14a64b3e4615dd34f6cd3e307840687726574f1d778fce1b492f75797748de556ab76c5619c8a9a02", + "sourceShard": 1, + "destinationShard": 1, + "notarizedAtSourceInMetaNonce": 1094038, + "NotarizedAtSourceInMetaHash": "8d0b98f75000cf86c66af5583e586a737f295a853cef9e665623089a27a0b9e6", + "notarizedAtDestinationInMetaNonce": 1094038, + "notarizedAtDestinationInMetaHash": "8d0b98f75000cf86c66af5583e586a737f295a853cef9e665623089a27a0b9e6", + "miniblockType": "TxBlock", + "smartContractResults": [ + { + "hash": "SCR-hash1", + "nonce": 21024, + "value": 1000000000000000000, + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "relayerAddress": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "relayedValue": 0, + "prevTxHash": "e789cab27227ea2805c2445cbdcf742a1126e30e5c38a1cf5cb94fae3804a4e8", + "originalTxHash": "e789cab27227ea2805c2445cbdcf742a1126e30e5c38a1cf5cb94fae3804a4e8", + "operation": "transfer" + } + ], + "status": "success", + "receivers": [ + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + ], + "receiversShardIDs": [ + 0 + ], + "operation": "transfer", + "initiallyPaidFee": "1049500000000000", + "fee": "1049500000000000", + "isRelayed": true, + "chainID": "T", + "version": 1, + "options": 0 + }, + "scrs": [ + { + "type": "unsigned", + "processingTypeOnSource": "MoveBalance", + "processingTypeOnDestination": "MoveBalance", + "hash": "SCR-hash1", + "value": "1000000000000000000", + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sourceShard": 1, + "destinationShard": 0, + "notarizedAtSourceInMetaNonce": 1094038, + "NotarizedAtSourceInMetaHash": "8d0b98f75000cf86c66af5583e586a737f295a853cef9e665623089a27a0b9e6", + "notarizedAtDestinationInMetaNonce": 1094042, + "notarizedAtDestinationInMetaHash": "8517fe85e09cba9df1fd1ac8558b9017abca1ab1e76eccaeb05c90917597dae8", + "miniblockType": "SmartContractResultBlock", + "miniblockHash": "8aeabbed7e0d6569f5dd2584d5632289c8a46c2d7f32a7c9fa27db3c915a72d3", + "hyperblockNonce": 1094042, + "hyperblockHash": "8517fe85e09cba9df1fd1ac8558b9017abca1ab1e76eccaeb05c90917597dae8", + "timestamp": 1707472208, + "logs": { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "events": [ + { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "identifier": "completedTxEvent", + "topics": [ + "54nKsnIn6igFwkRcvc90KhEm4w5cOKHPXLlPrjgEpOg=" + ], + "data": null, + "additionalData": null + } + ] + }, + "status": "success", + "operation": "transfer", + "fee": "0", + "callType": "directCall", + "relayerAddress": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", + "relayedValue": "0", + "options": 0 + } + ] +} diff --git a/process/testdata/finishedOKRelayedTxIntraShard.json b/process/testdata/finishedOKRelayedTxIntraShard.json index 0c4ddabd..b2f4839d 100644 --- a/process/testdata/finishedOKRelayedTxIntraShard.json +++ b/process/testdata/finishedOKRelayedTxIntraShard.json @@ -42,5 +42,24 @@ "chainID": "D", "version": 1, "options": 0 - } -} \ No newline at end of file + }, + "scrs": [ + { + "type": "unsigned", + "processingTypeOnSource": "MoveBalance", + "processingTypeOnDestination": "MoveBalance", + "hash": "SCR-hash1", + "value": "1000000000000000000", + "receiver": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "sender": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "timestamp": 1707396974, + "status": "success", + "operation": "transfer", + "fee": "0", + "callType": "directCall", + "relayerAddress": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "relayedValue": "1000000000000000000", + "options": 0 + } + ] +} diff --git a/process/testdata/finishedOKRewardTx.json b/process/testdata/finishedOKRewardTx.json new file mode 100644 index 00000000..d45144a0 --- /dev/null +++ b/process/testdata/finishedOKRewardTx.json @@ -0,0 +1,24 @@ +{ + "transaction": { + "type": "reward", + "processingTypeOnSource": "MoveBalance", + "processingTypeOnDestination": "MoveBalance", + "hash": "191a0bf2d29559181da9d93bfd35f8b9f136122006d10b51096fec6be65fb25d", + "value": "586698853111273957", + "receiver": "erd1kkcvtdc6j235d9x830hlz8xc4vvz3q63mlhqykw5nq8f2t0tfx7sx4jhhj", + "sender": "metachain", + "gasUsed": 50000, + "sourceShard": 4294967295, + "destinationShard": 1, + "blockNonce": 1093268, + "blockHash": "4db7e2478663a19dea9ac29949c2482118448f0497aad8f0807eb3e02a60c76f", + "notarizedAtSourceInMetaNonce": 1093987, + "NotarizedAtSourceInMetaHash": "41eeb81ae7a0155f98de00bba25e667b3efa8cc0bf20a8131867e86f6b90d5ff", + "notarizedAtDestinationInMetaNonce": 1093991, + "notarizedAtDestinationInMetaHash": "d301328ed7ba3cbfb74257af110c383c99594c366783563407d67bb1a8c8b489", + "status": "success", + "operation": "transfer", + "fee": "0", + "options": 0 + } +} diff --git a/process/transactionProcessor.go b/process/transactionProcessor.go index 75ec2752..75b1bcbe 100644 --- a/process/transactionProcessor.go +++ b/process/transactionProcessor.go @@ -5,6 +5,7 @@ import ( "fmt" "math/big" "net/http" + "strings" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -40,6 +41,7 @@ const ( internalVMErrorsEventIdentifier = "internalVMErrors" // TODO export this in mx-chain-core-go, remove unexported definitions from mx-chain-vm's moveBalanceDescriptor = "MoveBalance" relayedTransactionDescriptor = "RelayedTx" + relayedTxV1DataMarker = "relayedTx@" ) type requestType int @@ -68,6 +70,7 @@ type TransactionProcessor struct { pubKeyConverter core.PubkeyConverter hasher hashing.Hasher marshalizer marshal.Marshalizer + relayedTxsMarshaller marshal.Marshalizer newTxCostProcessor func() (TransactionCostHandler, error) mergeLogsHandler LogsMergerHandler shouldAllowEntireTxPoolFetch bool @@ -102,6 +105,9 @@ func NewTransactionProcessor( return nil, ErrNilLogsMerger } + // no reason to get this from configs. If we are going to change the marshaller for the relayed transaction v1, + // we will need also an enable epoch handler + relayedTxsMarshaller := &marshal.JsonMarshalizer{} return &TransactionProcessor{ proc: proc, pubKeyConverter: pubKeyConverter, @@ -110,6 +116,7 @@ func NewTransactionProcessor( newTxCostProcessor: newTxCostProcessor, mergeLogsHandler: logsMerger, shouldAllowEntireTxPoolFetch: allowEntireTxPoolFetch, + relayedTxsMarshaller: relayedTxsMarshaller, }, nil } @@ -422,16 +429,6 @@ func (tp *TransactionProcessor) computeTransactionStatus(tx *transaction.ApiTran return data.TxStatusUnknown } - // sanity checks - senderAddress, err := tp.pubKeyConverter.Decode(tx.Sender) - if err != nil { - return transaction.TxStatusFail - } - receiverAddress, err := tp.pubKeyConverter.Decode(tx.Receiver) - if err != nil { - return transaction.TxStatusFail - } - if tx.Status == transaction.TxStatusInvalid { return transaction.TxStatusFail } @@ -448,7 +445,10 @@ func (tp *TransactionProcessor) computeTransactionStatus(tx *transaction.ApiTran return transaction.TxStatusFail } - isIntraShardRelayed := tp.checkIfIntraShardRelayedTransaction(tx, senderAddress, receiverAddress) + isIntraShardRelayed, err := tp.checkIfCompletelyIntraShardRelayedTransaction(tx) + if err != nil { + return transaction.TxStatusFail + } if isIntraShardRelayed { return tx.Status } @@ -500,24 +500,48 @@ func checkIfMoveBalanceNotarized(tx *transaction.ApiTransactionResult) bool { return true } -func (tp *TransactionProcessor) checkIfIntraShardRelayedTransaction( - tx *transaction.ApiTransactionResult, - senderAddress []byte, - receiverAddress []byte, -) bool { +func (tp *TransactionProcessor) checkIfCompletelyIntraShardRelayedTransaction(tx *transaction.ApiTransactionResult) (bool, error) { isNotarized := tx.NotarizedAtSourceInMetaNonce > 0 && tx.NotarizedAtDestinationInMetaNonce > 0 if !isNotarized { - return false + return false, nil } isRelayedTransaction := tx.ProcessingTypeOnSource == relayedTransactionDescriptor && tx.ProcessingTypeOnDestination == relayedTransactionDescriptor if !isRelayedTransaction { - return false + return false, nil + } + + senderAddress, err := tp.pubKeyConverter.Decode(tx.Sender) + if err != nil { + return false, err + } + receiverAddress, err := tp.pubKeyConverter.Decode(tx.Receiver) + if err != nil { + return false, err + } + + dataField := string(tx.Data) + if strings.Index(dataField, relayedTxV1DataMarker) != 0 { + return false, fmt.Errorf("wrong relayed v1 data marker position") + } + + hexedInnerTxData := dataField[len(relayedTxV1DataMarker):] + innerTxData, err := hex.DecodeString(hexedInnerTxData) + if err != nil { + return false, err + } + + innerTx := &transaction.Transaction{} + err = tp.relayedTxsMarshaller.Unmarshal(innerTx, innerTxData) + if err != nil { + return false, err } - isSameShard := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) + isSameShardOnRelayed := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) + isSameShardOnInner := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.RcvAddr) && + tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.SndAddr) - return isSameShard + return isSameShardOnRelayed && isSameShardOnInner, nil } func findIdentifierInLogs(logs []*transaction.ApiLogs, identifier string) bool { @@ -705,7 +729,7 @@ func (tp *TransactionProcessor) mergeScResultsFromSourceAndDestIfNeeded( } func (tp *TransactionProcessor) getScResultsUnion(scResults []*transaction.ApiSmartContractResult) []*transaction.ApiSmartContractResult { - scResultsHash := make(map[string]*transaction.ApiSmartContractResult, 0) + scResultsHash := make(map[string]*transaction.ApiSmartContractResult) for _, scResult := range scResults { scResultFromMap, found := scResultsHash[scResult.Hash] if !found { diff --git a/process/transactionProcessor_test.go b/process/transactionProcessor_test.go index 8e37a2dd..f95e8797 100644 --- a/process/transactionProcessor_test.go +++ b/process/transactionProcessor_test.go @@ -1925,6 +1925,15 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusSuccess, status) }) + t.Run("ok relayed move balance cross shard transaction", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxCrossShard.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusSuccess, status) + }) t.Run("tx ok", func(t *testing.T) { t.Parallel() @@ -1935,6 +1944,15 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { require.Equal(t, transaction.TxStatusSuccess, status) }) }) + t.Run("reward transaction", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRewardTx.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusSuccess, status) + }) t.Run("invalid transaction", func(t *testing.T) { t.Parallel() @@ -1966,6 +1984,39 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Receiver = "not a sender" + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + t.Run("malformed relayed v1 - relayed v1 marker on wrong position", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Data = append([]byte("A"), testData.Transaction.Data...) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + t.Run("malformed relayed v1 - not a hex character after the marker", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Data[45] = byte('T') + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusFail, status) + }) + t.Run("malformed relayed v1 - marshaller will fail", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Data = append(testData.Transaction.Data, []byte("aaaaaa")...) + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusFail, status) }) From a977822405d2894ae5078fd15fbcc7fa565d2eb6 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 9 Feb 2024 16:38:01 +0200 Subject: [PATCH 3/4] - refactored solution --- .../malformedRelayedTxIntraShard.json | 32 ++++++++ process/transactionProcessor.go | 82 +++++++++++++++---- process/transactionProcessor_test.go | 42 ++++++++-- 3 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 process/testdata/malformedRelayedTxIntraShard.json diff --git a/process/testdata/malformedRelayedTxIntraShard.json b/process/testdata/malformedRelayedTxIntraShard.json new file mode 100644 index 00000000..0600938f --- /dev/null +++ b/process/testdata/malformedRelayedTxIntraShard.json @@ -0,0 +1,32 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "a4823050d2396540b17bd9290523973763142c9f655bb26cd9e33f359b6d73ad", + "receiver": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "sender": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", + "gasPrice": 1000000000, + "gasLimit": 11052000, + "gasUsed": 11052000, + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTM4MzUzODJjMjI3NjYxNmM3NTY1MjIzYTMxMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMmMyMjcyNjU2MzY1Njk3NjY1NzIyMjNhMjI3MzU0NmY0MjY0NDM1MDQ0NWE3MzcyMmY2YTRmN2EzNzY0MzY0NTZkNDU0YjQ1NzczOTQ5Njk0MjRlNDI0OTczNjU1NDY2MmI3MjY3MzE3NDY2NTI2MzNkMjIyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNDE1NDZjNDg0Yzc2Mzk2ZjY4NmU2MzYxNmQ0MzM4Nzc2NzM5NzA2NDUxNjgzODZiNzc3MDQ3NDIzNTZhNjk0OTQ5NmYzMzQ5NDg0YjU5NGU2MTY1NDUzZDIyMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzMTMwMzAzMDMwMzAzMDMwMmMyMjYzNjg2MTY5NmU0OTQ0MjIzYTIyNTI0MTNkM2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMTJjMjI3MzY5Njc2ZTYxNzQ3NTcyNjUyMjNhMjI1NjRjNGY1ODQ5NTI3YTZkNDY0ZTMwNTg1MDYxNTc3MDM4NWEzMDUwNjM3NzY5MzkzOTRmNTQyYjZiNmI3Nzc0Mzg1MTU3Mzc2NjM0NzU2ZDVhNjI3ODU0NmEzNzQyNzc0NTU5NTg2MzRiMzM0NjU2NGMzNTY3NDM2YTUyNzk3MTcwMzc2ZTYzNTg2YzU1NDM3Mzc3NmY0ZTZiNmQ2MzUyNzI1MjM5NTM0MjQxM2QzZDIyN2Q=", + "notarizedAtSourceInMetaNonce":2171094, + "NotarizedAtSourceInMetaHash":"02d38e178073d1af3b77c2dc87da50f8139e00b468c436fc04f22879acf5a45f", + "notarizedAtDestinationInMetaNonce":2171094, + "notarizedAtDestinationInMetaHash":"02d38e178073d1af3b77c2dc87da50f8139e00b468c436fc04f22879acf5a45f", + "status": "success", + "receivers": [ + "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7" + ], + "receiversShardIDs": [ + 1 + ], + "operation": "transfer", + "initiallyPaidFee": "1152000000000000", + "fee": "1152000000000000", + "isRelayed": true, + "chainID": "D", + "version": 1, + "options": 0 + } +} diff --git a/process/transactionProcessor.go b/process/transactionProcessor.go index 75b1bcbe..70f6a988 100644 --- a/process/transactionProcessor.go +++ b/process/transactionProcessor.go @@ -445,19 +445,18 @@ func (tp *TransactionProcessor) computeTransactionStatus(tx *transaction.ApiTran return transaction.TxStatusFail } - isIntraShardRelayed, err := tp.checkIfCompletelyIntraShardRelayedTransaction(tx) + allLogs, allScrs, err := tp.gatherAllLogsAndScrs(tx) if err != nil { - return transaction.TxStatusFail - } - if isIntraShardRelayed { - return tx.Status + log.Warn("error in TransactionProcessor.computeTransactionStatus", "error", err) + return data.TxStatusUnknown } - allLogs, err := tp.gatherAllLogs(tx) + allLogs, err = tp.addMissingLogsOnProcessingExceptions(tx, allLogs, allScrs) if err != nil { - log.Warn("error in TransactionProcessor.computeTransactionStatus", "error", err) + log.Warn("error in TransactionProcessor.computeTransactionStatus on addMissingLogsOnProcessingExceptions call", "error", err) return data.TxStatusUnknown } + if checkIfFailed(allLogs) { return transaction.TxStatusFail } @@ -500,7 +499,50 @@ func checkIfMoveBalanceNotarized(tx *transaction.ApiTransactionResult) bool { return true } -func (tp *TransactionProcessor) checkIfCompletelyIntraShardRelayedTransaction(tx *transaction.ApiTransactionResult) (bool, error) { +func (tp *TransactionProcessor) addMissingLogsOnProcessingExceptions( + tx *transaction.ApiTransactionResult, + allLogs []*transaction.ApiLogs, + allScrs []*transaction.ApiTransactionResult, +) ([]*transaction.ApiLogs, error) { + newLogs, err := tp.handleIntraShardRelayedV1MoveBalanceTransactions(tx, allScrs) + if err != nil { + return nil, err + } + + allLogs = append(allLogs, newLogs...) + + return allLogs, nil +} + +func (tp *TransactionProcessor) handleIntraShardRelayedV1MoveBalanceTransactions( + tx *transaction.ApiTransactionResult, + allScrs []*transaction.ApiTransactionResult, +) ([]*transaction.ApiLogs, error) { + var newLogs []*transaction.ApiLogs + isIntraShardRelayedV1MoveBalanceTransaction, err := tp.isIntraShardRelayedV1MoveBalanceTransaction(tx, allScrs) + if err != nil { + return newLogs, err + } + + if isIntraShardRelayedV1MoveBalanceTransaction { + newLogs = append(newLogs, &transaction.ApiLogs{ + Address: tx.Sender, + Events: []*transaction.Events{ + { + Address: tx.Sender, + Identifier: core.CompletedTxEventIdentifier, + }, + }, + }) + } + + return newLogs, nil +} + +func (tp *TransactionProcessor) isIntraShardRelayedV1MoveBalanceTransaction( + tx *transaction.ApiTransactionResult, + allScrs []*transaction.ApiTransactionResult, +) (bool, error) { isNotarized := tx.NotarizedAtSourceInMetaNonce > 0 && tx.NotarizedAtDestinationInMetaNonce > 0 if !isNotarized { return false, nil @@ -511,6 +553,16 @@ func (tp *TransactionProcessor) checkIfCompletelyIntraShardRelayedTransaction(tx return false, nil } + if len(allScrs) == 0 { + return false, fmt.Errorf("no smart contracts results for the given relayed transaction v1") + } + + firstScr := allScrs[0] + innerIsMoveBalance := firstScr.ProcessingTypeOnSource == moveBalanceDescriptor && firstScr.ProcessingTypeOnDestination == moveBalanceDescriptor + if !innerIsMoveBalance { + return false, nil + } + senderAddress, err := tp.pubKeyConverter.Decode(tx.Sender) if err != nil { return false, err @@ -538,10 +590,10 @@ func (tp *TransactionProcessor) checkIfCompletelyIntraShardRelayedTransaction(tx } isSameShardOnRelayed := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) - isSameShardOnInner := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.RcvAddr) && - tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.SndAddr) + isSameShardOnInnerForReceiver := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.RcvAddr) + isSameShardOnInnerForSender := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.SndAddr) - return isSameShardOnRelayed && isSameShardOnInner, nil + return isSameShardOnRelayed && isSameShardOnInnerForReceiver && isSameShardOnInnerForSender, nil } func findIdentifierInLogs(logs []*transaction.ApiLogs, identifier string) bool { @@ -573,20 +625,22 @@ func findIdentifierInSingleLog(log *transaction.ApiLogs, identifier string) bool return false } -func (tp *TransactionProcessor) gatherAllLogs(tx *transaction.ApiTransactionResult) ([]*transaction.ApiLogs, error) { +func (tp *TransactionProcessor) gatherAllLogsAndScrs(tx *transaction.ApiTransactionResult) ([]*transaction.ApiLogs, []*transaction.ApiTransactionResult, error) { const withResults = true allLogs := []*transaction.ApiLogs{tx.Logs} + allScrs := make([]*transaction.ApiTransactionResult, 0) for _, scrFromTx := range tx.SmartContractResults { scr, err := tp.GetTransaction(scrFromTx.Hash, withResults) if err != nil { - return nil, fmt.Errorf("%w for scr hash %s", err, scrFromTx.Hash) + return nil, nil, fmt.Errorf("%w for scr hash %s", err, scrFromTx.Hash) } allLogs = append(allLogs, scr.Logs) + allScrs = append(allScrs, scr) } - return allLogs, nil + return allLogs, allScrs, nil } func (tp *TransactionProcessor) getTxFromObservers(txHash string, reqType requestType, withResults bool) (*transaction.ApiTransactionResult, error) { diff --git a/process/transactionProcessor_test.go b/process/transactionProcessor_test.go index f95e8797..eca19b57 100644 --- a/process/transactionProcessor_test.go +++ b/process/transactionProcessor_test.go @@ -1925,6 +1925,18 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusSuccess, status) }) + t.Run("ok relayed sc call function balance intra shard transaction still pending", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.SCRs[0].ProcessingTypeOnSource = "SCInvoking" + testData.SCRs[0].ProcessingTypeOnDestination = "SCInvoking" + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusPending, status) + }) t.Run("ok relayed move balance cross shard transaction", func(t *testing.T) { t.Parallel() @@ -1974,7 +1986,7 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Sender = "not a sender" status := tp.ComputeTransactionStatus(testData.Transaction, withResults) - require.Equal(t, transaction.TxStatusFail, status) + require.Equal(t, data.TxStatusUnknown, status) }) t.Run("malformed relayed v1 inner transaction - wrong receiver", func(t *testing.T) { t.Parallel() @@ -1985,7 +1997,7 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Receiver = "not a sender" status := tp.ComputeTransactionStatus(testData.Transaction, withResults) - require.Equal(t, transaction.TxStatusFail, status) + require.Equal(t, data.TxStatusUnknown, status) }) t.Run("malformed relayed v1 - relayed v1 marker on wrong position", func(t *testing.T) { t.Parallel() @@ -1996,7 +2008,7 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Data = append([]byte("A"), testData.Transaction.Data...) status := tp.ComputeTransactionStatus(testData.Transaction, withResults) - require.Equal(t, transaction.TxStatusFail, status) + require.Equal(t, data.TxStatusUnknown, status) }) t.Run("malformed relayed v1 - not a hex character after the marker", func(t *testing.T) { t.Parallel() @@ -2007,7 +2019,7 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Data[45] = byte('T') status := tp.ComputeTransactionStatus(testData.Transaction, withResults) - require.Equal(t, transaction.TxStatusFail, status) + require.Equal(t, data.TxStatusUnknown, status) }) t.Run("malformed relayed v1 - marshaller will fail", func(t *testing.T) { t.Parallel() @@ -2018,7 +2030,27 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData.Transaction.Data = append(testData.Transaction.Data, []byte("aaaaaa")...) status := tp.ComputeTransactionStatus(testData.Transaction, withResults) - require.Equal(t, transaction.TxStatusFail, status) + require.Equal(t, data.TxStatusUnknown, status) + }) + t.Run("malformed relayed v1 - missing scrs", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.SCRs = nil + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, data.TxStatusUnknown, status) + }) + t.Run("malformed relayed v1 - no scr generated", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/malformedRelayedTxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, data.TxStatusUnknown, status) }) }) } From 2dc37557d843ac16f045b1156f4208a8dca9af9a Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Wed, 14 Feb 2024 17:58:12 +0200 Subject: [PATCH 4/4] - added support for v2 relayed transactions as well --- process/export_test.go | 3 + .../finishedOKRelayedV2TxIntraShard.json | 57 ++++++++++++++ .../malformedRelayedV2TxIntraShard.json | 43 +++++++++++ process/transactionProcessor.go | 74 +++++++++++++++---- process/transactionProcessor_test.go | 40 ++++++++++ 5 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 process/testdata/finishedOKRelayedV2TxIntraShard.json create mode 100644 process/testdata/malformedRelayedV2TxIntraShard.json diff --git a/process/export_test.go b/process/export_test.go index 0eec3804..6a3c08bd 100644 --- a/process/export_test.go +++ b/process/export_test.go @@ -7,6 +7,9 @@ import ( proxyData "github.com/multiversx/mx-chain-proxy-go/data" ) +// RelayedTxV2DataMarker - +const RelayedTxV2DataMarker = relayedTxV2DataMarker + // SetDelayForCheckingNodesSyncState - func (bp *BaseProcessor) SetDelayForCheckingNodesSyncState(delay time.Duration) { bp.delayForCheckingNodesSyncState = delay diff --git a/process/testdata/finishedOKRelayedV2TxIntraShard.json b/process/testdata/finishedOKRelayedV2TxIntraShard.json new file mode 100644 index 00000000..7967c4eb --- /dev/null +++ b/process/testdata/finishedOKRelayedV2TxIntraShard.json @@ -0,0 +1,57 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTxV2", + "processingTypeOnDestination": "RelayedTxV2", + "hash": "47845341c9bf9172fdb8ebc8a7db729ff91c66c8d708e042af7a0b3f6a3c337e", + "nonce": 0, + "round": 1590, + "epoch": 7, + "value": "0", + "receiver": "erd1jrvq9emyfd97xs2kq53qedkz4sv40aayjt7tjcw9x6kjujvuv8uqnww788", + "sender": "erd19al0qr2zcgu067sku2m86djxp5dexle6we2lrlm63z29q2yawfsqmnxk78", + "data": "cmVsYXllZFR4VjJAYWY2ZjM2M2E4NWVkNmRjYWQwMDJkNjRhMzdlMmMyMmQwYWVjYTEzNWY0MWZlM2UwYjhhNDlkYzAwNWZkZDNhOEAwMUA3MjYxNmU2NDZmNmRAZDVjNDRiNDk5MTI3ZDA3ZWE5YTdhYTgyMDVlYjUxMmMyYzRmYzhjZTA2NzcwN2QxMWZkNmI2NmI0ZmEzYWQwMWRiNzk4ODY2YzEyMzM3Y2JjODYyZWMzMzllNzAzMzBlMGZkZGQyZTBjZTViNGUzOGYxYzdjZGM0ZTczYWFmMDU=", + "notarizedAtSourceInMetaNonce": 1477, + "NotarizedAtSourceInMetaHash": "20d21f4610e5c997a82ede2e9da9bb6ba4e806488829a5e88d74ec6f735979a5", + "notarizedAtDestinationInMetaNonce": 1477, + "notarizedAtDestinationInMetaHash": "20d21f4610e5c997a82ede2e9da9bb6ba4e806488829a5e88d74ec6f735979a5", + "smartContractResults": [ + { + "hash": "SCR-hash1", + "receiver": "erd14ahnvw59a4ku45qz6e9r0ckz959wegf47s078c9c5jwuqp0a6w5qgfjy87", + "sender": "erd1jrvq9emyfd97xs2kq53qedkz4sv40aayjt7tjcw9x6kjujvuv8uqnww788", + "relayerAddress": "erd19al0qr2zcgu067sku2m86djxp5dexle6we2lrlm63z29q2yawfsqmnxk78", + "data": "random", + "operation": "transfer" + } + ], + "status": "success", + "receivers": [ + "erd14ahnvw59a4ku45qz6e9r0ckz959wegf47s078c9c5jwuqp0a6w5qgfjy87" + ], + "receiversShardIDs": [ + 0 + ], + "operation": "transfer", + "initiallyPaidFee": "481500000000000", + "fee": "481500000000000", + "isRelayed": true, + "chainID": "1", + "version": 1, + "options": 0 + }, + "scrs": [ + { + "type": "unsigned", + "processingTypeOnSource": "MoveBalance", + "processingTypeOnDestination": "MoveBalance", + "hash": "SCR-hash1", + "receiver": "erd14ahnvw59a4ku45qz6e9r0ckz959wegf47s078c9c5jwuqp0a6w5qgfjy87", + "sender": "erd1jrvq9emyfd97xs2kq53qedkz4sv40aayjt7tjcw9x6kjujvuv8uqnww788", + "data": "cmFuZG9t", + "miniblockType": "SmartContractResultBlock", + "callType": "directCall", + "relayerAddress": "erd19al0qr2zcgu067sku2m86djxp5dexle6we2lrlm63z29q2yawfsqmnxk78" + } + ] +} \ No newline at end of file diff --git a/process/testdata/malformedRelayedV2TxIntraShard.json b/process/testdata/malformedRelayedV2TxIntraShard.json new file mode 100644 index 00000000..3a50e049 --- /dev/null +++ b/process/testdata/malformedRelayedV2TxIntraShard.json @@ -0,0 +1,43 @@ +{ + "transaction": { + "type": "normal", + "processingTypeOnSource": "RelayedTxV2", + "processingTypeOnDestination": "RelayedTxV2", + "hash": "47845341c9bf9172fdb8ebc8a7db729ff91c66c8d708e042af7a0b3f6a3c337e", + "nonce": 0, + "round": 1590, + "epoch": 7, + "value": "0", + "receiver": "erd1jrvq9emyfd97xs2kq53qedkz4sv40aayjt7tjcw9x6kjujvuv8uqnww788", + "sender": "erd19al0qr2zcgu067sku2m86djxp5dexle6we2lrlm63z29q2yawfsqmnxk78", + "data": "cmVsYXllZFR4VjJAYWY2ZjM2M2E4NWVkNmRjYWQwMDJkNjRhMzdlMmMyMmQwYWVjYTEzNWY0MWZlM2UwYjhhNDlkYzAwNWZkZDNhOEAwMUA3MjYxNmU2NDZmNmRAZDVjNDRiNDk5MTI3ZDA3ZWE5YTdhYTgyMDVlYjUxMmMyYzRmYzhjZTA2NzcwN2QxMWZkNmI2NmI0ZmEzYWQwMWRiNzk4ODY2YzEyMzM3Y2JjODYyZWMzMzllNzAzMzBlMGZkZGQyZTBjZTViNGUzOGYxYzdjZGM0ZTczYWFmMDU=", + "notarizedAtSourceInMetaNonce": 1477, + "NotarizedAtSourceInMetaHash": "20d21f4610e5c997a82ede2e9da9bb6ba4e806488829a5e88d74ec6f735979a5", + "notarizedAtDestinationInMetaNonce": 1477, + "notarizedAtDestinationInMetaHash": "20d21f4610e5c997a82ede2e9da9bb6ba4e806488829a5e88d74ec6f735979a5", + "smartContractResults": [ + { + "hash": "SCR-hash1", + "receiver": "erd14ahnvw59a4ku45qz6e9r0ckz959wegf47s078c9c5jwuqp0a6w5qgfjy87", + "sender": "erd1jrvq9emyfd97xs2kq53qedkz4sv40aayjt7tjcw9x6kjujvuv8uqnww788", + "relayerAddress": "erd19al0qr2zcgu067sku2m86djxp5dexle6we2lrlm63z29q2yawfsqmnxk78", + "data": "random", + "operation": "transfer" + } + ], + "status": "success", + "receivers": [ + "erd14ahnvw59a4ku45qz6e9r0ckz959wegf47s078c9c5jwuqp0a6w5qgfjy87" + ], + "receiversShardIDs": [ + 0 + ], + "operation": "transfer", + "initiallyPaidFee": "481500000000000", + "fee": "481500000000000", + "isRelayed": true, + "chainID": "1", + "version": 1, + "options": 0 + } +} \ No newline at end of file diff --git a/process/transactionProcessor.go b/process/transactionProcessor.go index 70f6a988..f6118c1a 100644 --- a/process/transactionProcessor.go +++ b/process/transactionProcessor.go @@ -40,8 +40,11 @@ const ( nonceGapsParam = "?nonce-gaps=true" internalVMErrorsEventIdentifier = "internalVMErrors" // TODO export this in mx-chain-core-go, remove unexported definitions from mx-chain-vm's moveBalanceDescriptor = "MoveBalance" - relayedTransactionDescriptor = "RelayedTx" + relayedV1TransactionDescriptor = "RelayedTx" + relayedV2TransactionDescriptor = "RelayedTxV2" relayedTxV1DataMarker = "relayedTx@" + relayedTxV2DataMarker = "relayedTxV2" + argumentsSeparator = "@" ) type requestType int @@ -504,7 +507,7 @@ func (tp *TransactionProcessor) addMissingLogsOnProcessingExceptions( allLogs []*transaction.ApiLogs, allScrs []*transaction.ApiTransactionResult, ) ([]*transaction.ApiLogs, error) { - newLogs, err := tp.handleIntraShardRelayedV1MoveBalanceTransactions(tx, allScrs) + newLogs, err := tp.handleIntraShardRelayedMoveBalanceTransactions(tx, allScrs) if err != nil { return nil, err } @@ -514,12 +517,12 @@ func (tp *TransactionProcessor) addMissingLogsOnProcessingExceptions( return allLogs, nil } -func (tp *TransactionProcessor) handleIntraShardRelayedV1MoveBalanceTransactions( +func (tp *TransactionProcessor) handleIntraShardRelayedMoveBalanceTransactions( tx *transaction.ApiTransactionResult, allScrs []*transaction.ApiTransactionResult, ) ([]*transaction.ApiLogs, error) { var newLogs []*transaction.ApiLogs - isIntraShardRelayedV1MoveBalanceTransaction, err := tp.isIntraShardRelayedV1MoveBalanceTransaction(tx, allScrs) + isIntraShardRelayedV1MoveBalanceTransaction, err := tp.isIntraShardRelayedMoveBalanceTransaction(tx, allScrs) if err != nil { return newLogs, err } @@ -539,7 +542,7 @@ func (tp *TransactionProcessor) handleIntraShardRelayedV1MoveBalanceTransactions return newLogs, nil } -func (tp *TransactionProcessor) isIntraShardRelayedV1MoveBalanceTransaction( +func (tp *TransactionProcessor) isIntraShardRelayedMoveBalanceTransaction( tx *transaction.ApiTransactionResult, allScrs []*transaction.ApiTransactionResult, ) (bool, error) { @@ -548,7 +551,10 @@ func (tp *TransactionProcessor) isIntraShardRelayedV1MoveBalanceTransaction( return false, nil } - isRelayedTransaction := tx.ProcessingTypeOnSource == relayedTransactionDescriptor && tx.ProcessingTypeOnDestination == relayedTransactionDescriptor + isRelayedV1 := tx.ProcessingTypeOnSource == relayedV1TransactionDescriptor && tx.ProcessingTypeOnDestination == relayedV1TransactionDescriptor + isRelayedV2 := tx.ProcessingTypeOnSource == relayedV2TransactionDescriptor && tx.ProcessingTypeOnDestination == relayedV2TransactionDescriptor + + isRelayedTransaction := isRelayedV1 || isRelayedV2 if !isRelayedTransaction { return false, nil } @@ -572,12 +578,33 @@ func (tp *TransactionProcessor) isIntraShardRelayedV1MoveBalanceTransaction( return false, err } - dataField := string(tx.Data) - if strings.Index(dataField, relayedTxV1DataMarker) != 0 { + isSameShardOnRelayed := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) + isInnerTxSameShard, err := tp.isSameShardSenderReceiverOfInnerTx(senderAddress, tx) + + return isSameShardOnRelayed && isInnerTxSameShard, err +} + +func (tp *TransactionProcessor) isSameShardSenderReceiverOfInnerTx( + relayedSender []byte, + relayedTx *transaction.ApiTransactionResult, +) (bool, error) { + if relayedTx.ProcessingTypeOnSource == relayedV1TransactionDescriptor { + return tp.isSameShardSenderReceiverOfInnerTxV1(relayedSender, relayedTx) + } + + return tp.isSameShardSenderReceiverOfInnerTxV2(relayedSender, relayedTx) +} + +func (tp *TransactionProcessor) isSameShardSenderReceiverOfInnerTxV1( + relayedSender []byte, + relayedTx *transaction.ApiTransactionResult, +) (bool, error) { + relayedDataField := string(relayedTx.Data) + if strings.Index(relayedDataField, relayedTxV1DataMarker) != 0 { return false, fmt.Errorf("wrong relayed v1 data marker position") } - hexedInnerTxData := dataField[len(relayedTxV1DataMarker):] + hexedInnerTxData := relayedDataField[len(relayedTxV1DataMarker):] innerTxData, err := hex.DecodeString(hexedInnerTxData) if err != nil { return false, err @@ -589,11 +616,32 @@ func (tp *TransactionProcessor) isIntraShardRelayedV1MoveBalanceTransaction( return false, err } - isSameShardOnRelayed := tp.proc.GetShardCoordinator().SameShard(senderAddress, receiverAddress) - isSameShardOnInnerForReceiver := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.RcvAddr) - isSameShardOnInnerForSender := tp.proc.GetShardCoordinator().SameShard(senderAddress, innerTx.SndAddr) + isSameShardOnInnerForReceiver := tp.proc.GetShardCoordinator().SameShard(relayedSender, innerTx.RcvAddr) + isSameShardOnInnerForSender := tp.proc.GetShardCoordinator().SameShard(relayedSender, innerTx.SndAddr) + + return isSameShardOnInnerForReceiver && isSameShardOnInnerForSender, nil +} + +func (tp *TransactionProcessor) isSameShardSenderReceiverOfInnerTxV2( + relayedSender []byte, + relayedTx *transaction.ApiTransactionResult, +) (bool, error) { + relayedDataField := string(relayedTx.Data) + if strings.Index(relayedDataField, relayedTxV2DataMarker) != 0 { + return false, fmt.Errorf("wrong relayed v2 data marker position") + } + arguments := strings.Split(relayedDataField, argumentsSeparator) + if len(arguments) < 2 { + return false, fmt.Errorf("wrong relayed v2 formatted data field") + } + + hexedReceiver := arguments[1] + receiver, err := hex.DecodeString(hexedReceiver) + if err != nil { + return false, err + } - return isSameShardOnRelayed && isSameShardOnInnerForReceiver && isSameShardOnInnerForSender, nil + return tp.proc.GetShardCoordinator().SameShard(relayedSender, receiver), nil } func findIdentifierInLogs(logs []*transaction.ApiLogs, identifier string) bool { diff --git a/process/transactionProcessor_test.go b/process/transactionProcessor_test.go index eca19b57..e8191280 100644 --- a/process/transactionProcessor_test.go +++ b/process/transactionProcessor_test.go @@ -1925,6 +1925,15 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, transaction.TxStatusSuccess, status) }) + t.Run("ok relayed v2 move balance intra shard transaction", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedV2TxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, transaction.TxStatusSuccess, status) + }) t.Run("ok relayed sc call function balance intra shard transaction still pending", func(t *testing.T) { t.Parallel() @@ -2010,6 +2019,28 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, data.TxStatusUnknown, status) }) + t.Run("malformed relayed v2 - missing marker", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedV2TxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Data = []byte("aa") + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, data.TxStatusUnknown, status) + }) + t.Run("malformed relayed v2 - not enough arguments", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/finishedOKRelayedV2TxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + + testData.Transaction.Data = []byte(process.RelayedTxV2DataMarker) + + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, data.TxStatusUnknown, status) + }) t.Run("malformed relayed v1 - not a hex character after the marker", func(t *testing.T) { t.Parallel() @@ -2049,6 +2080,15 @@ func TestTransactionProcessor_computeTransactionStatus(t *testing.T) { testData := loadJsonIntoTxAndScrs(t, "./testdata/malformedRelayedTxIntraShard.json") tp := createTestProcessorFromScenarioData(testData) + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) + require.Equal(t, data.TxStatusUnknown, status) + }) + t.Run("malformed relayed v2 - no scr generated", func(t *testing.T) { + t.Parallel() + + testData := loadJsonIntoTxAndScrs(t, "./testdata/malformedRelayedV2TxIntraShard.json") + tp := createTestProcessorFromScenarioData(testData) + status := tp.ComputeTransactionStatus(testData.Transaction, withResults) require.Equal(t, data.TxStatusUnknown, status) })