Skip to content

Commit

Permalink
Merge pull request #83 from multiversx/main-into-esdt
Browse files Browse the repository at this point in the history
Merge main into esdt (1)
  • Loading branch information
andreibancioiu authored Nov 9, 2023
2 parents a7ddfe5 + bd5022e commit 3b26c0c
Show file tree
Hide file tree
Showing 25 changed files with 540 additions and 43 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/coinbase/rosetta-sdk-go v0.8.3
github.com/coinbase/rosetta-sdk-go/types v1.0.0
github.com/multiversx/mx-chain-core-go v1.1.37
github.com/multiversx/mx-chain-go v1.5.12
github.com/multiversx/mx-chain-go v1.5.13
github.com/multiversx/mx-chain-logger-go v1.0.11
github.com/multiversx/mx-chain-proxy-go v1.1.39
github.com/multiversx/mx-chain-storage-go v1.0.7
Expand Down Expand Up @@ -43,7 +43,7 @@ require (
github.com/multiversx/concurrent-map v0.1.4 // indirect
github.com/multiversx/mx-chain-crypto-go v1.2.6 // indirect
github.com/multiversx/mx-chain-es-indexer-go v1.3.20 // indirect
github.com/multiversx/mx-chain-p2p-go v1.0.17 // indirect
github.com/multiversx/mx-chain-p2p-go v1.0.18 // indirect
github.com/multiversx/mx-chain-vm-common-go v1.3.42 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ github.com/multiversx/mx-chain-crypto-go v1.2.6 h1:yxsjAQGh62los+iYmORMfh3w9qen0
github.com/multiversx/mx-chain-crypto-go v1.2.6/go.mod h1:rOj0Rr19HTOYt9YTeym7RKxlHt91NXln3LVKjHKVmA0=
github.com/multiversx/mx-chain-es-indexer-go v1.3.20 h1:/82eDBsjlEm6dNgW44WQOBqKByRAWr3n6OWjYpi43vE=
github.com/multiversx/mx-chain-es-indexer-go v1.3.20/go.mod h1:B8ahv6M93qHallA9fN+yknQFzdrDhHDgVlCy/0PVcG0=
github.com/multiversx/mx-chain-go v1.5.12 h1:DIgq+Gwu8lzpaSmusX12F9N9m6P7XA4ZsoBbtF5qhVs=
github.com/multiversx/mx-chain-go v1.5.12/go.mod h1:wD4sqoF8rkX+8qTrUMHL/xWr24r2Em9nGtwlExW78A0=
github.com/multiversx/mx-chain-go v1.5.13 h1:30Eipz1R8Mqe/xjc3bKUYtOnrbhddtsi3pBkaiDrLb4=
github.com/multiversx/mx-chain-go v1.5.13/go.mod h1:c22Et9ftMVQk+wx65n+d8am8SFsOSgQD6WR47vpP0Tg=
github.com/multiversx/mx-chain-logger-go v1.0.11 h1:DFsHa+sc5fKwhDR50I8uBM99RTDTEW68ESyr5ALRDwE=
github.com/multiversx/mx-chain-logger-go v1.0.11/go.mod h1:1srDkP0DQucWQ+rYfaq0BX2qLnULsUdRPADpYUTM6dA=
github.com/multiversx/mx-chain-p2p-go v1.0.17 h1:ijAwm/J4TqcDJvbWpGW9cQDNzfeaN/KfJYEVaTvM5c0=
github.com/multiversx/mx-chain-p2p-go v1.0.17/go.mod h1:Z7zPjv7adFUembS0XgxfecvvJTJOaGy09jT5JGqFyf0=
github.com/multiversx/mx-chain-p2p-go v1.0.18 h1:Dc+ngk8AV1UdBeZMzZP2MkxDyfoNrD/vY77umvo3SqQ=
github.com/multiversx/mx-chain-p2p-go v1.0.18/go.mod h1:Xy1B8X+AaGafDNcI6QZsTxO6bBf4akEyq+WdGRWSCpI=
github.com/multiversx/mx-chain-proxy-go v1.1.39 h1:hrZQeio1qPU8rDapTVzi3E7cl7Br2KJbqUi39QRy0b0=
github.com/multiversx/mx-chain-proxy-go v1.1.39/go.mod h1:b1gukLqxvi976gWZ4bHLvIaaAsyuLHi2JxNRLBAkcR8=
github.com/multiversx/mx-chain-storage-go v1.0.7 h1:UqLo/OLTD3IHiE/TB/SEdNRV1GG2f1R6vIP5ehHwCNw=
Expand Down
5 changes: 5 additions & 0 deletions server/factory/components/observerFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ type ObserverFacade struct {
facade.TransactionProcessor
facade.BlockProcessor
}

// ComputeShardId computes the shard ID for a given public key
func (facade *ObserverFacade) ComputeShardId(pubKey []byte) uint32 {
return facade.GetShardCoordinator().ComputeId(pubKey)
}
1 change: 1 addition & 0 deletions server/factory/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type NetworkProvider interface {
GetAccountNativeBalance(address string, options resources.AccountQueryOptions) (*resources.AccountOnBlock, error)
GetAccountESDTBalance(address string, tokenIdentifier string, options resources.AccountQueryOptions) (*resources.AccountESDTBalance, error)
IsAddressObserved(address string) (bool, error)
ComputeShardIdOfPubKey(pubkey []byte) uint32
ConvertPubKeyToAddress(pubkey []byte) string
ConvertAddressToPubKey(address string) ([]byte, error)
SendTransaction(tx *data.Transaction) (string, error)
Expand Down
2 changes: 1 addition & 1 deletion server/provider/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

type observerFacade interface {
CallGetRestEndPoint(baseUrl string, path string, value interface{}) (int, error)
ComputeShardId(pubKey []byte) (uint32, error)
ComputeShardId(pubKey []byte) uint32
SendTransaction(tx *data.Transaction) (int, string, error)
ComputeTransactionHash(tx *data.Transaction) (string, error)
GetTransactionByHashAndSenderAddress(hash string, sender string, withEvents bool) (*transaction.ApiTransactionResult, int, error)
Expand Down
12 changes: 7 additions & 5 deletions server/provider/networkProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,7 @@ func (provider *networkProvider) IsAddressObserved(address string) (bool, error)
return false, err
}

shard, err := provider.observerFacade.ComputeShardId(pubKey)
if err != nil {
return false, err
}

shard := provider.observerFacade.ComputeShardId(pubKey)
isObservedActualShard := shard == provider.observedActualShard
isObservedProjectedShard := pubKey[len(pubKey)-1] == byte(provider.observedProjectedShard)

Expand All @@ -323,6 +319,12 @@ func (provider *networkProvider) IsAddressObserved(address string) (bool, error)
return isObservedActualShard, nil
}

// ComputeShardIdOfPubKey computes the shard ID of a public key
func (provider *networkProvider) ComputeShardIdOfPubKey(pubKey []byte) uint32 {
shard := provider.observerFacade.ComputeShardId(pubKey)
return shard
}

// ConvertPubKeyToAddress converts a public key to an address
func (provider *networkProvider) ConvertPubKeyToAddress(pubkey []byte) string {
return provider.pubKeyConverter.Encode(pubkey)
Expand Down
11 changes: 11 additions & 0 deletions server/provider/networkProvider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ func TestNetworkProvider_DoGetBlockByNonce(t *testing.T) {
})
}

