Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle & dismiss ineffective refunds #100

Merged
merged 2 commits into from
Aug 22, 2024
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
4 changes: 4 additions & 0 deletions server/services/networkProviderExtension.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func (extension *networkProviderExtension) getGenesisBlockIdentifier() *types.Bl
return blockSummaryToIdentifier(summary)
}

func (extension *networkProviderExtension) isContractAddress(address string) bool {
return !extension.isUserAddress(address)
}

func (extension *networkProviderExtension) isUserAddress(address string) bool {
pubKey, err := extension.provider.ConvertAddressToPubKey(address)
if err != nil {
Expand Down
16 changes: 12 additions & 4 deletions server/services/transactionsFeaturesDetector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import (
)

type transactionsFeaturesDetector struct {
networkProvider NetworkProvider
eventsController *transactionEventsController
networkProvider NetworkProvider
networkProviderExtension *networkProviderExtension
eventsController *transactionEventsController
}

func newTransactionsFeaturesDetector(provider NetworkProvider) *transactionsFeaturesDetector {
return &transactionsFeaturesDetector{
networkProvider: provider,
eventsController: newTransactionEventsController(provider),
networkProvider: provider,
networkProviderExtension: newNetworkProviderExtension(provider),
eventsController: newTransactionEventsController(provider),
}
}

Expand Down Expand Up @@ -106,3 +108,9 @@ func (detector *transactionsFeaturesDetector) isContractCallWithSignalError(tx *
func (detector *transactionsFeaturesDetector) isIntrashard(tx *transaction.ApiTransactionResult) bool {
return tx.SourceShard == tx.DestinationShard
}

// isSmartContractResultIneffectiveRefund detects smart contract results that are ineffective refunds.
// Also see: https://console.cloud.google.com/bigquery?sq=667383445384:de7a5f3f172f4b50a9aaed353ef79839
func (detector *transactionsFeaturesDetector) isSmartContractResultIneffectiveRefund(scr *transaction.ApiTransactionResult) bool {
return scr.IsRefund && scr.Sender == scr.Receiver && detector.networkProviderExtension.isContractAddress(scr.Sender)
}
23 changes: 23 additions & 0 deletions server/services/transactionsFeaturesDetector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,26 @@ func TestTransactionsFeaturesDetector_isIntrashard(t *testing.T) {
DestinationShard: 1,
}))
}

func TestTransactionsFeaturesDetector_isSmartContractResultIneffectiveRefund(t *testing.T) {
networkProvider := testscommon.NewNetworkProviderMock()
detector := newTransactionsFeaturesDetector(networkProvider)

require.True(t, detector.isSmartContractResultIneffectiveRefund(&transaction.ApiTransactionResult{
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressOfContract,
IsRefund: true,
}))

require.False(t, detector.isSmartContractResultIneffectiveRefund(&transaction.ApiTransactionResult{
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressOfContract,
IsRefund: false,
}))

require.False(t, detector.isSmartContractResultIneffectiveRefund(&transaction.ApiTransactionResult{
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressAlice,
IsRefund: false,
}))
}
9 changes: 9 additions & 0 deletions server/services/transactionsTransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ func (transformer *transactionsTransformer) unsignedTxToRosettaTx(
scr *transaction.ApiTransactionResult,
txsInBlock []*transaction.ApiTransactionResult,
) *types.Transaction {
if transformer.featuresDetector.isSmartContractResultIneffectiveRefund(scr) {
log.Info("unsignedTxToRosettaTx: ineffective refund", "hash", scr.Hash)

return &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier(scr.Hash),
Operations: []*types.Operation{},
}
}

if scr.IsRefund {
return &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier(scr.Hash),
Expand Down
106 changes: 64 additions & 42 deletions server/services/transactionsTransformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,55 +239,77 @@ func TestTransactionsTransformer_UnsignedTxToRosettaTx(t *testing.T) {
extension := newNetworkProviderExtension(networkProvider)
transformer := newTransactionsTransformer(networkProvider)

refundTx := &transaction.ApiTransactionResult{
Hash: "aaaa",
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressAlice,
Value: "1234",
IsRefund: true,
}
t.Run("refund SCR", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Hash: "aaaa",
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressAlice,
Value: "1234",
IsRefund: true,
}

expectedRefundTx := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("aaaa"),
Operations: []*types.Operation{
{
Type: opFeeRefundAsScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressAlice),
Amount: extension.valueToNativeAmount("1234"),
expectedTx := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("aaaa"),
Operations: []*types.Operation{
{
Type: opFeeRefundAsScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressAlice),
Amount: extension.valueToNativeAmount("1234"),
},
},
},
}
}

moveBalanceTx := &transaction.ApiTransactionResult{
Hash: "aaaa",
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressAlice,
Value: "1234",
}
rosettaTx := transformer.unsignedTxToRosettaTx(tx, nil)
require.Equal(t, expectedTx, rosettaTx)
})

expectedMoveBalanceTx := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("aaaa"),
Operations: []*types.Operation{
{
Type: opScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressOfContract),
Amount: extension.valueToNativeAmount("-1234"),
},
{
Type: opScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressAlice),
Amount: extension.valueToNativeAmount("1234"),
t.Run("move balance SCR", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Hash: "aaaa",
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressAlice,
Value: "1234",
}

expectedTx := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("aaaa"),
Operations: []*types.Operation{
{
Type: opScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressOfContract),
Amount: extension.valueToNativeAmount("-1234"),
},
{
Type: opScResult,
Account: addressToAccountIdentifier(testscommon.TestAddressAlice),
Amount: extension.valueToNativeAmount("1234"),
},
},
},
Metadata: extractTransactionMetadata(moveBalanceTx),
}
Metadata: extractTransactionMetadata(tx),
}

rosettaTx := transformer.unsignedTxToRosettaTx(tx, []*transaction.ApiTransactionResult{tx})
require.Equal(t, expectedTx, rosettaTx)
})

txsInBlock := []*transaction.ApiTransactionResult{refundTx, moveBalanceTx}
t.Run("ineffective refund SCR", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Hash: "aaaa",
Sender: testscommon.TestAddressOfContract,
Receiver: testscommon.TestAddressOfContract,
Value: "1234",
IsRefund: true,
}

rosettaRefundTx := transformer.unsignedTxToRosettaTx(refundTx, txsInBlock)
rosettaMoveBalanceTx := transformer.unsignedTxToRosettaTx(moveBalanceTx, txsInBlock)
require.Equal(t, expectedRefundTx, rosettaRefundTx)
require.Equal(t, expectedMoveBalanceTx, rosettaMoveBalanceTx)
expectedTx := &types.Transaction{
TransactionIdentifier: hashToTransactionIdentifier("aaaa"),
Operations: []*types.Operation{},
Metadata: nil,
}

rosettaTx := transformer.unsignedTxToRosettaTx(tx, []*transaction.ApiTransactionResult{tx})
require.Equal(t, expectedTx, rosettaTx)
})
}

func TestTransactionsTransformer_InvalidTxToRosettaTx(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions systemtests/check_with_mesh_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def run_rosetta(configuration: Configuration):
f"--observer-actual-shard={configuration.network_shard}",
f"--network-id={configuration.network_id}",
f"--network-name={configuration.network_name}",
f"--handle-contracts",
f"--native-currency={configuration.native_currency}",
f"--config-custom-currencies={configuration.config_file_custom_currencies}",
f"--first-historical-epoch={current_epoch}",
Expand Down
Loading