Skip to content
Open
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
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ require (
github.com/coinbase/rosetta-sdk-go v0.8.3
github.com/coinbase/rosetta-sdk-go/types v1.0.0
github.com/gin-gonic/gin v1.10.0
github.com/multiversx/mx-chain-core-go v1.4.0
github.com/multiversx/mx-chain-go v1.10.1
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260219122727-014ae9f9311f
github.com/multiversx/mx-chain-go v1.11.1
github.com/multiversx/mx-chain-logger-go v1.1.0
github.com/multiversx/mx-chain-proxy-go v1.3.0
github.com/multiversx/mx-chain-proxy-go v1.4.0
github.com/multiversx/mx-chain-storage-go v1.1.0
github.com/stretchr/testify v1.10.0
github.com/urfave/cli v1.22.16
Expand Down Expand Up @@ -44,7 +44,7 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiversx/mx-chain-communication-go v1.3.0 // indirect
github.com/multiversx/mx-chain-crypto-go v1.3.0 // indirect
github.com/multiversx/mx-chain-es-indexer-go v1.9.1 // indirect
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260112102658-97d6a0ceb5f6 // indirect
github.com/multiversx/mx-chain-vm-common-go v1.6.0 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg=
github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
Expand Down Expand Up @@ -121,18 +121,18 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY
github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o=
github.com/multiversx/mx-chain-communication-go v1.3.0 h1:ziNM1dRuiR/7al2L/jGEA/a/hjurtJ/HEqgazHNt9P8=
github.com/multiversx/mx-chain-communication-go v1.3.0/go.mod h1:gDVWn6zUW6aCN1YOm/FbbT5MUmhgn/L1Rmpl8EoH3Yg=
github.com/multiversx/mx-chain-core-go v1.4.0 h1:p6FbfCzvMXF54kpS0B5mrjNWYpq4SEQqo0UvrMF7YVY=
github.com/multiversx/mx-chain-core-go v1.4.0/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g=
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260219122727-014ae9f9311f h1:kngckbX3TbZpU0LQpetUM8xNvUu/FtmZQtM66yoGcz0=
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260219122727-014ae9f9311f/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g=
github.com/multiversx/mx-chain-crypto-go v1.3.0 h1:0eK2bkDOMi8VbSPrB1/vGJSYT81IBtfL4zw+C4sWe/k=
github.com/multiversx/mx-chain-crypto-go v1.3.0/go.mod h1:nPIkxxzyTP8IquWKds+22Q2OJ9W7LtusC7cAosz7ojM=
github.com/multiversx/mx-chain-es-indexer-go v1.9.1 h1:Jg/4CLzIiwyrjuy+ZccEJ4TcvlHXnBUr5o3pclVitGo=
github.com/multiversx/mx-chain-es-indexer-go v1.9.1/go.mod h1:t1rkD2vHXSI4EClig0h7+kRCSUCRrMF+emr4DHxFtfA=
github.com/multiversx/mx-chain-go v1.10.1 h1:sRx7Ronn9SNB2oBMXJSl8kaJqm5iuluuGKNOec1h+XM=
github.com/multiversx/mx-chain-go v1.10.1/go.mod h1:DavxpLJtHRLLLKU1xfrU1bAhgZD5paptiGS5KfX4Lco=
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260112102658-97d6a0ceb5f6 h1:ywYAthnCkytgSGfMBTBvojlBJh9o5zUmottZwaVYTd8=
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260112102658-97d6a0ceb5f6/go.mod h1:F/BpaYVPuHN7POJN6gwvJfZ22diYtvz2576a+PWiPvw=
github.com/multiversx/mx-chain-go v1.11.1 h1:PC2cP10LEmfL18yMyfsMu0usTxa/ttXNQyi3BIVsuwM=
github.com/multiversx/mx-chain-go v1.11.1/go.mod h1:7c9Qvi3lvE0wuHP/xTxbjs1GvTO/7W4NGmzT9IDsJCM=
github.com/multiversx/mx-chain-logger-go v1.1.0 h1:97x84A6L4RfCa6YOx1HpAFxZp1cf/WI0Qh112whgZNM=
github.com/multiversx/mx-chain-logger-go v1.1.0/go.mod h1:K9XgiohLwOsNACETMNL0LItJMREuEvTH6NsoXWXWg7g=
github.com/multiversx/mx-chain-proxy-go v1.3.0 h1:sEfgO8qEKnUotCnX5EPTb+XC+VNiJcbzvZaY9sLrSXg=
github.com/multiversx/mx-chain-proxy-go v1.3.0/go.mod h1:hdCT241dL+3d4t5+g/OUeAfZ8H/Y72SbY7SNF7vp0us=
github.com/multiversx/mx-chain-proxy-go v1.4.0 h1:fwGWr3q2j3oMqYSfupVuAIPyuOgGSDP/aAA2Q8MU4T0=
github.com/multiversx/mx-chain-proxy-go v1.4.0/go.mod h1:kizqVThJggPV0cD29qeOCFhLwnrbuYQ7saGg+GzEOog=
github.com/multiversx/mx-chain-storage-go v1.1.0 h1:M1Y9DqMrJ62s7Zw31+cyuqsnPIvlG4jLBJl5WzeZLe8=
github.com/multiversx/mx-chain-storage-go v1.1.0/go.mod h1:o6Jm7cjfPmcc6XpyihYWrd6sx3sgqwurrunw3ZrfyxI=
github.com/multiversx/mx-chain-vm-common-go v1.6.0 h1:M2zmf/ptEINciWxYCPLIkwOMTvvzWjELYYB+0MMQ5Gw=
Expand Down
14 changes: 13 additions & 1 deletion server/factory/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package factory

import (
"time"

"github.com/multiversx/mx-chain-core-go/core/pubkeyConverter"
hasherFactory "github.com/multiversx/mx-chain-core-go/hashing/factory"
marshalFactory "github.com/multiversx/mx-chain-core-go/marshal/factory"
Expand All @@ -12,6 +14,7 @@ import (
"github.com/multiversx/mx-chain-rosetta/server/factory/components"
"github.com/multiversx/mx-chain-rosetta/server/provider"
"github.com/multiversx/mx-chain-rosetta/server/resources"
"github.com/multiversx/mx-chain-storage-go/timecache"
)

const (
Expand Down Expand Up @@ -119,7 +122,16 @@ func CreateNetworkProvider(args ArgsCreateNetworkProvider) (NetworkProvider, err
return nil, err
}

blockProcessor, err := process.NewBlockProcessor(baseProcessor)
cacheDuration := time.Duration(30) * time.Second
timedCache, err := timecache.NewTimeCacher(timecache.ArgTimeCacher{
DefaultSpan: cacheDuration,
CacheExpiry: cacheDuration,
})
if err != nil {
return nil, err
}

blockProcessor, err := process.NewBlockProcessor(baseProcessor, timedCache)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion server/provider/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (provider *networkProvider) GetAccount(address string) (*resources.AccountO
return data, nil
}

// GetAccountNativeBalance gets the native balance by address
// GetAccountBalance gets the native balance by address
func (provider *networkProvider) GetAccountBalance(address string, tokenIdentifier string, options resources.AccountQueryOptions) (*resources.AccountBalanceOnBlock, error) {
isNativeBalance := tokenIdentifier == provider.nativeCurrency.Symbol
if isNativeBalance {
Expand Down
4 changes: 3 additions & 1 deletion server/provider/networkProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ func (provider *networkProvider) getBlockSummaryByNonce(nonce uint64) (resources
Nonce: blockResponse.Data.Block.Nonce,
Hash: blockResponse.Data.Block.Hash,
PreviousBlockHash: blockResponse.Data.Block.PrevBlockHash,
Timestamp: int64(blockResponse.Data.Block.Timestamp),
Timestamp: blockResponse.Data.Block.Timestamp,
TimestampMs: blockResponse.Data.Block.TimestampMs,
}, nil
}

Expand Down Expand Up @@ -285,6 +286,7 @@ func createBlockCopy(block *api.Block) *api.Block {
StateRootHash: block.StateRootHash,
Status: block.Status,
Timestamp: block.Timestamp,
TimestampMs: block.TimestampMs,
MiniBlocks: miniblocksCopy,
}
}
Expand Down
13 changes: 9 additions & 4 deletions server/provider/nodeStatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (provider *networkProvider) GetNodeStatus() (*resources.AggregatedNodeStatu
return nil, err
}

latestNonce, err := getLatestNonceGivenHighestFinalNonce(plainNodeStatus.HighestFinalNonce)
latestNonce, err := getLatestNonceGivenHighestFinalNonceAndLastExecutedNonce(plainNodeStatus.HighestFinalNonce, plainNodeStatus.LastExecutedNonce)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -71,18 +71,23 @@ func (provider *networkProvider) getLatestBlockNonce() (uint64, error) {
}

// In the context of scheduled transactions, make sure the N+1 block is final, as well.
return getLatestNonceGivenHighestFinalNonce(nodeStatus.HighestFinalNonce)
return getLatestNonceGivenHighestFinalNonceAndLastExecutedNonce(nodeStatus.HighestFinalNonce, nodeStatus.LastExecutedNonce)
}

func getLatestNonceGivenHighestFinalNonce(highestFinalNonce uint64) (uint64, error) {
func getLatestNonceGivenHighestFinalNonceAndLastExecutedNonce(highestFinalNonce uint64, lastExecutedNonce uint64) (uint64, error) {
// Account for rollback-related edge cases while node is syncing (in conjunction with scheduled miniblocks).
const nonceDelta = 2

if highestFinalNonce <= nonceDelta {
return 0, errCannotGetLatestBlockNonce
}

return highestFinalNonce - nonceDelta, nil
nonceToReturn := highestFinalNonce - nonceDelta
if lastExecutedNonce > 0 && nonceToReturn > lastExecutedNonce {
return lastExecutedNonce, nil
}

return nonceToReturn, nil
}

func (provider *networkProvider) getOldestNonceWithHistoricalStateGivenNodeStatus(status *resources.NodeStatus) (uint64, error) {
Expand Down
76 changes: 74 additions & 2 deletions server/provider/nodeStatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestNetworkProvider_GetNodeStatusWithSuccess(t *testing.T) {
Hash: "00000998",
PrevBlockHash: "00000997",
Timestamp: 998,
TimestampMs: 998200,
},
},
}, nil
Expand All @@ -80,6 +81,7 @@ func TestNetworkProvider_GetNodeStatusWithSuccess(t *testing.T) {
Hash: "00000300",
PrevBlockHash: "00000299",
Timestamp: 300,
TimestampMs: 3002000,
},
},
}, nil
Expand All @@ -102,13 +104,15 @@ func TestNetworkProvider_GetNodeStatusWithSuccess(t *testing.T) {
Hash: "00000998",
PreviousBlockHash: "00000997",
Timestamp: 998,
TimestampMs: 998200,
}