func Test_ComputeShardIdOfPubKey(t *testing.T) {
args := createDefaultArgsNewNetworkProvider()
provider, err := NewNetworkProvider(args)
require.Nil(t, err)
require.NotNil(t, provider)

require.Equal(t, uint32(0), provider.ComputeShardIdOfPubKey(testscommon.TestPubKeyBob))
require.Equal(t, uint32(1), provider.ComputeShardIdOfPubKey(testscommon.TestPubKeyAlice))
require.Equal(t, uint32(2), provider.ComputeShardIdOfPubKey(testscommon.TestPubKeyCarol))
}

func Test_ComputeTransactionFeeForMoveBalance(t *testing.T) {
args := createDefaultArgsNewNetworkProvider()
provider, err := NewNetworkProvider(args)
Expand Down
8 changes: 8 additions & 0 deletions server/services/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ func isNonZeroAmount(amount string) bool {
return !isZeroAmount(amount)
}

func isZeroBigIntOrNil(value *big.Int) bool {
if value == nil {
return true
}

return value.Sign() == 0
}

func getMagnitudeOfAmount(amount string) string {
return strings.Trim(amount, "-")
}
Expand Down
7 changes: 7 additions & 0 deletions server/services/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ func Test_IsNonZeroAmount(t *testing.T) {
require.True(t, isNonZeroAmount("-1"))
}

func Test_IsZeroBigInt(t *testing.T) {
require.True(t, isZeroBigIntOrNil(big.NewInt(0)))
require.True(t, isZeroBigIntOrNil(nil))
require.False(t, isZeroBigIntOrNil(big.NewInt(42)))
require.False(t, isZeroBigIntOrNil(big.NewInt(-42)))
}

func Test_GetMagnitudeOfAmount(t *testing.T) {
require.Equal(t, "100", getMagnitudeOfAmount("100"))
require.Equal(t, "100", getMagnitudeOfAmount("-100"))
Expand Down
9 changes: 6 additions & 3 deletions server/services/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@ package services
import (
"encoding/hex"
"strings"

"github.com/multiversx/mx-chain-core-go/core"
)

var (
transactionVersion = 1
transactionProcessingTypeRelayed = "RelayedTx"
transactionProcessingTypeBuiltInFunctionCall = "BuiltInFunctionCall"
transactionProcessingTypeMoveBalance = "MoveBalance"
builtInFunctionClaimDeveloperRewards = "ClaimDeveloperRewards"
builtInFunctionClaimDeveloperRewards = core.BuiltInFunctionClaimDeveloperRewards
refundGasMessage = "refundedGas"
sendingValueToNonPayableContractDataPrefix = "@" + hex.EncodeToString([]byte("sending value to non payable contract"))
argumentsSeparator = "@"
sendingValueToNonPayableContractDataPrefix = argumentsSeparator + hex.EncodeToString([]byte("sending value to non payable contract"))
emptyHash = strings.Repeat("0", 64)
nodeVersionForOfflineRosetta = "N / A"
)

var (
transactionEventSignalError = "signalError"
transactionEventSignalError = core.SignalErrorOperation
transactionEventESDTTransfer = "ESDTTransfer"
transactionEventESDTNFTTransfer = "ESDTNFTTransfer"
transactionEventMultiESDTNFTTransfer = "MultiESDTNFTTransfer"
Expand Down
1 change: 1 addition & 0 deletions server/services/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,4 @@ func (factory *errFactory) getPrototypeByCode(code errCode) errPrototype {

var errEventNotFound = errors.New("transaction event not found")
var errCannotRecognizeEvent = errors.New("cannot recognize transaction event")
var errCannotParseRelayedV1 = errors.New("cannot parse relayed V1 transaction")
1 change: 1 addition & 0 deletions server/services/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type NetworkProvider interface {
GetAccountNativeBalance(address string, options resources.AccountQueryOptions) (*resources.AccountOnBlock, error)
GetAccountESDTBalance(address string, tokenIdentifier string, options resources.AccountQueryOptions) (*resources.AccountESDTBalance, error)
IsAddressObserved(address string) (bool, error)
ComputeShardIdOfPubKey(pubkey []byte) uint32
ConvertPubKeyToAddress(pubkey []byte) string
ConvertAddressToPubKey(address string) ([]byte, error)
SendTransaction(tx *data.Transaction) (string, error)
Expand Down
45 changes: 45 additions & 0 deletions server/services/relayedTransactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package services

import (
"encoding/hex"
"encoding/json"
"math/big"
"strings"

"github.com/multiversx/mx-chain-core-go/data/transaction"
)

// innerTransactionOfRelayedV1 is used to parse the inner transaction of a relayed V1 transaction, and holds only the fields handled by Rosetta.
type innerTransactionOfRelayedV1 struct {
Nonce uint64 `json:"nonce"`
Value big.Int `json:"value"`
ReceiverPubKey []byte `json:"receiver"`
SenderPubKey []byte `json:"sender"`
}

func isRelayedV1Transaction(tx *transaction.ApiTransactionResult) bool {
return (tx.Type == string(transaction.TxTypeNormal)) &&
(tx.ProcessingTypeOnSource == transactionProcessingTypeRelayed) &&
(tx.ProcessingTypeOnDestination == transactionProcessingTypeRelayed)
}

func parseInnerTxOfRelayedV1(tx *transaction.ApiTransactionResult) (*innerTransactionOfRelayedV1, error) {
subparts := strings.Split(string(tx.Data), argumentsSeparator)
if len(subparts) != 2 {
return nil, errCannotParseRelayedV1
}

innerTxPayloadDecoded, err := hex.DecodeString(subparts[1])
if err != nil {
return nil, err
}

var innerTx innerTransactionOfRelayedV1

err = json.Unmarshal(innerTxPayloadDecoded, &innerTx)
if err != nil {
return nil, err
}

return &innerTx, nil
}
50 changes: 50 additions & 0 deletions server/services/relayedTransactions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package services

import (
"testing"

"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/multiversx/mx-chain-rosetta/testscommon"
"github.com/stretchr/testify/require"
)

func Test_IsRelayedV1Transaction(t *testing.T) {
t.Run("arbitrary tx", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{}
require.False(t, isRelayedV1Transaction(tx))
})

t.Run("relayed v1 tx", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Type: string(transaction.TxTypeNormal),
ProcessingTypeOnSource: transactionProcessingTypeRelayed,
ProcessingTypeOnDestination: transactionProcessingTypeRelayed,
}

require.True(t, isRelayedV1Transaction(tx))
})
}

func Test_ParseInnerTxOfRelayedV1(t *testing.T) {
t.Run("arbitrary tx", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{}
innerTx, err := parseInnerTxOfRelayedV1(tx)
require.ErrorIs(t, err, errCannotParseRelayedV1)
require.Nil(t, innerTx)
})

t.Run("relayed v1 tx (Alice to Bob, 1 EGLD)", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Data: []byte("relayedTx@7b226e6f6e6365223a372c2273656e646572223a2241546c484c76396f686e63616d433877673970645168386b77704742356a6949496f3349484b594e6165453d222c227265636569766572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c2276616c7565223a313030303030303030303030303030303030302c226761735072696365223a313030303030303030302c226761734c696d6974223a35303030302c2264617461223a22222c227369676e6174757265223a222b4161696451714c4d6150314b4f414d42506a557554774955775137724f6d62586976446c6b4944775a315a48353053366377714a4163576a496a744f732f435177502b79597a6643356730637571526b55437842413d3d222c22636861696e4944223a224d513d3d222c2276657273696f6e223a327d"),
}

innerTx, err := parseInnerTxOfRelayedV1(tx)
require.NoError(t, err)
require.NotNil(t, innerTx)

require.Equal(t, uint64(7), innerTx.Nonce)
require.Equal(t, "1000000000000000000", innerTx.Value.String())
require.Equal(t, testscommon.TestPubKeyAlice, innerTx.SenderPubKey)
require.Equal(t, testscommon.TestPubKeyBob, innerTx.ReceiverPubKey)
})
}
15 changes: 15 additions & 0 deletions server/services/transactionEventsController.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ func newTransactionEventsController(provider NetworkProvider) *transactionEvents
}
}

