Skip to content

Commit

Permalink
Exotic scenario: extractInnerTxOperationsIfBeforeSiriusRelayedComplet…
Browse files Browse the repository at this point in the history
…elyIntrashardWithSignalError.
  • Loading branch information
andreibancioiu committed Aug 22, 2024
1 parent 4cf3d53 commit 406b3fc
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 12 deletions.
10 changes: 10 additions & 0 deletions cmd/rosetta/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -203,6 +210,7 @@ func getAllCliFlags() []cli.Flag {
cliFlagNumHistoricalEpochs,
cliFlagShouldHandleContracts,
cliFlagConfigFileCustomCurrencies,
cliFlagActivationEpochSirius,
cliFlagActivationEpochSpica,
}
}
Expand Down Expand Up @@ -233,6 +241,7 @@ type parsedCliFlags struct {
numHistoricalEpochs uint32
shouldHandleContracts bool
configFileCustomCurrencies string
activationEpochSirius uint32
activationEpochSpica uint32
}

Expand Down Expand Up @@ -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)),
}
}
1 change: 1 addition & 0 deletions cmd/rosetta/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions server/factory/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
2 changes: 2 additions & 0 deletions server/factory/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type ArgsCreateNetworkProvider struct {
FirstHistoricalEpoch uint32
NumHistoricalEpochs uint32
ShouldHandleContracts bool
ActivationEpochSirius uint32
ActivationEpochSpica uint32
}

Expand Down Expand Up @@ -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{
Expand Down
11 changes: 11 additions & 0 deletions server/provider/networkProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type ArgsNewNetworkProvider struct {
FirstHistoricalEpoch uint32
NumHistoricalEpochs uint32
ShouldHandleContracts bool
ActivationEpochSirius uint32
ActivationEpochSpica uint32

ObserverFacade observerFacade
Expand All @@ -65,6 +66,7 @@ type networkProvider struct {
firstHistoricalEpoch uint32
numHistoricalEpochs uint32
shouldHandleContracts bool
activationEpochSirius uint32
activationEpochSpica uint32

observerFacade observerFacade
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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(),
)
Expand Down
1 change: 1 addition & 0 deletions server/services/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
23 changes: 16 additions & 7 deletions server/services/transactionsTransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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 {
Expand All @@ -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)

Expand Down
55 changes: 50 additions & 5 deletions server/services/transactionsTransformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -103,19 +106,61 @@ 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) {
networkProvider := testscommon.NewNetworkProviderMock()
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)
})
Expand All @@ -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)
})
Expand All @@ -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{
{
Expand Down Expand Up @@ -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)
})
Expand Down
6 changes: 6 additions & 0 deletions testscommon/networkProviderMock.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type networkProviderMock struct {
MockCustomCurrencies []resources.Currency
MockGenesisBlockHash string
MockGenesisTimestamp int64
MockActivationEpochSirius uint32
MockActivationEpochSpica uint32
MockNetworkConfig *resources.NetworkConfig
MockGenesisBalances []*resources.GenesisBalance
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 406b3fc

Please sign in to comment.