Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/regularly_check_mainnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@ jobs:

- name: check:data (shard 0)
run: |
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=0 --num-blocks=3000
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=0 --num-blocks=5000
env:
MAINNET_PROXY_URL: ${{ secrets.MAINNET_PROXY_URL }}
PYTHONPATH: .

- name: check:data (shard 1)
run: |
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=1 --num-blocks=3000
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=1 --num-blocks=5000
env:
MAINNET_PROXY_URL: ${{ secrets.MAINNET_PROXY_URL }}
PYTHONPATH: .

- name: check:data (shard 2)
run: |
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=2 --num-blocks=3000
python3 ./systemtests/check_with_mesh_cli.py --mode=data --network=mainnet --shard=2 --num-blocks=5000
env:
MAINNET_PROXY_URL: ${{ secrets.MAINNET_PROXY_URL }}
PYTHONPATH: .
22 changes: 10 additions & 12 deletions server/services/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ import (
)

const (
opGenesisBalanceMovement = "GenesisBalanceMovement"
opTransfer = "Transfer"
opFee = "Fee"
opReward = "Reward"
opScResult = "SmartContractResult"
opFeeRefundAsScResult = "FeeRefundAsSmartContractResult"
opDeveloperRewardsAsScResult = "DeveloperRewardsAsSmartContractResult"
opDeveloperRewards = "DeveloperRewards"
opFeeOfInvalidTx = "FeeOfInvalidTransaction"
opFeeRefund = "FeeRefund"
opCustomTransfer = "CustomTransfer"
opGenesisBalanceMovement = "GenesisBalanceMovement"
opTransfer = "Transfer"
opFee = "Fee"
opReward = "Reward"
opScResult = "SmartContractResult"
opFeeRefundAsScResult = "FeeRefundAsSmartContractResult"
opDeveloperRewards = "DeveloperRewards"
opFeeOfInvalidTx = "FeeOfInvalidTransaction"
opFeeRefund = "FeeRefund"
opCustomTransfer = "CustomTransfer"
)

