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
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ require (
github.com/libp2p/go-libp2p-pubsub v0.13.0
github.com/mitchellh/mapstructure v1.5.0
github.com/multiversx/mx-chain-communication-go v1.3.0
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260112082618-e36a88370557
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260130090903-7407465d70c6
github.com/multiversx/mx-chain-crypto-go v1.3.1-0.20260130102706-42f7b53faee5
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260112102658-97d6a0ceb5f6
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260130120024-d47cdda758fb
github.com/multiversx/mx-chain-logger-go v1.1.0
github.com/multiversx/mx-chain-scenario-go v1.6.0
github.com/multiversx/mx-chain-storage-go v1.1.0
Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -401,14 +401,12 @@ 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.2-0.20260112082618-e36a88370557 h1:OKl4jmdUPjR9qlK8e5g8BMA9vM0rtr8i87WnfSn7gVA=
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260112082618-e36a88370557/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g=
github.com/multiversx/mx-chain-crypto-go v1.3.1-0.20260128090315-a5e63d1878ce h1:zc1RTNiNE63JxMPmDAmSRpaaEsPTZEa3F6HK2T5ahgw=
github.com/multiversx/mx-chain-crypto-go v1.3.1-0.20260128090315-a5e63d1878ce/go.mod h1:nPIkxxzyTP8IquWKds+22Q2OJ9W7LtusC7cAosz7ojM=
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260130090903-7407465d70c6 h1:OpV/546No27cXiePXupD/GCPspka98AAHLUxJAy9CII=
github.com/multiversx/mx-chain-core-go v1.4.2-0.20260130090903-7407465d70c6/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g=
github.com/multiversx/mx-chain-crypto-go v1.3.1-0.20260130102706-42f7b53faee5 h1:gV/Zrbs8lhEUJGyDbEeC4oyR5utJqy9ksbAr5M2oTzI=
github.com/multiversx/mx-chain-crypto-go v1.3.1-0.20260130102706-42f7b53faee5/go.mod h1:nPIkxxzyTP8IquWKds+22Q2OJ9W7LtusC7cAosz7ojM=
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-es-indexer-go v1.9.3-0.20260130120024-d47cdda758fb h1:8RxpOza9cQYt1Cw+PX9nvief3vyBYVK+znAkz3YcyQ4=
github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20260130120024-d47cdda758fb/go.mod h1:PTa6npUWFrZCYC1+/pybWn8CGo8hg/LCRpHusFUuhlY=
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-scenario-go v1.6.0 h1:cwDFuS1pSc4YXnfiKKDTEb+QDY4fulPQaiRgIebnKxI=
Expand Down
48 changes: 46 additions & 2 deletions outport/process/outportDataProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (odp *outportDataProvider) PrepareOutportSaveBlockData(arg ArgPrepareOutpor
return nil, ErrNilBodyHandler
}

pool, err := odp.createPool(arg.RewardsTxs)
pool, err := odp.createPool(arg.RewardsTxs, arg.Header)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -279,6 +279,11 @@ func (odp *outportDataProvider) prepareExecutionResultsData(args ArgPrepareOutpo
return nil, err
}

err = odp.addInPoolUnexecutableTransactions(headerHash, pool)
if err != nil {
return nil, fmt.Errorf("addInPoolUnexecutableTransactions: %w", err)
}

err = odp.transactionsFeeProcessor.PutFeeAndGasUsed(pool, executionResult.GetHeaderEpoch())
if err != nil {
return nil, fmt.Errorf("transactionsFeeProcessor.PutFeeAndGasUsed %w", err)
Expand Down Expand Up @@ -313,6 +318,40 @@ func (odp *outportDataProvider) prepareExecutionResultsData(args ArgPrepareOutpo
return results, nil
}

func (odp *outportDataProvider) addInPoolUnexecutableTransactions(headerHash []byte, pool *outportcore.TransactionPool) error {
unexecutableTxHashes, err := common.GetCachedUnexecutableTxHashes(odp.dataPool.PostProcessTransactions(), headerHash)
if err != nil {
return err
}
if len(unexecutableTxHashes) == 0 {
return nil
}

cacheID := process.ShardCacherIdentifier(odp.shardID, odp.shardID)
txCache := odp.dataPool.Transactions().ShardDataStore(cacheID)
if check.IfNil(txCache) {
return nil
}

pool.UnexecutableTransactions = make(map[string]*transaction.Transaction)
for _, txHash := range unexecutableTxHashes {
txI, found := txCache.Get(txHash)
if !found {
log.Warn("addInPoolUnexecutableTransactions - cannot find unexecutable tx in tx cache", "txHash", txHash)
continue
}
tx, ok := txI.(*transaction.Transaction)
if !ok {
log.Warn("addInPoolUnexecutableTransactions - cannot cast object from cache to transaction", "txHash", txHash, "type", fmt.Sprintf("%T", txI))
continue
}

pool.UnexecutableTransactions[hex.EncodeToString(txHash)] = tx
}

return nil
}

func hasRewardsOnBody(body *block.Body) bool {
for _, mb := range body.MiniBlocks {
if mb.Type == block.RewardsBlock {
Expand Down Expand Up @@ -488,7 +527,12 @@ func findLeaderIndex(blsKeys []string, leaderBlsKey string) uint64 {
return 0
}

func (odp *outportDataProvider) createPool(rewardsTxs map[string]data.TransactionHandler) (*outportcore.TransactionPool, error) {
func (odp *outportDataProvider) createPool(rewardsTxs map[string]data.TransactionHandler, header data.HeaderHandler) (*outportcore.TransactionPool, error) {
if header.IsHeaderV3() {
// transactions will be indexed after execution
return &outportcore.TransactionPool{}, nil
}

Comment on lines +530 to +535
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new createPool behavior for header v3 (returning an empty TransactionPool when header.IsHeaderV3() is true) is not covered by tests; adding a test that exercises PrepareOutportSaveBlockData with a v3 header and asserts that the returned TransactionPool does not contain regular transactions would help ensure this path behaves as intended.

Copilot uses AI. Check for mistakes.
grouped := map[block.Type]map[string]data.TransactionHandler{
block.TxBlock: odp.txCoordinator.GetAllCurrentUsedTxs(block.TxBlock),
block.SmartContractResultBlock: odp.txCoordinator.GetAllCurrentUsedTxs(block.SmartContractResultBlock),
Expand Down
77 changes: 77 additions & 0 deletions outport/process/outportDataProvider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/outport/process/alteredaccounts/shared"
"github.com/multiversx/mx-chain-go/process"
"github.com/multiversx/mx-chain-go/storage"
"github.com/multiversx/mx-chain-go/testscommon/cache"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-chain-go/outport/mock"
Expand Down Expand Up @@ -792,6 +794,7 @@ func TestPrepareExecutionResultsData(t *testing.T) {
logsKey := common.PrepareLogEventsKey(headerHash)
logsSlice := make([]data.LogDataHandler, 0)
arg.DataPool.PostProcessTransactions().Put(logsKey, logsSlice, 0)
arg.DataPool.PostProcessTransactions().Put(common.PrepareUnexecutableTxHashesKey(headerHash), make([][]byte, 0), 0)

cachedTxs := make(map[block.Type]map[string]data.TransactionHandler)
cachedTxs[block.TxBlock] = make(map[string]data.TransactionHandler)
Expand Down Expand Up @@ -835,6 +838,7 @@ func TestPrepareExecutionResultsData(t *testing.T) {
cachedTxs := make(map[block.Type]map[string]data.TransactionHandler)
cachedTxs[block.TxBlock] = make(map[string]data.TransactionHandler)
arg.DataPool.PostProcessTransactions().Put(headerHash, cachedTxs, 1)
arg.DataPool.PostProcessTransactions().Put(common.PrepareUnexecutableTxHashesKey(headerHash), make([][]byte, 0), 0)

key := common.PrepareOrderedTxHashesKey(headerHash)
arg.DataPool.PostProcessTransactions().Put(key, [][]byte{[]byte("a")}, 1)
Expand Down Expand Up @@ -875,6 +879,7 @@ func TestPrepareExecutionResultsData(t *testing.T) {
logsKey := common.PrepareLogEventsKey(headerHash)
logsSlice := make([]data.LogDataHandler, 0)
arg.DataPool.PostProcessTransactions().Put(logsKey, logsSlice, 0)
arg.DataPool.PostProcessTransactions().Put(common.PrepareUnexecutableTxHashesKey(headerHash), make([][]byte, 0), 0)

cachedTxs := make(map[block.Type]map[string]data.TransactionHandler)
cachedTxs[block.TxBlock] = make(map[string]data.TransactionHandler)
Expand Down Expand Up @@ -1014,6 +1019,7 @@ func TestOutportDataProvider_GetRewards(t *testing.T) {

key := common.PrepareOrderedTxHashesKey(headerHash)
arg.DataPool.PostProcessTransactions().Put(key, [][]byte{[]byte("a")}, 1)
arg.DataPool.PostProcessTransactions().Put(common.PrepareUnexecutableTxHashesKey(headerHash), make([][]byte, 0), 0)

outportDataP, _ := NewOutportDataProvider(arg)

Expand Down Expand Up @@ -1041,3 +1047,74 @@ func TestOutportDataProvider_GetRewards(t *testing.T) {
require.Nil(t, err)
require.Len(t, results, 1)
}

func TestAddInPoolUnexecutableTransactions(t *testing.T) {
t.Parallel()

t.Run("cannot cache unexecutable tx hashes", func(t *testing.T) {
arg := createArgOutportDataProvider()
arg.DataPool = &dataRetriever.PoolsHolderStub{
PostProcessTransactionsCalled: func() storage.Cacher {
return &cache.CacherStub{
GetCalled: func(key []byte) (value interface{}, ok bool) {
return []byte("a"), false
},
}
},
}

outportDataP, _ := NewOutportDataProvider(arg)

headerHash := []byte("hash")

pool := &outportcore.TransactionPool{}
err := outportDataP.addInPoolUnexecutableTransactions(headerHash, pool)
require.ErrorIs(t, err, common.ErrMissingUnexecutableTxHash)
})

t.Run("no unexecutable txs should return nil", func(t *testing.T) {
arg := createArgOutportDataProvider()
arg.DataPool = &dataRetriever.PoolsHolderStub{
PostProcessTransactionsCalled: func() storage.Cacher {
return &cache.CacherStub{
GetCalled: func(key []byte) (value interface{}, ok bool) {
return make([][]byte, 0), true
},
}
},
}

outportDataP, _ := NewOutportDataProvider(arg)

headerHash := []byte("hash")

pool := &outportcore.TransactionPool{}
err := outportDataP.addInPoolUnexecutableTransactions(headerHash, pool)
require.NoError(t, err)
require.Nil(t, pool.UnexecutableTransactions)
})

t.Run("should work", func(t *testing.T) {
arg := createArgOutportDataProvider()
arg.DataPool = dataRetriever.NewPoolsHolderMock()

headerHash := []byte("hash")
txHash1, txHash2, txHash3 := []byte("a"), []byte("b"), []byte("c")
tx := &transaction.Transaction{Nonce: 1}

arg.DataPool.PostProcessTransactions().Put(common.PrepareUnexecutableTxHashesKey(headerHash), [][]byte{txHash1, txHash2, txHash3}, 1)

cacheID := process.ShardCacherIdentifier(0, 0)
arg.DataPool.Transactions().AddData(txHash2, &smartContractResult.SmartContractResult{}, 1, cacheID)
arg.DataPool.Transactions().AddData(txHash3, tx, 1, cacheID)

outportDataP, _ := NewOutportDataProvider(arg)

pool := &outportcore.TransactionPool{}
err := outportDataP.addInPoolUnexecutableTransactions(headerHash, pool)
require.NoError(t, err)
require.Equal(t, 1, len(pool.UnexecutableTransactions))
require.Equal(t, tx, pool.UnexecutableTransactions[hex.EncodeToString(txHash3)])
})

}
2 changes: 1 addition & 1 deletion process/block/baseProcess.go
Original file line number Diff line number Diff line change
Expand Up @@ -1257,8 +1257,8 @@ func (bp *baseProcessor) cleanupUnexecutableTxsFromPool(headerHash []byte) {
return
}

cacheID := process.ShardCacherIdentifier(bp.shardCoordinator.SelfId(), bp.shardCoordinator.SelfId())
for _, txHash := range unexecutableTxHashes {
cacheID := process.ShardCacherIdentifier(bp.shardCoordinator.SelfId(), bp.shardCoordinator.SelfId())
bp.dataPool.Transactions().RemoveData(txHash, cacheID)
}
}
Expand Down
Loading