From 406b3fc63216f0303ee00075c7edd5adab6b85da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Thu, 22 Aug 2024 13:49:10 +0300 Subject: [PATCH] Exotic scenario: extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError. --- cmd/rosetta/cli.go | 10 ++++ cmd/rosetta/main.go | 1 + server/factory/interface.go | 1 + server/factory/provider.go | 2 + server/provider/networkProvider.go | 11 ++++ server/services/interface.go | 1 + server/services/transactionsTransformer.go | 23 +++++--- .../services/transactionsTransformer_test.go | 55 +++++++++++++++++-- testscommon/networkProviderMock.go | 6 ++ 9 files changed, 98 insertions(+), 12 deletions(-) diff --git a/cmd/rosetta/cli.go b/cmd/rosetta/cli.go index 9c4cbcb9..5ce59cfa 100644 --- a/cmd/rosetta/cli.go +++ b/cmd/rosetta/cli.go @@ -169,6 +169,13 @@ VERSION: Required: false, } + cliFlagActivationEpochSirius = cli.UintFlag{ + Name: "activation-epoch-spica", + Usage: "Specifies the activation epoch for Sirius release.", + Required: false, + Value: 1265, + } + cliFlagActivationEpochSpica = cli.UintFlag{ Name: "activation-epoch-spica", Usage: "Specifies the activation epoch for Spica release.", @@ -203,6 +210,7 @@ func getAllCliFlags() []cli.Flag { cliFlagNumHistoricalEpochs, cliFlagShouldHandleContracts, cliFlagConfigFileCustomCurrencies, + cliFlagActivationEpochSirius, cliFlagActivationEpochSpica, } } @@ -233,6 +241,7 @@ type parsedCliFlags struct { numHistoricalEpochs uint32 shouldHandleContracts bool configFileCustomCurrencies string + activationEpochSirius uint32 activationEpochSpica uint32 } @@ -263,6 +272,7 @@ func getParsedCliFlags(ctx *cli.Context) parsedCliFlags { numHistoricalEpochs: uint32(ctx.GlobalUint(cliFlagNumHistoricalEpochs.Name)), shouldHandleContracts: ctx.GlobalBool(cliFlagShouldHandleContracts.Name), configFileCustomCurrencies: ctx.GlobalString(cliFlagConfigFileCustomCurrencies.Name), + activationEpochSirius: uint32(ctx.GlobalUint(cliFlagActivationEpochSirius.Name)), activationEpochSpica: uint32(ctx.GlobalUint(cliFlagActivationEpochSpica.Name)), } } diff --git a/cmd/rosetta/main.go b/cmd/rosetta/main.go index dbb2d423..c0c338a4 100644 --- a/cmd/rosetta/main.go +++ b/cmd/rosetta/main.go @@ -82,6 +82,7 @@ func startRosetta(ctx *cli.Context) error { FirstHistoricalEpoch: cliFlags.firstHistoricalEpoch, NumHistoricalEpochs: cliFlags.numHistoricalEpochs, ShouldHandleContracts: cliFlags.shouldHandleContracts, + ActivationEpochSirius: cliFlags.activationEpochSpica, ActivationEpochSpica: cliFlags.activationEpochSpica, }) if err != nil { diff --git a/server/factory/interface.go b/server/factory/interface.go index 861af975..0be4f8c9 100644 --- a/server/factory/interface.go +++ b/server/factory/interface.go @@ -35,6 +35,7 @@ type NetworkProvider interface { ComputeReceiptHash(apiReceipt *transaction.ApiReceipt) (string, error) ComputeTransactionFeeForMoveBalance(tx *transaction.ApiTransactionResult) *big.Int GetMempoolTransactionByHash(hash string) (*transaction.ApiTransactionResult, error) + IsReleaseSiriusActive(epoch uint32) bool IsReleaseSpicaActive(epoch uint32) bool LogDescription() } diff --git a/server/factory/provider.go b/server/factory/provider.go index b3acc26d..393a90ca 100644 --- a/server/factory/provider.go +++ b/server/factory/provider.go @@ -49,6 +49,7 @@ type ArgsCreateNetworkProvider struct { FirstHistoricalEpoch uint32 NumHistoricalEpochs uint32 ShouldHandleContracts bool + ActivationEpochSirius uint32 ActivationEpochSpica uint32 } @@ -141,6 +142,7 @@ func CreateNetworkProvider(args ArgsCreateNetworkProvider) (NetworkProvider, err FirstHistoricalEpoch: args.FirstHistoricalEpoch, NumHistoricalEpochs: args.NumHistoricalEpochs, ShouldHandleContracts: args.ShouldHandleContracts, + ActivationEpochSirius: args.ActivationEpochSirius, ActivationEpochSpica: args.ActivationEpochSpica, ObserverFacade: &components.ObserverFacade{ diff --git a/server/provider/networkProvider.go b/server/provider/networkProvider.go index 67ac8b69..cae2bdae 100644 --- a/server/provider/networkProvider.go +++ b/server/provider/networkProvider.go @@ -42,6 +42,7 @@ type ArgsNewNetworkProvider struct { FirstHistoricalEpoch uint32 NumHistoricalEpochs uint32 ShouldHandleContracts bool + ActivationEpochSirius uint32 ActivationEpochSpica uint32 ObserverFacade observerFacade @@ -65,6 +66,7 @@ type networkProvider struct { firstHistoricalEpoch uint32 numHistoricalEpochs uint32 shouldHandleContracts bool + activationEpochSirius uint32 activationEpochSpica uint32 observerFacade observerFacade @@ -106,6 +108,7 @@ func NewNetworkProvider(args ArgsNewNetworkProvider) (*networkProvider, error) { firstHistoricalEpoch: args.FirstHistoricalEpoch, numHistoricalEpochs: args.NumHistoricalEpochs, shouldHandleContracts: args.ShouldHandleContracts, + activationEpochSirius: args.ActivationEpochSirius, activationEpochSpica: args.ActivationEpochSpica, observerFacade: args.ObserverFacade, @@ -465,6 +468,11 @@ func (provider *networkProvider) ComputeTransactionFeeForMoveBalance(tx *transac return fee } +// IsReleaseSiriusActive returns whether the Sirius release is active in the provided epoch +func (provider *networkProvider) IsReleaseSiriusActive(epoch uint32) bool { + return epoch < provider.activationEpochSpica +} + // IsReleaseSpicaActive returns whether the Spica release is active in the provided epoch func (provider *networkProvider) IsReleaseSpicaActive(epoch uint32) bool { return epoch >= provider.activationEpochSpica @@ -482,6 +490,9 @@ func (provider *networkProvider) LogDescription() { "observedProjectedShardIsSet", provider.observedProjectedShardIsSet, "firstHistoricalEpoch", provider.firstHistoricalEpoch, "numHistoricalEpochs", provider.numHistoricalEpochs, + "shouldHandleContracts", provider.shouldHandleContracts, + "activationEpochSirius", provider.activationEpochSirius, + "activationEpochSpica", provider.activationEpochSpica, "nativeCurrency", provider.GetNativeCurrency().Symbol, "customCurrencies", provider.GetCustomCurrenciesSymbols(), ) diff --git a/server/services/interface.go b/server/services/interface.go index ca9630dc..dab3742a 100644 --- a/server/services/interface.go +++ b/server/services/interface.go @@ -34,5 +34,6 @@ type NetworkProvider interface { ComputeReceiptHash(apiReceipt *transaction.ApiReceipt) (string, error) ComputeTransactionFeeForMoveBalance(tx *transaction.ApiTransactionResult) *big.Int GetMempoolTransactionByHash(hash string) (*transaction.ApiTransactionResult, error) + IsReleaseSiriusActive(epoch uint32) bool IsReleaseSpicaActive(epoch uint32) bool } diff --git a/server/services/transactionsTransformer.go b/server/services/transactionsTransformer.go index 73683caf..2e0a8c59 100644 --- a/server/services/transactionsTransformer.go +++ b/server/services/transactionsTransformer.go @@ -214,15 +214,11 @@ func (transformer *transactionsTransformer) normalTxToRosetta(tx *transaction.Ap Amount: transformer.extension.valueToNativeAmount("-" + tx.InitiallyPaidFee), }) - innerTxOperationsIfRelayedCompletelyIntrashardWithSignalError, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx) + innerTxOperationsIfRelayedCompletelyIntrashardWithSignalError, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx) if err != nil { return nil, err } - if len(innerTxOperationsIfRelayedCompletelyIntrashardWithSignalError) > 0 { - log.Info("normalTxToRosetta(): innerTxOperationsIfRelayedCompletelyIntrashardWithSignalError", "tx", tx.Hash, "block", tx.BlockNonce) - } - operations = append(operations, innerTxOperationsIfRelayedCompletelyIntrashardWithSignalError...) return &types.Transaction{ @@ -232,8 +228,19 @@ func (transformer *transactionsTransformer) normalTxToRosetta(tx *transaction.Ap }, nil } -// This only handles operations for the native balance. -func (transformer *transactionsTransformer) extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx *transaction.ApiTransactionResult) ([]*types.Operation, error) { +// extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError recovers the inner transaction operations (native balance transfers) +// for a transaction that is relayed and completely intrashard, and has a signal error, and was processed before the activation of Sirius features. +// Before Sirius, such a transaction is accompanied by an SCR with the value refund, thus Rosetta has to recover the inner transaction operations, as well, +// to show the complete picture (that the operations cancel each other out). +// After Sirius, the protocol does not generate an SCR with the value refund for such transactions, so this workaround is not needed. +// Additional references: +// - https://github.com/multiversx/mx-chain-rosetta/pull/81/files +// - https://console.cloud.google.com/bigquery?sq=667383445384:bfeb7de9aeec453192612ddc7fa9d94e +func (transformer *transactionsTransformer) extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx *transaction.ApiTransactionResult) ([]*types.Operation, error) { + if transformer.provider.IsReleaseSiriusActive(tx.Epoch) { + return []*types.Operation{}, nil + } + // Only relayed V1 is handled. Relayed V2 cannot bear native value in the inner transaction. isRelayedTransaction := isRelayedV1Transaction(tx) if !isRelayedTransaction { @@ -253,6 +260,8 @@ func (transformer *transactionsTransformer) extractInnerTxOperationsIfRelayedCom return []*types.Operation{}, nil } + log.Info("extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError", "tx", tx.Hash) + senderAddress := transformer.provider.ConvertPubKeyToAddress(innerTx.SenderPubKey) receiverAddress := transformer.provider.ConvertPubKeyToAddress(innerTx.ReceiverPubKey) diff --git a/server/services/transactionsTransformer_test.go b/server/services/transactionsTransformer_test.go index e9056190..160a2c4d 100644 --- a/server/services/transactionsTransformer_test.go +++ b/server/services/transactionsTransformer_test.go @@ -19,6 +19,8 @@ func TestTransactionsTransformer_NormalTxToRosettaTx(t *testing.T) { extension := newNetworkProviderExtension(networkProvider) transformer := newTransactionsTransformer(networkProvider) + networkProvider.MockActivationEpochSirius = 42 + t.Run("move balance tx", func(t *testing.T) { tx := &transaction.ApiTransactionResult{ Hash: "aaaa", @@ -55,8 +57,9 @@ func TestTransactionsTransformer_NormalTxToRosettaTx(t *testing.T) { require.Equal(t, expectedRosettaTx, rosettaTx) }) - t.Run("relayed tx, completely intrashard, with signal error", func(t *testing.T) { + t.Run("relayed tx, completely intrashard, with signal error (before Sirius)", func(t *testing.T) { tx := &transaction.ApiTransactionResult{ + Epoch: 41, Type: string(transaction.TxTypeNormal), ProcessingTypeOnSource: transactionProcessingTypeRelayedV1, ProcessingTypeOnDestination: transactionProcessingTypeRelayedV1, @@ -103,6 +106,46 @@ func TestTransactionsTransformer_NormalTxToRosettaTx(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedRosettaTx, rosettaTx) }) + + t.Run("relayed tx, completely intrashard, with signal error (after Sirius)", func(t *testing.T) { + tx := &transaction.ApiTransactionResult{ + Epoch: 43, + Type: string(transaction.TxTypeNormal), + ProcessingTypeOnSource: transactionProcessingTypeRelayedV1, + ProcessingTypeOnDestination: transactionProcessingTypeRelayedV1, + Hash: "aaaa", + Sender: testscommon.TestUserAShard0.Address, + Receiver: testscommon.TestUserBShard0.Address, + SourceShard: 0, + DestinationShard: 0, + InitiallyPaidFee: "50000000000000", + // Has non-zero value + Data: []byte("relayedTx@7b226e6f6e6365223a372c2273656e646572223a226e69424758747949504349644a78793373796c6c455a474c78506a503148734a45646e43732b6d726577413d222c227265636569766572223a224141414141414141414141464145356c3079623173734a3933504433672f4b396f48384579366d576958513d222c2276616c7565223a313030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a226e6830743338585a77614b6a725878446969716f6d364d6a5671724d612f6b70767474696a33692b5a6d43492f3778626830596762363548424151445744396f7036575567674541755430756e5253595736455341413d3d222c22636861696e4944223a224d513d3d222c2276657273696f6e223a327d"), + Logs: &transaction.ApiLogs{ + Events: []*transaction.Events{ + { + Identifier: transactionEventSignalError, + }, + }, + }, + } + + expectedRosettaTx := &types.Transaction{ + TransactionIdentifier: hashToTransactionIdentifier("aaaa"), + Operations: []*types.Operation{ + { + Type: opFee, + Account: addressToAccountIdentifier(testscommon.TestUserAShard0.Address), + Amount: extension.valueToNativeAmount("-50000000000000"), + }, + }, + Metadata: extractTransactionMetadata(tx), + } + + rosettaTx, err := transformer.normalTxToRosetta(tx) + require.NoError(t, err) + require.Equal(t, expectedRosettaTx, rosettaTx) + }) } func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(t *testing.T) { @@ -110,12 +153,14 @@ func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntr extension := newNetworkProviderExtension(networkProvider) transformer := newTransactionsTransformer(networkProvider) + networkProvider.MockActivationEpochSirius = 42 + t.Run("non-relayed tx", func(t *testing.T) { tx := &transaction.ApiTransactionResult{ Type: string(transaction.TxTypeNormal), } - operations, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx) + operations, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx) require.NoError(t, err) require.Len(t, operations, 0) }) @@ -128,7 +173,7 @@ func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntr Data: []byte("bad"), } - operations, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx) + operations, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx) require.ErrorIs(t, err, errCannotParseRelayedV1) require.Empty(t, operations) }) @@ -150,7 +195,7 @@ func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntr }, } - operations, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx) + operations, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx) require.NoError(t, err) require.Equal(t, []*types.Operation{ { @@ -183,7 +228,7 @@ func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntr }, } - operations, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx) + operations, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx) require.NoError(t, err) require.Len(t, operations, 0) }) diff --git a/testscommon/networkProviderMock.go b/testscommon/networkProviderMock.go index 38c5606e..84e8b739 100644 --- a/testscommon/networkProviderMock.go +++ b/testscommon/networkProviderMock.go @@ -31,6 +31,7 @@ type networkProviderMock struct { MockCustomCurrencies []resources.Currency MockGenesisBlockHash string MockGenesisTimestamp int64 + MockActivationEpochSirius uint32 MockActivationEpochSpica uint32 MockNetworkConfig *resources.NetworkConfig MockGenesisBalances []*resources.GenesisBalance @@ -364,6 +365,11 @@ func (mock *networkProviderMock) GetMempoolTransactionByHash(hash string) (*tran return nil, nil } +// IsReleaseSiriusActive - +func (mock *networkProviderMock) IsReleaseSiriusActive(epoch uint32) bool { + return epoch >= mock.MockActivationEpochSirius +} + // IsReleaseSpicaActive - func (mock *networkProviderMock) IsReleaseSpicaActive(epoch uint32) bool { return epoch >= mock.MockActivationEpochSpica