var (
Expand All @@ -27,7 +26,6 @@ var (
opReward,
opScResult,
opFeeRefundAsScResult,
opDeveloperRewardsAsScResult,
opDeveloperRewards,
opFeeOfInvalidTx,
opFeeRefund,
Expand Down
75 changes: 55 additions & 20 deletions server/services/testdata/blocks_with_claim_developer_rewards.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,48 @@
"type": "normal",
"processingTypeOnSource": "BuiltInFunctionCall",
"processingTypeOnDestination": "BuiltInFunctionCall",
"hash": "96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75",
"epoch": 20,
"hash": "e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1",
"nonce": 536,
"round": 1092226,
"epoch": 909,
"value": "0",
"receiver": "erd1qqqqqqqqqqqqqpgqagjekf5mxv86hy5c62vvtug5vc6jmgcsq6uq8reras",
"receiver": "erd1qqqqqqqqqqqqqpgq2kndhnp65u9jvg79pz6luthf2tcfc9f6q6uq28n2n9",
"sender": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6",
"gasPrice": 1000000000,
"gasLimit": 8000000,
"data": "Q2xhaW1EZXZlbG9wZXJSZXdhcmRz",
"signature": "132aa54ec093c21e2d0d447bf9a9b1da5c33d4120da66fb53dbc78d1d3d007e2314fa06348302f503e96a91b697dd943189df0ad4c3750416a704de2ac007e06",
"signature": "b8771fe3f0184bed47f002543f5302b93bfeace7cf1c3ef138522e4c53e162745359d4eb4fa2cf40036ebc1c4db9e8269ea5b81047aa6d9f6965c5792d84e40e",
"sourceShard": 0,
"destinationShard": 0,
"miniblockType": "TxBlock",
"miniblockHash": "2684c32382e5acd3912d8ae7adfa934e1950d24927f0b99baae6617182a1eae8",
"logs": {
"address": "erd1qqqqqqqqqqqqqpgqagjekf5mxv86hy5c62vvtug5vc6jmgcsq6uq8reras",
"address": "erd1qqqqqqqqqqqqqpgq2kndhnp65u9jvg79pz6luthf2tcfc9f6q6uq28n2n9",
"events": [
{
"address": "erd1qqqqqqqqqqqqqpgqagjekf5mxv86hy5c62vvtug5vc6jmgcsq6uq8reras",
"address": "erd1qqqqqqqqqqqqqpgq2kndhnp65u9jvg79pz6luthf2tcfc9f6q6uq28n2n9",
"identifier": "ClaimDeveloperRewards",
"topics": [
"AZ012mtA",
"XPSryD5QxTCdgH/D9naYh1mh4wEAG8mgJlgE9Cr4Brg="
],
"data": null,
"additionalData": null
},
{
"address": "erd1qqqqqqqqqqqqqpgq2kndhnp65u9jvg79pz6luthf2tcfc9f6q6uq28n2n9",
"identifier": "completedTxEvent",
"topics": [
"lrGgUz/xffPRd3iJEXAj0LF4zID6clNdG47BoTvPOnU="
"4Ms+EIxOBKbSQG1xjCD5drNl3j0d0AZ/0vrplvfJy9E="
],
"data": null,
"additionalData": null
}
]
},
"status": "success",
"operation": "ClaimDeveloperRewards",
"function": "ClaimDeveloperRewards",
"initiallyPaidFee": "160685000000000"
}
]
Expand All @@ -42,33 +60,50 @@
"type": "unsigned",
"processingTypeOnSource": "MoveBalance",
"processingTypeOnDestination": "MoveBalance",
"hash": "d05c5f65f564d740aa1e81f7a96581d739783a43c232a6c86112afa1e6c318c4",
"epoch": 20,
"hash": "7969b30a33e9d925cacda530a4afa74269cd4e366f0598486e23b70f5e10cf98",
"nonce": 0,
"round": 1092226,
"epoch": 909,
"value": "1774725000000",
"receiver": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6",
"sender": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6",
"previousTransactionHash": "96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75",
"originalTransactionHash": "96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75",
"gasPrice": 1000000000,
"previousTransactionHash": "e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1",
"originalTransactionHash": "e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1",
"originalSender": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6",
"sourceShard": 0,
"destinationShard": 0
"destinationShard": 0,
"miniblockType": "SmartContractResultBlock",
"miniblockHash": "8ba6961a4191dc4f42296eab4f68a56048fd257c943f4e787e31befb8b155d84",
"status": "success",
"operation": "transfer",
"callType": "directCall",
"options": 0
},
{
"type": "unsigned",
"processingTypeOnSource": "MoveBalance",
"processingTypeOnDestination": "MoveBalance",
"hash": "bc89442d5e77113f0b4e7383e5d078776fc6724690ba9f98d1704202c324e090",
"epoch": 20,
"hash": "06f81486996225b597bc7ed89b4062cd895491b33b1e81f34205ef48272bd644",
"nonce": 537,
"round": 1092226,
"epoch": 909,
"value": "29185000000000",
"receiver": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6",
"sender": "erd1qqqqqqqqqqqqqpgqagjekf5mxv86hy5c62vvtug5vc6jmgcsq6uq8reras",
"previousTransactionHash": "96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75",
"originalTransactionHash": "96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75",
"sender": "erd1qqqqqqqqqqqqqpgq2kndhnp65u9jvg79pz6luthf2tcfc9f6q6uq28n2n9",
"gasPrice": 1000000000,
"data": "QDZmNmI=",
"previousTransactionHash": "e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1",
"originalTransactionHash": "e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1",
"sourceShard": 0,
"destinationShard": 0,
"nonce": 29,
"data": "QDZmNmI=",
"isRefund": true
"miniblockType": "SmartContractResultBlock",
"miniblockHash": "8ba6961a4191dc4f42296eab4f68a56048fd257c943f4e787e31befb8b155d84",
"status": "success",
"operation": "transfer",
"isRefund": true,
"callType": "directCall",
"options": 0
}
]
}
Expand Down
3 changes: 3 additions & 0 deletions server/services/transactionsFeaturesDetector.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ func newTransactionsFeaturesDetector(provider NetworkProvider) *transactionsFeat
}

// Example SCRs can be found here: https://api.multiversx.com/transactions?function=ClaimDeveloperRewards
// Unfortunately, the network does not provide a way to easily and properly detect whether a SCR
// is the result of claiming developer rewards. Here, we apply a best-effort (and suboptimal) strategy:
// we scan through all the other items in the block, in order to find a parent transaction that matches the operation in question.
func (detector *transactionsFeaturesDetector) doesContractResultHoldRewardsOfClaimDeveloperRewards(
contractResult *transaction.ApiTransactionResult,
allTransactionsInBlock []*transaction.ApiTransactionResult,
Expand Down
56 changes: 28 additions & 28 deletions server/services/transactionsTransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,25 @@ func (transformer *transactionsTransformer) unsignedTxToRosettaTx(
}
}

if !transformer.areClaimDeveloperRewardsEventsEnabled(scr.Epoch) {
// Handle developer rewards in a legacy manner (without looking at events / logs)
if transformer.featuresDetector.doesContractResultHoldRewardsOfClaimDeveloperRewards(scr, txsInBlock) {
return &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier(scr.Hash),
Operations: []*types.Operation{
{
Type: opDeveloperRewardsAsScResult,
Account: addressToAccountIdentifier(scr.Receiver),
Amount: transformer.extension.valueToNativeAmount(scr.Value),
},
},
}
// Handle developer rewards:
//
// (a) When the developer rewards are claimed in an intra-shard fashion, the network generates misleading SCRs.
// In addition to the regular refund SCR, there's a SCR that notarizes the rewards as a misleading balance transfer, from the developer to self:
// - https://explorer.multiversx.com/transactions?function=ClaimDeveloperRewards&senderShard=0&receiverShard=0
// - and so on...
//
// (b) When the developer rewards are claimed in a cross-shard fashion, the network generates misleading SCRs.
// In addition to the regular refund SCR, there's a SCR that notarizes the rewards as a misleading balance transfer, from the contract to the developer:
// - https://explorer.multiversx.com/transactions?function=ClaimDeveloperRewards&senderShard=0&receiverShard=1
// - and so on ...
//
// Either way, correct transaction events with identifier "ClaimDeveloperRewards" are generated.
// Here, we simply ignore all SCRs which **seem to hold a developer reward**,
// since they are properly handled by "addOperationsGivenTransactionEvents".
if transformer.featuresDetector.doesContractResultHoldRewardsOfClaimDeveloperRewards(scr, txsInBlock) {
return &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier(scr.Hash),
Operations: []*types.Operation{},
}
}

