Skip to content

Commit

Permalink
Handle relayed V1, intra-shard, whose inner tx moves native value, wh…
Browse files Browse the repository at this point in the history
…ich result in signal error - before vs. after Sirius.
  • Loading branch information
andreibancioiu committed Aug 22, 2024
1 parent 05fed49 commit 6388e67
Show file tree
Hide file tree
Showing 12 changed files with 143 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-sirius",
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.activationEpochSirius,
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 @@ -432,6 +435,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.activationEpochSirius
}

// 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 @@ -449,6 +457,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
}
29 changes: 22 additions & 7 deletions server/services/transactionsTransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,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 @@ -221,8 +217,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 @@ -242,14 +249,22 @@ 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)

return []*types.Operation{
{
Type: opTransfer,
Account: addressToAccountIdentifier(senderAddress),
Amount: transformer.extension.valueToNativeAmount("-" + innerTx.Value.String()),
},
{
Type: opTransfer,
Account: addressToAccountIdentifier(receiverAddress),
Amount: transformer.extension.valueToNativeAmount(innerTx.Value.String()),
},
}, nil
}

Expand Down
65 changes: 60 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 @@ -90,6 +93,51 @@ func TestTransactionsTransformer_NormalTxToRosettaTx(t *testing.T) {
Account: addressToAccountIdentifier(testscommon.TestUserCShard0.Address),
Amount: extension.valueToNativeAmount("-1000000000000000000"),
},
{
Type: opTransfer,
Account: addressToAccountIdentifier(testscommon.TestAddressOfContract),
Amount: extension.valueToNativeAmount("1000000000000000000"),
},
},
Metadata: extractTransactionMetadata(tx),
}

rosettaTx, err := transformer.normalTxToRosetta(tx)
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),
}
Expand All @@ -105,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)
})
Expand All @@ -123,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 @@ -145,14 +195,19 @@ func TestTransactionsTransformer_ExtractInnerTxOperationsIfRelayedCompletelyIntr
},
}

operations, err := transformer.extractInnerTxOperationsIfRelayedCompletelyIntrashardWithSignalError(tx)
operations, err := transformer.extractInnerTxOperationsIfBeforeSiriusRelayedCompletelyIntrashardWithSignalError(tx)
require.NoError(t, err)
require.Equal(t, []*types.Operation{
{
Type: opTransfer,
Account: addressToAccountIdentifier(testscommon.TestUserCShard0.Address),
Amount: extension.valueToNativeAmount("-1000000000000000000"),
},
{
Type: opTransfer,
Account: addressToAccountIdentifier(testscommon.TestAddressOfContract),
Amount: extension.valueToNativeAmount("1000000000000000000"),
},
}, operations)
})

Expand All @@ -173,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
2 changes: 2 additions & 0 deletions systemtests/check_with_mesh_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def run_rosetta(configuration: Configuration):
f"--config-custom-currencies={configuration.config_file_custom_currencies}",
f"--first-historical-epoch={current_epoch}",
f"--num-historical-epochs={configuration.num_historical_epochs}",
f"--activation-epoch-sirius={configuration.activation_epoch_sirius}",
f"--activation-epoch-spica={configuration.activation_epoch_spica}",
]

return subprocess.Popen(command)
Expand Down
27 changes: 27 additions & 0 deletions systemtests/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class Configuration:
num_historical_epochs: int
observer_url: str
proxy_url: str
activation_epoch_sirius: int
activation_epoch_spica: int
check_construction_native_configuration_file: str
check_construction_custom_configuration_file: str
check_data_configuration_file: str
Expand All @@ -24,6 +26,25 @@ class Configuration:


CONFIGURATIONS = {
"mainnet": Configuration(
network_shard=0,
network_id="1",
network_name="untitled",
native_currency="EGLD",
config_file_custom_currencies="systemtests/rosetta_config/mainnet-custom-currencies.json",
num_historical_epochs=2,
observer_url="",
proxy_url="https://gateway.multiversx.com",
activation_epoch_sirius=1265,
activation_epoch_spica=4294967295,
check_construction_native_configuration_file="",
check_construction_custom_configuration_file="",
check_data_configuration_file="systemtests/mesh_cli_config/check-data.json",
check_data_directory="systemtests/mainnet-data",
known_contracts=[
],
explorer_url="https://explorer.multiversx.com",
),
"devnet": Configuration(
network_shard=0,
network_id="D",
Expand All @@ -33,6 +54,8 @@ class Configuration:
num_historical_epochs=2,
observer_url="",
proxy_url="https://devnet-gateway.multiversx.com",
activation_epoch_sirius=629,
activation_epoch_spica=4294967295,
check_construction_native_configuration_file="systemtests/mesh_cli_config/devnet-construction-native.json",
check_construction_custom_configuration_file="systemtests/mesh_cli_config/devnet-construction-custom.json",
check_data_configuration_file="systemtests/mesh_cli_config/check-data.json",
Expand All @@ -53,6 +76,8 @@ class Configuration:
num_historical_epochs=1,
observer_url="",
proxy_url="https://testnet-gateway.multiversx.com",
activation_epoch_sirius=1,
activation_epoch_spica=4294967295,
check_construction_native_configuration_file="systemtests/mesh_cli_config/testnet-construction-native.json",
check_construction_custom_configuration_file="systemtests/mesh_cli_config/testnet-construction-custom.json",
check_data_configuration_file="systemtests/mesh_cli_config/check-data.json",
Expand All @@ -73,6 +98,8 @@ class Configuration:
num_historical_epochs=2,
observer_url="",
proxy_url="http://localhost:7950",
activation_epoch_sirius=1,
activation_epoch_spica=4294967295,
check_construction_native_configuration_file="systemtests/mesh_cli_config/localnet-construction-native.json",
check_construction_custom_configuration_file="systemtests/mesh_cli_config/localnet-construction-custom.json",
check_data_configuration_file="systemtests/mesh_cli_config/check-data.json",
Expand Down
Binary file modified systemtests/contracts/dummy.wasm
Binary file not shown.
Loading

0 comments on commit 6388e67

Please sign in to comment.