expectedSummaryOfOldest := resources.BlockSummary{
Nonce: 300,
Hash: "00000300",
PreviousBlockHash: "00000299",
Timestamp: 300,
TimestampMs: 3002000,
}

nodeStatus, err := provider.GetNodeStatus()
Expand Down Expand Up @@ -164,8 +168,8 @@ func TestNetworkProvider_GetLatestBlockNonce(t *testing.T) {
args.FirstHistoricalEpoch = 2
args.NumHistoricalEpochs = 8

provider, err := NewNetworkProvider(args)
require.Nil(t, err)
provider, errC := NewNetworkProvider(args)
require.Nil(t, errC)
require.NotNil(t, provider)

t.Run("when HighestFinalNonce <= 2 (node didn't start syncing)", func(t *testing.T) {
Expand Down Expand Up @@ -207,6 +211,74 @@ func TestNetworkProvider_GetLatestBlockNonce(t *testing.T) {
return 0, errors.New("unexpected request")
}

nonce, err := provider.getLatestBlockNonce()
require.Nil(t, err)
require.Equal(t, uint64(40), nonce)
})
t.Run("when HighestFinalNonce is greater than LastExecutedNonce", func(t *testing.T) {
t.Parallel()

observerFacade.CallGetRestEndPointCalled = func(baseUrl, path string, value interface{}) (int, error) {
if path == "/node/status" {
value.(*resources.NodeStatusApiResponse).Data = resources.NodeStatusApiResponsePayload{
Status: resources.NodeStatus{
HighestFinalNonce: 42,
LastExecutedNonce: 39,
},
}

return 0, nil
}

return 0, errors.New("unexpected request")
}

nonce, err := provider.getLatestBlockNonce()
require.Nil(t, err)
require.Equal(t, uint64(39), nonce)
})

t.Run("when HighestFinalNonce is greater than LastExecutedNonce, but LastExecutedNonce is zero", func(t *testing.T) {
t.Parallel()

observerFacade.CallGetRestEndPointCalled = func(baseUrl, path string, value interface{}) (int, error) {
if path == "/node/status" {
value.(*resources.NodeStatusApiResponse).Data = resources.NodeStatusApiResponsePayload{
Status: resources.NodeStatus{
HighestFinalNonce: 42,
LastExecutedNonce: 0,
},
}

return 0, nil
}

return 0, errors.New("unexpected request")
}

nonce, err := provider.getLatestBlockNonce()
require.Nil(t, err)
require.Equal(t, uint64(40), nonce)
})

t.Run("when LastExecutedNonce is greater than or equal to HighestFinalNonce minus two", func(t *testing.T) {
t.Parallel()

observerFacade.CallGetRestEndPointCalled = func(baseUrl, path string, value interface{}) (int, error) {
if path == "/node/status" {
value.(*resources.NodeStatusApiResponse).Data = resources.NodeStatusApiResponsePayload{
Status: resources.NodeStatus{
HighestFinalNonce: 42,
LastExecutedNonce: 40,
},
}

return 0, nil
}

return 0, errors.New("unexpected request")
}

nonce, err := provider.getLatestBlockNonce()
require.Nil(t, err)
require.Equal(t, uint64(40), nonce)
Expand Down
1 change: 1 addition & 0 deletions server/resources/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type NodeStatus struct {
CurrentEpoch uint32 `json:"erd_epoch_number"`
HighestNonce uint64 `json:"erd_nonce"`
HighestFinalNonce uint64 `json:"erd_highest_final_nonce"`
LastExecutedNonce uint64 `json:"erd_last_executed_nonce"`
}

// EpochStartApiResponse is an API resource
Expand Down
1 change: 1 addition & 0 deletions server/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type BlockSummary struct {
Hash string
PreviousBlockHash string
Timestamp int64
TimestampMs int64
}

// Currency is an internal resource
Expand Down
2 changes: 1 addition & 1 deletion server/services/blockService.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (service *blockService) convertToRosettaBlock(block *api.Block) (*types.Blo
Block: &types.Block{
BlockIdentifier: blockToIdentifier(block),
ParentBlockIdentifier: parentBlockIdentifier,
Timestamp: timestampInMilliseconds(int64(block.Timestamp)),
Timestamp: getTimestampInMS(block.Timestamp, block.TimestampMs),
Transactions: transactions,
Metadata: objectsMap{
"shard": block.Shard,
Expand Down
7 changes: 7 additions & 0 deletions server/services/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ func timestampInMilliseconds(timestamp int64) int64 {
return timestamp * 1000
}

func getTimestampInMS(timestampSec int64, timestampMS int64) int64 {
if timestampMS > 0 {
return timestampMS
}
return timestampInMilliseconds(timestampSec)
}

func stringToHex(value string) string {
encoded := hex.EncodeToString([]byte(value))
encoded = ensureEvenLengthOfHexString(encoded)
Expand Down
7 changes: 7 additions & 0 deletions server/services/converters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,10 @@ func TestHexToAmount(t *testing.T) {
require.Nil(t, err)
require.Equal(t, "100", amount)
}

func TestGetTimestamp(t *testing.T) {

require.Equal(t, int64(0), getTimestampInMS(0, 0))
require.Equal(t, int64(500000), getTimestampInMS(500, 0))
require.Equal(t, int64(500200), getTimestampInMS(500, 500200))
}
2 changes: 1 addition & 1 deletion server/services/networkService.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (service *networkService) NetworkStatus(

networkStatusResponse := &types.NetworkStatusResponse{
CurrentBlockIdentifier: blockSummaryToIdentifier(&nodeStatus.LatestBlock),
CurrentBlockTimestamp: timestampInMilliseconds(nodeStatus.LatestBlock.Timestamp),
CurrentBlockTimestamp: getTimestampInMS(nodeStatus.LatestBlock.Timestamp, nodeStatus.LatestBlock.TimestampMs),
GenesisBlockIdentifier: service.extension.getGenesisBlockIdentifier(),
OldestBlockIdentifier: blockSummaryToIdentifier(&nodeStatus.OldestBlockWithHistoricalState),
SyncStatus: &types.SyncStatus{
Expand Down
Loading