Expand Down Expand Up @@ -585,18 +591,16 @@ func (transformer *transactionsTransformer) addOperationsGivenTransactionEvents(
rosettaTx.Operations = append(rosettaTx.Operations, operations...)
}

if transformer.areClaimDeveloperRewardsEventsEnabled(tx.Epoch) {
for _, event := range eventsClaimDeveloperRewards {
operations := []*types.Operation{
{
Type: opDeveloperRewards,
Account: addressToAccountIdentifier(event.receiverAddress),
Amount: transformer.extension.valueToNativeAmount(event.value),
},
}

rosettaTx.Operations = append(rosettaTx.Operations, operations...)
for _, event := range eventsClaimDeveloperRewards {
operations := []*types.Operation{
{
Type: opDeveloperRewards,
Account: addressToAccountIdentifier(event.receiverAddress),
Amount: transformer.extension.valueToNativeAmount(event.value),
},
}

rosettaTx.Operations = append(rosettaTx.Operations, operations...)
}

return nil
Expand Down Expand Up @@ -636,7 +640,3 @@ func (transformer *transactionsTransformer) extractOperationsFromEventESDT(event

return make([]*types.Operation, 0)
}

func (transformer *transactionsTransformer) areClaimDeveloperRewardsEventsEnabled(epoch uint32) bool {
return transformer.provider.IsReleaseSpicaActive(epoch)
}
31 changes: 11 additions & 20 deletions server/services/transactionsTransformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1260,16 +1260,15 @@ func TestTransactionsTransformer_TransformBlockTxsHavingClaimDeveloperRewards(t
blocks, err := readTestBlocks("testdata/blocks_with_claim_developer_rewards.json")
require.Nil(t, err)

t.Run("recover operations in legacy manner (without using events)", func(t *testing.T) {
t.Run("recover operations using ClaimDeveloperRewards events", func(t *testing.T) {
txs, err := transformer.transformBlockTxs(blocks[0])
require.Nil(t, err)
require.Len(t, txs, 3)
require.Len(t, txs[0].Operations, 1)
require.Len(t, txs, 2)
require.Len(t, txs[0].Operations, 2)
require.Len(t, txs[1].Operations, 1)
require.Len(t, txs[2].Operations, 1)

expectedTx0 := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("96b1a0533ff17df3d1777889117023d0b178cc80fa72535d1b8ec1a13bcf3a75"),
TransactionIdentifier: hashToTransactionIdentifier("e0cb3e108c4e04a6d2406d718c20f976b365de3d1dd0067fd2fae996f7c9cbd1"),
Operations: []*types.Operation{
{
Type: opFee,
Expand All @@ -1278,30 +1277,22 @@ func TestTransactionsTransformer_TransformBlockTxsHavingClaimDeveloperRewards(t
Amount: extension.valueToNativeAmount("-160685000000000"),
Status: &opStatusSuccess,
},
},
Metadata: extractTransactionMetadata(blocks[0].MiniBlocks[0].Transactions[0]),
}

require.Equal(t, expectedTx0, txs[0])

expectedTx1 := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("d05c5f65f564d740aa1e81f7a96581d739783a43c232a6c86112afa1e6c318c4"),
Operations: []*types.Operation{
{
Type: opDeveloperRewardsAsScResult,
OperationIdentifier: indexToOperationIdentifier(0),
Type: opDeveloperRewards,
OperationIdentifier: indexToOperationIdentifier(1),
Account: addressToAccountIdentifier("erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6"),
Amount: extension.valueToNativeAmount("1774725000000"),
Status: &opStatusSuccess,
},
},
Metadata: extractTransactionMetadata(blocks[0].MiniBlocks[0].Transactions[0]),
}

require.Equal(t, expectedTx1, txs[1])
require.Equal(t, expectedTx0, txs[0])

// Fee refund
expectedTx2 := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("bc89442d5e77113f0b4e7383e5d078776fc6724690ba9f98d1704202c324e090"),
expectedTx1 := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("06f81486996225b597bc7ed89b4062cd895491b33b1e81f34205ef48272bd644"),
Operations: []*types.Operation{
{
Type: opFeeRefundAsScResult,
Expand All @@ -1313,7 +1304,7 @@ func TestTransactionsTransformer_TransformBlockTxsHavingClaimDeveloperRewards(t
},
}

require.Equal(t, expectedTx2, txs[2])
require.Equal(t, expectedTx1, txs[1])
})
}

Expand Down
Loading