func (controller *transactionEventsController) hasAnySignalError(tx *transaction.ApiTransactionResult) bool {
if !controller.hasEvents(tx) {
return false
}

for _, event := range tx.Logs.Events {
isSignalError := event.Identifier == transactionEventSignalError
if isSignalError {
return true
}
}

return false
}

func (controller *transactionEventsController) hasSignalErrorOfSendingValueToNonPayableContract(tx *transaction.ApiTransactionResult) bool {
if !controller.hasEvents(tx) {
return false
Expand Down
27 changes: 27 additions & 0 deletions server/services/transactionEventsController_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@ import (
"github.com/stretchr/testify/require"
)

func TestTransactionEventsController_HasAnySignalError(t *testing.T) {
networkProvider := testscommon.NewNetworkProviderMock()
controller := newTransactionEventsController(networkProvider)

t.Run("arbitrary tx", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{}
txMatches := controller.hasAnySignalError(tx)
require.False(t, txMatches)
})

t.Run("tx with event 'signalError'", func(t *testing.T) {
tx := &transaction.ApiTransactionResult{
Logs: &transaction.ApiLogs{

Events: []*transaction.Events{
{
Identifier: transactionEventSignalError,
},
},
},
}

txMatches := controller.hasAnySignalError(tx)
require.True(t, txMatches)
})
}

func TestTransactionEventsController_FindManyEventsByIdentifier(t *testing.T) {
networkProvider := testscommon.NewNetworkProviderMock()
controller := newTransactionEventsController(networkProvider)
Expand Down
5 changes: 1 addition & 4 deletions server/services/transactionFilters.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ func filterOutIntrashardRelayedTransactionAlreadyHeldInInvalidMiniblock(txs []*t
}

for _, tx := range txs {
isRelayedTransaction := (tx.Type == string(transaction.TxTypeNormal)) &&
(tx.ProcessingTypeOnSource == transactionProcessingTypeRelayed) &&
(tx.ProcessingTypeOnDestination == transactionProcessingTypeRelayed)

isRelayedTransaction := isRelayedV1Transaction(tx)
_, alreadyHeldInInvalidMiniblock := invalidTxs[tx.Hash]

if isRelayedTransaction && alreadyHeldInInvalidMiniblock {
Expand Down
25 changes: 21 additions & 4 deletions server/services/transactionsFeaturesDetector.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ import (
)

type transactionsFeaturesDetector struct {
networkProvider NetworkProvider
eventsController *transactionEventsController
}

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

// Example SCRs can be found here: https://api.multiversx.com/transactions?function=ClaimDeveloperRewards
func (extractor *transactionsFeaturesDetector) doesContractResultHoldRewardsOfClaimDeveloperRewards(
func (detector *transactionsFeaturesDetector) doesContractResultHoldRewardsOfClaimDeveloperRewards(
contractResult *transaction.ApiTransactionResult,
allTransactionsInBlock []*transaction.ApiTransactionResult,
) bool {
Expand All @@ -44,7 +46,7 @@ func (extractor *transactionsFeaturesDetector) doesContractResultHoldRewardsOfCl
// that only consume the "data movement" component of the gas:
// - "sending value to non-payable contract"
// - "meta transaction is invalid"
func (extractor *transactionsFeaturesDetector) isInvalidTransactionOfTypeMoveBalanceThatOnlyConsumesDataMovementGas(tx *transaction.ApiTransactionResult) bool {
func (detector *transactionsFeaturesDetector) isInvalidTransactionOfTypeMoveBalanceThatOnlyConsumesDataMovementGas(tx *transaction.ApiTransactionResult) bool {
isInvalid := tx.Type == string(transaction.TxTypeInvalid)
isMoveBalance := tx.ProcessingTypeOnSource == transactionProcessingTypeMoveBalance && tx.ProcessingTypeOnDestination == transactionProcessingTypeMoveBalance

Expand All @@ -53,7 +55,22 @@ func (extractor *transactionsFeaturesDetector) isInvalidTransactionOfTypeMoveBal
}

// TODO: Analyze whether we can simplify the conditions below, or possibly discard them completely / replace them with simpler ones.
withSendingValueToNonPayableContract := extractor.eventsController.hasSignalErrorOfSendingValueToNonPayableContract(tx)
withMetaTransactionIsInvalid := extractor.eventsController.hasSignalErrorOfMetaTransactionIsInvalid(tx)
withSendingValueToNonPayableContract := detector.eventsController.hasSignalErrorOfSendingValueToNonPayableContract(tx)
withMetaTransactionIsInvalid := detector.eventsController.hasSignalErrorOfMetaTransactionIsInvalid(tx)
return withSendingValueToNonPayableContract || withMetaTransactionIsInvalid
}

func (detector *transactionsFeaturesDetector) isRelayedTransactionCompletelyIntrashardWithSignalError(tx *transaction.ApiTransactionResult, innerTx *innerTransactionOfRelayedV1) bool {
innerTxSenderShard := detector.networkProvider.ComputeShardIdOfPubKey(innerTx.SenderPubKey)
innerTxReceiverShard := detector.networkProvider.ComputeShardIdOfPubKey(innerTx.ReceiverPubKey)

isCompletelyIntrashard := tx.SourceShard == tx.DestinationShard &&
innerTxSenderShard == innerTxReceiverShard &&
innerTxSenderShard == tx.SourceShard
if !isCompletelyIntrashard {
return false
}

isWithSignalError := detector.eventsController.hasAnySignalError(tx)
return isWithSignalError
}
Loading

0 comments on commit 3b26c0c

Please sign in to comment.