From 707530bb764213f70df2f4cc38c689ad98f9007c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 1 Mar 2024 12:14:57 +0200 Subject: [PATCH 1/7] Remember latest queried epoch. --- process/smartContract/scQueryService.go | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index b243a8db2b0..47d0348dab2 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -33,7 +33,6 @@ var logQueryService = logger.GetOrCreate("process/smartcontract.queryService") // MaxGasLimitPerQuery - each unit is the equivalent of 1 nanosecond processing time const MaxGasLimitPerQuery = 300_000_000_000 -const epochDifferenceToConsiderHistory = 2 // SCQueryService can execute Get functions over SC to fetch stored values type SCQueryService struct { @@ -53,6 +52,7 @@ type SCQueryService struct { marshaller marshal.Marshalizer hasher hashing.Hasher uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + latestQueriedEpoch core.OptionalUint32 } // ArgsNewSCQueryService defines the arguments needed for the sc query service @@ -103,6 +103,7 @@ func NewSCQueryService( marshaller: args.Marshaller, hasher: args.Hasher, uint64ByteSliceConverter: args.Uint64ByteSliceConverter, + latestQueriedEpoch: core.OptionalUint32{}, }, nil } @@ -255,14 +256,36 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da } accountsAdapter := service.blockChainHook.GetAccountsAdapter() - if blockHeader.GetEpoch()+epochDifferenceToConsiderHistory >= service.getCurrentEpoch() { + + if service.isLatestQueriedEpoch(blockHeader.GetEpoch()) { logQueryService.Trace("calling RecreateTrie, for recent history", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) - return accountsAdapter.RecreateTrie(blockRootHash) + + err := accountsAdapter.RecreateTrie(blockRootHash) + if err != nil { + return err + } + + service.rememberQueriedEpoch(blockHeader.GetEpoch()) } logQueryService.Trace("calling RecreateTrieFromEpoch, for older history", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) holder := holders.NewRootHashHolder(blockRootHash, core.OptionalUint32{Value: blockHeader.GetEpoch(), HasValue: true}) - return accountsAdapter.RecreateTrieFromEpoch(holder) + + err := accountsAdapter.RecreateTrieFromEpoch(holder) + if err != nil { + return err + } + + service.rememberQueriedEpoch(blockHeader.GetEpoch()) + return err +} + +func (service *SCQueryService) isLatestQueriedEpoch(epoch uint32) bool { + return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epoch +} + +func (service *SCQueryService) rememberQueriedEpoch(epoch uint32) { + service.latestQueriedEpoch = core.OptionalUint32{Value: epoch, HasValue: true} } func (service *SCQueryService) getCurrentEpoch() uint32 { From 0bbe9dfb2ebe25a4991036b166b7ead56eba2fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 1 Mar 2024 13:24:54 +0200 Subject: [PATCH 2/7] Fix after review. --- process/smartContract/scQueryService.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index 47d0348dab2..7e83f278272 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -266,6 +266,7 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da } service.rememberQueriedEpoch(blockHeader.GetEpoch()) + return nil } logQueryService.Trace("calling RecreateTrieFromEpoch, for older history", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) @@ -277,7 +278,7 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da } service.rememberQueriedEpoch(blockHeader.GetEpoch()) - return err + return nil } func (service *SCQueryService) isLatestQueriedEpoch(epoch uint32) bool { From e731ccb53f2d7a53b1eee5dfe8be5432ea40b007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 1 Mar 2024 14:34:42 +0200 Subject: [PATCH 3/7] Fix tests. --- process/smartContract/scQueryService_test.go | 219 +++++++++---------- 1 file changed, 99 insertions(+), 120 deletions(-) diff --git a/process/smartContract/scQueryService_test.go b/process/smartContract/scQueryService_test.go index cd31bc165ec..10d57414305 100644 --- a/process/smartContract/scQueryService_test.go +++ b/process/smartContract/scQueryService_test.go @@ -367,10 +367,11 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, _ = target.ExecuteQuery(&query) assert.True(t, runWasCalled) }) - t.Run("block hash should work - old epoch", func(t *testing.T) { + t.Run("block hash should work - when epoch is different from latest queried epoch", func(t *testing.T) { t.Parallel() runWasCalled := false + epoch := uint32(37) mockVM := &mock.VMExecutionHandlerStub{ RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { @@ -399,7 +400,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ GetCurrentBlockHeaderCalled: func() data.HeaderHandler { return &block.Header{ - Epoch: 37, + Epoch: epoch, } }, } @@ -429,13 +430,20 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return true }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { - return 12, nil + return epoch, nil }, } + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + RecreateTrieCalled: func(rootHash []byte) error { recreateTrieWasCalled = true + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true assert.Equal(t, providedRootHash, options.GetRootHash()) return nil }, @@ -447,6 +455,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { } target, _ := NewSCQueryService(argsNewSCQuery) + target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -461,12 +470,14 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, err := target.ExecuteQuery(&query) assert.True(t, runWasCalled) - assert.True(t, recreateTrieWasCalled) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) assert.Nil(t, err) }) - t.Run("block hash should work - current epoch", func(t *testing.T) { + t.Run("block hash should work - when epoch is same as latest queried epoch", func(t *testing.T) { t.Parallel() + epoch := uint32(12) runWasCalled := false mockVM := &mock.VMExecutionHandlerStub{ @@ -502,6 +513,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { hdr := &block.Header{ RootHash: providedRootHash, + Epoch: epoch, } buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) return buff, nil @@ -514,16 +526,23 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return true }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { - return 12, nil + return epoch, nil }, } + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ RecreateTrieCalled: func(rootHash []byte) error { recreateTrieWasCalled = true assert.Equal(t, providedRootHash, rootHash) return nil }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true + return nil + }, } argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ GetAccountsAdapterCalled: func() state.AccountsAdapter { @@ -532,6 +551,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { } target, _ := NewSCQueryService(argsNewSCQuery) + target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: epoch} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -547,12 +567,14 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, err := target.ExecuteQuery(&query) assert.True(t, runWasCalled) assert.True(t, recreateTrieWasCalled) + assert.False(t, recreateTrieFromEpochWasCalled) assert.Nil(t, err) }) - t.Run("block nonce should work - old epoch", func(t *testing.T) { + t.Run("block nonce should work - when epoch is different from latest queried epoch", func(t *testing.T) { t.Parallel() runWasCalled := false + epoch := uint32(37) mockVM := &mock.VMExecutionHandlerStub{ RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { @@ -571,7 +593,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ GetCurrentBlockHeaderCalled: func() data.HeaderHandler { return &block.Header{ - Epoch: 37, + Epoch: epoch, } }, } @@ -616,13 +638,20 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { require.Equal(t, providedHash, hash) - return 12, nil + return epoch, nil }, } + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + RecreateTrieCalled: func(rootHash []byte) error { recreateTrieWasCalled = true + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true assert.Equal(t, providedRootHash, options.GetRootHash()) return nil }, @@ -634,6 +663,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { } target, _ := NewSCQueryService(argsNewSCQuery) + target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -651,12 +681,14 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, _ = target.ExecuteQuery(&query) assert.True(t, runWasCalled) - assert.True(t, recreateTrieWasCalled) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) }) - t.Run("block nonce should work - current epoch", func(t *testing.T) { + t.Run("block nonce should work - when epoch is same as latest queried epoch", func(t *testing.T) { t.Parallel() runWasCalled := false + epoch := uint32(12) mockVM := &mock.VMExecutionHandlerStub{ RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { @@ -695,6 +727,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { hdr := &block.Header{ RootHash: providedRootHash, + Epoch: epoch, } buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) return buff, nil @@ -708,16 +741,23 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { }, GetEpochByHashCalled: func(hash []byte) (uint32, error) { require.Equal(t, providedHash, hash) - return 12, nil + return epoch, nil }, } + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + providedAccountsAdapter := &stateMocks.AccountsStub{ RecreateTrieCalled: func(rootHash []byte) error { recreateTrieWasCalled = true assert.Equal(t, providedRootHash, rootHash) return nil }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieFromEpochWasCalled = true + return nil + }, } argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ GetAccountsAdapterCalled: func() state.AccountsAdapter { @@ -726,6 +766,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { } target, _ := NewSCQueryService(argsNewSCQuery) + target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: epoch} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -744,6 +785,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, _ = target.ExecuteQuery(&query) assert.True(t, runWasCalled) assert.True(t, recreateTrieWasCalled) + assert.False(t, recreateTrieFromEpochWasCalled) }) } @@ -770,10 +812,12 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { err := service.recreateTrie(testRootHash, nil) assert.ErrorIs(t, err, process.ErrNilBlockHeader) }) - t.Run("should call RecreateTrie for genesis block", func(t *testing.T) { + t.Run("should call RecreateTrieFromEpoch, remember epoch, then call RecreateTrie (for genesis block, then blocks in other epochs)", func(t *testing.T) { t.Parallel() recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false + argsNewSCQuery := createMockArgumentsForSCQuery() argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ GetCurrentBlockHeaderCalled: func() data.HeaderHandler { @@ -785,36 +829,16 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { return &stateMocks.AccountsStub{ RecreateTrieCalled: func(rootHash []byte) error { recreateTrieWasCalled = true + recreateTrieFromEpochWasCalled = false + assert.Equal(t, testRootHash, rootHash) return nil }, - } - }, - } - - service, _ := NewSCQueryService(argsNewSCQuery) - err := service.recreateTrie(testRootHash, &block.Header{}) - assert.Nil(t, err) - assert.True(t, recreateTrieWasCalled) - }) - t.Run("should call RecreateTrie for block on epoch 0", func(t *testing.T) { - t.Parallel() + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieWasCalled = false + recreateTrieFromEpochWasCalled = true - recreateTrieWasCalled := false - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: 0, - } - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - assert.Equal(t, testRootHash, rootHash) + assert.Equal(t, testRootHash, options.GetRootHash()) return nil }, } @@ -822,102 +846,57 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { } service, _ := NewSCQueryService(argsNewSCQuery) + assert.Equal(t, core.OptionalUint32{HasValue: false}, service.latestQueriedEpoch) + + // For genesis block, RecreateTrieFromEpoch should be called err := service.recreateTrie(testRootHash, &block.Header{}) assert.Nil(t, err) - assert.True(t, recreateTrieWasCalled) - }) - t.Run("should call RecreateTrie for block on epoch 1", func(t *testing.T) { - t.Parallel() + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) - recreateTrieWasCalled := false - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: 1, - } - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - assert.Equal(t, testRootHash, rootHash) - return nil - }, - } - }, - } + // For genesis block, RecreateTrie should be called + err = service.recreateTrie(testRootHash, &block.Header{}) + assert.Nil(t, err) + assert.False(t, recreateTrieFromEpochWasCalled) + assert.True(t, recreateTrieWasCalled) + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) - service, _ := NewSCQueryService(argsNewSCQuery) - err := service.recreateTrie(testRootHash, &block.Header{ + // For block in epoch 0, RecreateTrie should be called + err = service.recreateTrie(testRootHash, &block.Header{ Epoch: 0, }) assert.Nil(t, err) + assert.False(t, recreateTrieFromEpochWasCalled) assert.True(t, recreateTrieWasCalled) - }) - t.Run("should call RecreateTrie for block on epoch 2", func(t *testing.T) { - t.Parallel() + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) - recreateTrieWasCalled := false - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: 3, - } - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - assert.Equal(t, testRootHash, rootHash) - return nil - }, - } - }, - } + // For block in epoch 1, RecreateTrieFromEpoch should be called + err = service.recreateTrie(testRootHash, &block.Header{ + Epoch: 1, + }) + assert.Nil(t, err) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 1}, service.latestQueriedEpoch) - service, _ := NewSCQueryService(argsNewSCQuery) - err := service.recreateTrie(testRootHash, &block.Header{ - Epoch: 2, + // For block in epoch 1, RecreateTrie should be called + err = service.recreateTrie(testRootHash, &block.Header{ + Epoch: 1, }) assert.Nil(t, err) + assert.False(t, recreateTrieFromEpochWasCalled) assert.True(t, recreateTrieWasCalled) - }) - t.Run("should call RecreateTrieFromEpoch for block on epoch 3", func(t *testing.T) { - t.Parallel() - - recreateTrieWasCalled := false - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: 3, - } - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return &stateMocks.AccountsStub{ - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { - recreateTrieWasCalled = true - assert.Equal(t, testRootHash, options.GetRootHash()) - return nil - }, - } - }, - } + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 1}, service.latestQueriedEpoch) - service, _ := NewSCQueryService(argsNewSCQuery) - err := service.recreateTrie(testRootHash, &block.Header{ + // For block in epoch 0, RecreateTrieFromEpoch should be called + err = service.recreateTrie(testRootHash, &block.Header{ Epoch: 0, }) assert.Nil(t, err) - assert.True(t, recreateTrieWasCalled) + assert.True(t, recreateTrieFromEpochWasCalled) + assert.False(t, recreateTrieWasCalled) + assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) }) } From 211beab90854f6902df1af403c0fb1a15fee3fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 1 Mar 2024 15:19:20 +0200 Subject: [PATCH 4/7] Fix condition for RecreateTrie. --- process/smartContract/scQueryService.go | 18 +++++-- process/smartContract/scQueryService_test.go | 50 +++++++++++++++++++- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index 7e83f278272..8b65e1a203f 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -257,8 +257,8 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da accountsAdapter := service.blockChainHook.GetAccountsAdapter() - if service.isLatestQueriedEpoch(blockHeader.GetEpoch()) { - logQueryService.Trace("calling RecreateTrie, for recent history", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + if service.shouldCallRecreateTrieWithoutEpoch(blockHeader.GetEpoch()) { + logQueryService.Trace("calling RecreateTrie", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) err := accountsAdapter.RecreateTrie(blockRootHash) if err != nil { @@ -269,7 +269,7 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da return nil } - logQueryService.Trace("calling RecreateTrieFromEpoch, for older history", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + logQueryService.Trace("calling RecreateTrieFromEpoch", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) holder := holders.NewRootHashHolder(blockRootHash, core.OptionalUint32{Value: blockHeader.GetEpoch(), HasValue: true}) err := accountsAdapter.RecreateTrieFromEpoch(holder) @@ -281,8 +281,16 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da return nil } -func (service *SCQueryService) isLatestQueriedEpoch(epoch uint32) bool { - return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epoch +func (service *SCQueryService) shouldCallRecreateTrieWithoutEpoch(epochInQuestion uint32) bool { + if service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epochInQuestion { + return true + } + + if !service.latestQueriedEpoch.HasValue && epochInQuestion == service.getCurrentEpoch() { + return true + } + + return false } func (service *SCQueryService) rememberQueriedEpoch(epoch uint32) { diff --git a/process/smartContract/scQueryService_test.go b/process/smartContract/scQueryService_test.go index 10d57414305..a411afaa97b 100644 --- a/process/smartContract/scQueryService_test.go +++ b/process/smartContract/scQueryService_test.go @@ -789,6 +789,54 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { }) } +func TestSCQueryService_ShouldCallRecreateTrieWithoutEpoch(t *testing.T) { + t.Parallel() + + currentEpoch := uint32(0) + + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.Header{ + Epoch: currentEpoch, + } + }, + } + + service, err := NewSCQueryService(argsNewSCQuery) + assert.Nil(t, err) + assert.NotNil(t, service) + + currentEpoch = 0 + + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) + + currentEpoch = 37 + + service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 29} + + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) + + service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 37} + + assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(37)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) + + currentEpoch = 42 + + assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(37)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(42)) + + service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 42} + + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) + assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) + assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(42)) +} + func TestSCQueryService_RecreateTrie(t *testing.T) { t.Parallel() @@ -846,7 +894,7 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { } service, _ := NewSCQueryService(argsNewSCQuery) - assert.Equal(t, core.OptionalUint32{HasValue: false}, service.latestQueriedEpoch) + service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 37} // For genesis block, RecreateTrieFromEpoch should be called err := service.recreateTrie(testRootHash, &block.Header{}) From 952ccc8d43f99c40c9674a9f6391f6e6486fdfeb Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 1 Mar 2024 15:22:38 +0200 Subject: [PATCH 5/7] - handled vm queries in snapshotless mode --- factory/api/apiResolverFactory.go | 1 + process/smartContract/scQueryService.go | 8 ++ process/smartContract/scQueryService_test.go | 113 +++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index 99b99f80c81..6053e4212ad 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -461,6 +461,7 @@ func createScQueryElement( Marshaller: args.coreComponents.InternalMarshalizer(), Hasher: args.coreComponents.Hasher(), Uint64ByteSliceConverter: args.coreComponents.Uint64ByteSliceConverter(), + IsInSnapshottingMode: args.generalConfig.StateTriesConfig.SnapshotsEnabled, } return smartContract.NewSCQueryService(argsNewSCQueryService) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index 7e83f278272..75fe928a48a 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -53,6 +53,7 @@ type SCQueryService struct { hasher hashing.Hasher uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter latestQueriedEpoch core.OptionalUint32 + isInSnapshottingMode bool } // ArgsNewSCQueryService defines the arguments needed for the sc query service @@ -72,6 +73,7 @@ type ArgsNewSCQueryService struct { Marshaller marshal.Marshalizer Hasher hashing.Hasher Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + IsInSnapshottingMode bool } // NewSCQueryService returns a new instance of SCQueryService @@ -104,6 +106,7 @@ func NewSCQueryService( hasher: args.Hasher, uint64ByteSliceConverter: args.Uint64ByteSliceConverter, latestQueriedEpoch: core.OptionalUint32{}, + isInSnapshottingMode: args.IsInSnapshottingMode, }, nil } @@ -282,6 +285,11 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da } func (service *SCQueryService) isLatestQueriedEpoch(epoch uint32) bool { + if !service.isInSnapshottingMode { + // for snapshotless operation, we need to force this method to return true so the RecreateTrie will be called instead of RecreateTrieFromEpoch + return true + } + return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epoch } diff --git a/process/smartContract/scQueryService_test.go b/process/smartContract/scQueryService_test.go index 10d57414305..7a0a3d032de 100644 --- a/process/smartContract/scQueryService_test.go +++ b/process/smartContract/scQueryService_test.go @@ -62,6 +62,7 @@ func createMockArgumentsForSCQuery() ArgsNewSCQueryService { Marshaller: &marshallerMock.MarshalizerStub{}, Hasher: &testscommon.HasherStub{}, Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + IsInSnapshottingMode: true, } } @@ -684,6 +685,118 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { assert.True(t, recreateTrieFromEpochWasCalled) assert.False(t, recreateTrieWasCalled) }) + t.Run("block nonce should work - when epoch is different from latest queried epoch - in snapshotless mode", func(t *testing.T) { + t.Parallel() + + runWasCalled := false + epoch := uint32(37) + + mockVM := &mock.VMExecutionHandlerStub{ + RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { + runWasCalled = true + assert.Equal(t, int64(42), big.NewInt(0).SetBytes(input.Arguments[0]).Int64()) + assert.Equal(t, int64(43), big.NewInt(0).SetBytes(input.Arguments[1]).Int64()) + assert.Equal(t, scAddress, input.CallerAddr) + assert.Equal(t, funcName, input.Function) + + return &vmcommon.VMOutput{ + ReturnCode: vmcommon.Ok, + }, nil + }, + } + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.Header{ + Epoch: epoch, + } + }, + } + argsNewSCQuery.VmContainer = &mock.VMContainerMock{ + GetCalled: func(key []byte) (handler vmcommon.VMExecutionHandler, e error) { + return mockVM, nil + }, + } + argsNewSCQuery.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ + MaxGasLimitPerBlockCalled: func(_ uint32) uint64 { + return uint64(math.MaxUint64) + }, + } + providedHash := []byte("provided hash") + providedRootHash := []byte("provided root hash") + providedNonce := uint64(123) + argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} + counter := 0 + argsNewSCQuery.StorageService = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + return providedHash, nil + }, + GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { + counter++ + if counter > 2 { + return nil, fmt.Errorf("no scheduled") + } + hdr := &block.Header{ + RootHash: providedRootHash, + } + buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) + return buff, nil + }, + }, nil + }, + } + argsNewSCQuery.HistoryRepository = &dblookupext.HistoryRepositoryStub{ + IsEnabledCalled: func() bool { + return true + }, + GetEpochByHashCalled: func(hash []byte) (uint32, error) { + require.Equal(t, providedHash, hash) + return epoch, nil + }, + } + + recreateTrieWasCalled := false + + providedAccountsAdapter := &stateMocks.AccountsStub{ + RecreateTrieCalled: func(rootHash []byte) error { + recreateTrieWasCalled = true + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + assert.Fail(t, "should have not called RecreateTrieFromEpoch") + return nil + }, + } + argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ + GetAccountsAdapterCalled: func() state.AccountsAdapter { + return providedAccountsAdapter + }, + } + argsNewSCQuery.IsInSnapshottingMode = false + + target, _ := NewSCQueryService(argsNewSCQuery) + target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} + + dataArgs := make([][]byte, len(args)) + for i, arg := range args { + dataArgs[i] = append(dataArgs[i], arg.Bytes()...) + } + query := process.SCQuery{ + ScAddress: scAddress, + FuncName: funcName, + Arguments: dataArgs, + BlockNonce: core.OptionalUint64{ + Value: providedNonce, + HasValue: true, + }, + } + + _, _, _ = target.ExecuteQuery(&query) + assert.True(t, runWasCalled) + assert.True(t, recreateTrieWasCalled) + }) t.Run("block nonce should work - when epoch is same as latest queried epoch", func(t *testing.T) { t.Parallel() From d44648edfe5cc94c0a54a83a5b09a015086a0c46 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 1 Mar 2024 16:02:40 +0200 Subject: [PATCH 6/7] - fix after merge --- process/smartContract/scQueryService.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index 88dac8059b7..d594fd39b9a 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -290,7 +290,7 @@ func (service *SCQueryService) shouldCallRecreateTrieWithoutEpoch(epochInQuestio return true } - return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epoch + return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epochInQuestion } func (service *SCQueryService) rememberQueriedEpoch(epoch uint32) { From d867f82b9f6373faaf5157274ca9cf536fcc2c93 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 1 Mar 2024 17:56:18 +0200 Subject: [PATCH 7/7] - refactored solution --- cmd/node/flags.go | 25 +- common/operationmodes/historicalBalances.go | 41 ++ .../operationmodes/historicalBalances_test.go | 141 ++++++ common/operationmodes/operationmodes.go | 1 + factory/api/apiResolverFactory.go | 161 +++--- process/smartContract/scQueryService.go | 138 ++--- process/smartContract/scQueryService_test.go | 470 ++---------------- 7 files changed, 368 insertions(+), 609 deletions(-) create mode 100644 common/operationmodes/historicalBalances.go create mode 100644 common/operationmodes/historicalBalances_test.go diff --git a/cmd/node/flags.go b/cmd/node/flags.go index f40de41ef86..3f55c187060 100644 --- a/cmd/node/flags.go +++ b/cmd/node/flags.go @@ -632,7 +632,8 @@ func applyCompatibleConfigs(log logger.Logger, configs *config.Configs) error { isInHistoricalBalancesMode := operationmodes.SliceContainsElement(operationModes, operationmodes.OperationModeHistoricalBalances) if isInHistoricalBalancesMode { - processHistoricalBalancesMode(log, configs) + // TODO move all operation modes settings in the common/operationmodes package and add tests + operationmodes.ProcessHistoricalBalancesMode(log, configs) } isInDbLookupExtensionMode := operationmodes.SliceContainsElement(operationModes, operationmodes.OperationModeDbLookupExtension) @@ -648,28 +649,6 @@ func applyCompatibleConfigs(log logger.Logger, configs *config.Configs) error { return nil } -func processHistoricalBalancesMode(log logger.Logger, configs *config.Configs) { - configs.GeneralConfig.StoragePruning.Enabled = true - configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = false - configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = false - configs.GeneralConfig.GeneralSettings.StartInEpochEnabled = false - configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = false - configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = false - configs.GeneralConfig.DbLookupExtensions.Enabled = true - configs.PreferencesConfig.Preferences.FullArchive = true - - log.Warn("the node is in historical balances mode! Will auto-set some config values", - "StoragePruning.Enabled", configs.GeneralConfig.StoragePruning.Enabled, - "StoragePruning.ValidatorCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData, - "StoragePruning.ObserverCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData, - "StoragePruning.AccountsTrieCleanOldEpochsData", configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData, - "GeneralSettings.StartInEpochEnabled", configs.GeneralConfig.GeneralSettings.StartInEpochEnabled, - "StateTriesConfig.AccountsStatePruningEnabled", configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled, - "DbLookupExtensions.Enabled", configs.GeneralConfig.DbLookupExtensions.Enabled, - "Preferences.FullArchive", configs.PreferencesConfig.Preferences.FullArchive, - ) -} - func processDbLookupExtensionMode(log logger.Logger, configs *config.Configs) { configs.GeneralConfig.DbLookupExtensions.Enabled = true configs.GeneralConfig.StoragePruning.Enabled = true diff --git a/common/operationmodes/historicalBalances.go b/common/operationmodes/historicalBalances.go new file mode 100644 index 00000000000..da3cfe98dde --- /dev/null +++ b/common/operationmodes/historicalBalances.go @@ -0,0 +1,41 @@ +package operationmodes + +import ( + "github.com/multiversx/mx-chain-go/config" + logger "github.com/multiversx/mx-chain-logger-go" +) + +// ProcessHistoricalBalancesMode will process the provided flags for the historical balances +func ProcessHistoricalBalancesMode(log logger.Logger, configs *config.Configs) { + configs.GeneralConfig.StoragePruning.Enabled = true + configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = false + configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = false + configs.GeneralConfig.GeneralSettings.StartInEpochEnabled = false + configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = false + configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = false + configs.GeneralConfig.DbLookupExtensions.Enabled = true + configs.PreferencesConfig.Preferences.FullArchive = true + + log.Warn("the node is in historical balances mode! Will auto-set some config values", + "StoragePruning.Enabled", configs.GeneralConfig.StoragePruning.Enabled, + "StoragePruning.ValidatorCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData, + "StoragePruning.ObserverCleanOldEpochsData", configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData, + "StoragePruning.AccountsTrieCleanOldEpochsData", configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData, + "GeneralSettings.StartInEpochEnabled", configs.GeneralConfig.GeneralSettings.StartInEpochEnabled, + "StateTriesConfig.AccountsStatePruningEnabled", configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled, + "DbLookupExtensions.Enabled", configs.GeneralConfig.DbLookupExtensions.Enabled, + "Preferences.FullArchive", configs.PreferencesConfig.Preferences.FullArchive, + ) +} + +// IsInHistoricalBalancesMode returns true if the configuration provided denotes a historical balances mode +func IsInHistoricalBalancesMode(configs *config.Configs) bool { + return configs.GeneralConfig.StoragePruning.Enabled && + !configs.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData && + !configs.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData && + !configs.GeneralConfig.GeneralSettings.StartInEpochEnabled && + !configs.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData && + !configs.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled && + configs.GeneralConfig.DbLookupExtensions.Enabled && + configs.PreferencesConfig.Preferences.FullArchive +} diff --git a/common/operationmodes/historicalBalances_test.go b/common/operationmodes/historicalBalances_test.go new file mode 100644 index 00000000000..d06061c3027 --- /dev/null +++ b/common/operationmodes/historicalBalances_test.go @@ -0,0 +1,141 @@ +package operationmodes + +import ( + "testing" + + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/assert" +) + +func TestProcessHistoricalBalancesMode(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + + assert.True(t, cfg.GeneralConfig.StoragePruning.Enabled) + assert.False(t, cfg.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.GeneralSettings.StartInEpochEnabled) + assert.False(t, cfg.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData) + assert.False(t, cfg.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled) + assert.True(t, cfg.GeneralConfig.DbLookupExtensions.Enabled) + assert.True(t, cfg.PreferencesConfig.Preferences.FullArchive) +} + +func TestIsInHistoricalBalancesMode(t *testing.T) { + t.Parallel() + + t.Run("empty configs should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("storage pruning disabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.Enabled = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("validator clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.ValidatorCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("observer clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.ObserverCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("start in epoch enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.GeneralSettings.StartInEpochEnabled = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("accounts trie clean old epoch data enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StoragePruning.AccountsTrieCleanOldEpochsData = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("accounts state pruning enabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.StateTriesConfig.AccountsStatePruningEnabled = true + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("db lookup extension disabled should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.GeneralConfig.DbLookupExtensions.Enabled = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("not a full archive node should return false", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + cfg.PreferencesConfig.Preferences.FullArchive = false + assert.False(t, IsInHistoricalBalancesMode(cfg)) + }) + t.Run("with historical balances config should return true", func(t *testing.T) { + t.Parallel() + + cfg := &config.Configs{ + GeneralConfig: &config.Config{}, + PreferencesConfig: &config.Preferences{}, + } + ProcessHistoricalBalancesMode(&testscommon.LoggerStub{}, cfg) + assert.True(t, IsInHistoricalBalancesMode(cfg)) + }) + +} diff --git a/common/operationmodes/operationmodes.go b/common/operationmodes/operationmodes.go index 70aed256f4b..1ae6a6fad70 100644 --- a/common/operationmodes/operationmodes.go +++ b/common/operationmodes/operationmodes.go @@ -5,6 +5,7 @@ import ( "strings" ) +// constants that define the operation mode of the node const ( OperationModeFullArchive = "full-archive" OperationModeDbLookupExtension = "db-lookup-extension" diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index 6053e4212ad..dc015bad188 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -11,6 +11,7 @@ import ( "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/disabled" + "github.com/multiversx/mx-chain-go/common/operationmodes" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/blockchain" @@ -71,40 +72,42 @@ type ApiResolverArgs struct { } type scQueryServiceArgs struct { - generalConfig *config.Config - epochConfig *config.EpochConfig - coreComponents factory.CoreComponentsHolder - stateComponents factory.StateComponentsHolder - dataComponents factory.DataComponentsHolder - processComponents factory.ProcessComponentsHolder - statusCoreComponents factory.StatusCoreComponentsHolder - gasScheduleNotifier core.GasScheduleNotifier - messageSigVerifier vm.MessageSignVerifier - systemSCConfig *config.SystemSmartContractsConfig - bootstrapper process.Bootstrapper - guardedAccountHandler process.GuardedAccountHandler - allowVMQueriesChan chan struct{} - workingDir string - processingMode common.NodeProcessingMode + generalConfig *config.Config + epochConfig *config.EpochConfig + coreComponents factory.CoreComponentsHolder + stateComponents factory.StateComponentsHolder + dataComponents factory.DataComponentsHolder + processComponents factory.ProcessComponentsHolder + statusCoreComponents factory.StatusCoreComponentsHolder + gasScheduleNotifier core.GasScheduleNotifier + messageSigVerifier vm.MessageSignVerifier + systemSCConfig *config.SystemSmartContractsConfig + bootstrapper process.Bootstrapper + guardedAccountHandler process.GuardedAccountHandler + allowVMQueriesChan chan struct{} + workingDir string + processingMode common.NodeProcessingMode + isInHistoricalBalancesMode bool } type scQueryElementArgs struct { - generalConfig *config.Config - epochConfig *config.EpochConfig - coreComponents factory.CoreComponentsHolder - stateComponents factory.StateComponentsHolder - dataComponents factory.DataComponentsHolder - processComponents factory.ProcessComponentsHolder - statusCoreComponents factory.StatusCoreComponentsHolder - gasScheduleNotifier core.GasScheduleNotifier - messageSigVerifier vm.MessageSignVerifier - systemSCConfig *config.SystemSmartContractsConfig - bootstrapper process.Bootstrapper - guardedAccountHandler process.GuardedAccountHandler - allowVMQueriesChan chan struct{} - workingDir string - index int - processingMode common.NodeProcessingMode + generalConfig *config.Config + epochConfig *config.EpochConfig + coreComponents factory.CoreComponentsHolder + stateComponents factory.StateComponentsHolder + dataComponents factory.DataComponentsHolder + processComponents factory.ProcessComponentsHolder + statusCoreComponents factory.StatusCoreComponentsHolder + gasScheduleNotifier core.GasScheduleNotifier + messageSigVerifier vm.MessageSignVerifier + systemSCConfig *config.SystemSmartContractsConfig + bootstrapper process.Bootstrapper + guardedAccountHandler process.GuardedAccountHandler + allowVMQueriesChan chan struct{} + workingDir string + index int + processingMode common.NodeProcessingMode + isInHistoricalBalancesMode bool } // CreateApiResolver is able to create an ApiResolver instance that will solve the REST API requests through the node facade @@ -112,21 +115,22 @@ type scQueryElementArgs struct { func CreateApiResolver(args *ApiResolverArgs) (facade.ApiResolver, error) { apiWorkingDir := filepath.Join(args.Configs.FlagsConfig.WorkingDir, common.TemporaryPath) argsSCQuery := &scQueryServiceArgs{ - generalConfig: args.Configs.GeneralConfig, - epochConfig: args.Configs.EpochConfig, - coreComponents: args.CoreComponents, - dataComponents: args.DataComponents, - stateComponents: args.StateComponents, - processComponents: args.ProcessComponents, - statusCoreComponents: args.StatusCoreComponents, - gasScheduleNotifier: args.GasScheduleNotifier, - messageSigVerifier: args.CryptoComponents.MessageSignVerifier(), - systemSCConfig: args.Configs.SystemSCConfig, - bootstrapper: args.Bootstrapper, - guardedAccountHandler: args.BootstrapComponents.GuardedAccountHandler(), - allowVMQueriesChan: args.AllowVMQueriesChan, - workingDir: apiWorkingDir, - processingMode: args.ProcessingMode, + generalConfig: args.Configs.GeneralConfig, + epochConfig: args.Configs.EpochConfig, + coreComponents: args.CoreComponents, + dataComponents: args.DataComponents, + stateComponents: args.StateComponents, + processComponents: args.ProcessComponents, + statusCoreComponents: args.StatusCoreComponents, + gasScheduleNotifier: args.GasScheduleNotifier, + messageSigVerifier: args.CryptoComponents.MessageSignVerifier(), + systemSCConfig: args.Configs.SystemSCConfig, + bootstrapper: args.Bootstrapper, + guardedAccountHandler: args.BootstrapComponents.GuardedAccountHandler(), + allowVMQueriesChan: args.AllowVMQueriesChan, + workingDir: apiWorkingDir, + processingMode: args.ProcessingMode, + isInHistoricalBalancesMode: operationmodes.IsInHistoricalBalancesMode(args.Configs), } scQueryService, err := createScQueryService(argsSCQuery) @@ -299,22 +303,23 @@ func createScQueryService( } argsQueryElem := &scQueryElementArgs{ - generalConfig: args.generalConfig, - epochConfig: args.epochConfig, - coreComponents: args.coreComponents, - stateComponents: args.stateComponents, - dataComponents: args.dataComponents, - processComponents: args.processComponents, - statusCoreComponents: args.statusCoreComponents, - gasScheduleNotifier: args.gasScheduleNotifier, - messageSigVerifier: args.messageSigVerifier, - systemSCConfig: args.systemSCConfig, - bootstrapper: args.bootstrapper, - guardedAccountHandler: args.guardedAccountHandler, - allowVMQueriesChan: args.allowVMQueriesChan, - workingDir: args.workingDir, - index: 0, - processingMode: args.processingMode, + generalConfig: args.generalConfig, + epochConfig: args.epochConfig, + coreComponents: args.coreComponents, + stateComponents: args.stateComponents, + dataComponents: args.dataComponents, + processComponents: args.processComponents, + statusCoreComponents: args.statusCoreComponents, + gasScheduleNotifier: args.gasScheduleNotifier, + messageSigVerifier: args.messageSigVerifier, + systemSCConfig: args.systemSCConfig, + bootstrapper: args.bootstrapper, + guardedAccountHandler: args.guardedAccountHandler, + allowVMQueriesChan: args.allowVMQueriesChan, + workingDir: args.workingDir, + index: 0, + processingMode: args.processingMode, + isInHistoricalBalancesMode: args.isInHistoricalBalancesMode, } var err error @@ -446,22 +451,22 @@ func createScQueryElement( } argsNewSCQueryService := smartContract.ArgsNewSCQueryService{ - VmContainer: vmContainer, - EconomicsFee: args.coreComponents.EconomicsData(), - BlockChainHook: vmFactory.BlockChainHookImpl(), - MainBlockChain: args.dataComponents.Blockchain(), - APIBlockChain: apiBlockchain, - WasmVMChangeLocker: args.coreComponents.WasmVMChangeLocker(), - Bootstrapper: args.bootstrapper, - AllowExternalQueriesChan: args.allowVMQueriesChan, - MaxGasLimitPerQuery: maxGasForVmQueries, - HistoryRepository: args.processComponents.HistoryRepository(), - ShardCoordinator: args.processComponents.ShardCoordinator(), - StorageService: args.dataComponents.StorageService(), - Marshaller: args.coreComponents.InternalMarshalizer(), - Hasher: args.coreComponents.Hasher(), - Uint64ByteSliceConverter: args.coreComponents.Uint64ByteSliceConverter(), - IsInSnapshottingMode: args.generalConfig.StateTriesConfig.SnapshotsEnabled, + VmContainer: vmContainer, + EconomicsFee: args.coreComponents.EconomicsData(), + BlockChainHook: vmFactory.BlockChainHookImpl(), + MainBlockChain: args.dataComponents.Blockchain(), + APIBlockChain: apiBlockchain, + WasmVMChangeLocker: args.coreComponents.WasmVMChangeLocker(), + Bootstrapper: args.bootstrapper, + AllowExternalQueriesChan: args.allowVMQueriesChan, + MaxGasLimitPerQuery: maxGasForVmQueries, + HistoryRepository: args.processComponents.HistoryRepository(), + ShardCoordinator: args.processComponents.ShardCoordinator(), + StorageService: args.dataComponents.StorageService(), + Marshaller: args.coreComponents.InternalMarshalizer(), + Hasher: args.coreComponents.Hasher(), + Uint64ByteSliceConverter: args.coreComponents.Uint64ByteSliceConverter(), + IsInHistoricalBalancesMode: args.isInHistoricalBalancesMode, } return smartContract.NewSCQueryService(argsNewSCQueryService) diff --git a/process/smartContract/scQueryService.go b/process/smartContract/scQueryService.go index d594fd39b9a..10a5be173da 100644 --- a/process/smartContract/scQueryService.go +++ b/process/smartContract/scQueryService.go @@ -36,44 +36,43 @@ const MaxGasLimitPerQuery = 300_000_000_000 // SCQueryService can execute Get functions over SC to fetch stored values type SCQueryService struct { - vmContainer process.VirtualMachinesContainer - economicsFee process.FeeHandler - mutRunSc sync.Mutex - blockChainHook process.BlockChainHookWithAccountsAdapter - mainBlockChain data.ChainHandler - apiBlockChain data.ChainHandler - gasForQuery uint64 - wasmVMChangeLocker common.Locker - bootstrapper process.Bootstrapper - allowExternalQueriesChan chan struct{} - historyRepository dblookupext.HistoryRepository - shardCoordinator sharding.Coordinator - storageService dataRetriever.StorageService - marshaller marshal.Marshalizer - hasher hashing.Hasher - uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter - latestQueriedEpoch core.OptionalUint32 - isInSnapshottingMode bool + vmContainer process.VirtualMachinesContainer + economicsFee process.FeeHandler + mutRunSc sync.Mutex + blockChainHook process.BlockChainHookWithAccountsAdapter + mainBlockChain data.ChainHandler + apiBlockChain data.ChainHandler + gasForQuery uint64 + wasmVMChangeLocker common.Locker + bootstrapper process.Bootstrapper + allowExternalQueriesChan chan struct{} + historyRepository dblookupext.HistoryRepository + shardCoordinator sharding.Coordinator + storageService dataRetriever.StorageService + marshaller marshal.Marshalizer + hasher hashing.Hasher + uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + isInHistoricalBalancesMode bool } // ArgsNewSCQueryService defines the arguments needed for the sc query service type ArgsNewSCQueryService struct { - VmContainer process.VirtualMachinesContainer - EconomicsFee process.FeeHandler - BlockChainHook process.BlockChainHookWithAccountsAdapter - MainBlockChain data.ChainHandler - APIBlockChain data.ChainHandler - WasmVMChangeLocker common.Locker - Bootstrapper process.Bootstrapper - AllowExternalQueriesChan chan struct{} - MaxGasLimitPerQuery uint64 - HistoryRepository dblookupext.HistoryRepository - ShardCoordinator sharding.Coordinator - StorageService dataRetriever.StorageService - Marshaller marshal.Marshalizer - Hasher hashing.Hasher - Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter - IsInSnapshottingMode bool + VmContainer process.VirtualMachinesContainer + EconomicsFee process.FeeHandler + BlockChainHook process.BlockChainHookWithAccountsAdapter + MainBlockChain data.ChainHandler + APIBlockChain data.ChainHandler + WasmVMChangeLocker common.Locker + Bootstrapper process.Bootstrapper + AllowExternalQueriesChan chan struct{} + MaxGasLimitPerQuery uint64 + HistoryRepository dblookupext.HistoryRepository + ShardCoordinator sharding.Coordinator + StorageService dataRetriever.StorageService + Marshaller marshal.Marshalizer + Hasher hashing.Hasher + Uint64ByteSliceConverter typeConverters.Uint64ByteSliceConverter + IsInHistoricalBalancesMode bool } // NewSCQueryService returns a new instance of SCQueryService @@ -90,23 +89,22 @@ func NewSCQueryService( gasForQuery = args.MaxGasLimitPerQuery } return &SCQueryService{ - vmContainer: args.VmContainer, - economicsFee: args.EconomicsFee, - mainBlockChain: args.MainBlockChain, - apiBlockChain: args.APIBlockChain, - blockChainHook: args.BlockChainHook, - wasmVMChangeLocker: args.WasmVMChangeLocker, - bootstrapper: args.Bootstrapper, - gasForQuery: gasForQuery, - allowExternalQueriesChan: args.AllowExternalQueriesChan, - historyRepository: args.HistoryRepository, - shardCoordinator: args.ShardCoordinator, - storageService: args.StorageService, - marshaller: args.Marshaller, - hasher: args.Hasher, - uint64ByteSliceConverter: args.Uint64ByteSliceConverter, - latestQueriedEpoch: core.OptionalUint32{}, - isInSnapshottingMode: args.IsInSnapshottingMode, + vmContainer: args.VmContainer, + economicsFee: args.EconomicsFee, + mainBlockChain: args.MainBlockChain, + apiBlockChain: args.APIBlockChain, + blockChainHook: args.BlockChainHook, + wasmVMChangeLocker: args.WasmVMChangeLocker, + bootstrapper: args.Bootstrapper, + gasForQuery: gasForQuery, + allowExternalQueriesChan: args.AllowExternalQueriesChan, + historyRepository: args.HistoryRepository, + shardCoordinator: args.ShardCoordinator, + storageService: args.StorageService, + marshaller: args.Marshaller, + hasher: args.Hasher, + uint64ByteSliceConverter: args.Uint64ByteSliceConverter, + isInHistoricalBalancesMode: args.IsInHistoricalBalancesMode, }, nil } @@ -260,41 +258,15 @@ func (service *SCQueryService) recreateTrie(blockRootHash []byte, blockHeader da accountsAdapter := service.blockChainHook.GetAccountsAdapter() - if service.shouldCallRecreateTrieWithoutEpoch(blockHeader.GetEpoch()) { - logQueryService.Trace("calling RecreateTrie", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + if service.isInHistoricalBalancesMode { + logQueryService.Trace("calling RecreateTrieFromEpoch", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + holder := holders.NewRootHashHolder(blockRootHash, core.OptionalUint32{Value: blockHeader.GetEpoch(), HasValue: true}) - err := accountsAdapter.RecreateTrie(blockRootHash) - if err != nil { - return err - } - - service.rememberQueriedEpoch(blockHeader.GetEpoch()) - return nil - } - - logQueryService.Trace("calling RecreateTrieFromEpoch", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) - holder := holders.NewRootHashHolder(blockRootHash, core.OptionalUint32{Value: blockHeader.GetEpoch(), HasValue: true}) - - err := accountsAdapter.RecreateTrieFromEpoch(holder) - if err != nil { - return err + return accountsAdapter.RecreateTrieFromEpoch(holder) } - service.rememberQueriedEpoch(blockHeader.GetEpoch()) - return nil -} - -func (service *SCQueryService) shouldCallRecreateTrieWithoutEpoch(epochInQuestion uint32) bool { - if !service.isInSnapshottingMode { - // for snapshotless operation, we need to force this method to return true so the RecreateTrie will be called instead of RecreateTrieFromEpoch - return true - } - - return service.latestQueriedEpoch.HasValue && service.latestQueriedEpoch.Value == epochInQuestion -} - -func (service *SCQueryService) rememberQueriedEpoch(epoch uint32) { - service.latestQueriedEpoch = core.OptionalUint32{Value: epoch, HasValue: true} + logQueryService.Trace("calling RecreateTrie", "block", blockHeader.GetNonce(), "rootHash", blockRootHash) + return accountsAdapter.RecreateTrie(blockRootHash) } func (service *SCQueryService) getCurrentEpoch() uint32 { diff --git a/process/smartContract/scQueryService_test.go b/process/smartContract/scQueryService_test.go index 2cf6f35d075..d71542a8aaa 100644 --- a/process/smartContract/scQueryService_test.go +++ b/process/smartContract/scQueryService_test.go @@ -11,7 +11,6 @@ import ( "testing" "time" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" @@ -59,10 +58,10 @@ func createMockArgumentsForSCQuery() ArgsNewSCQueryService { return &storageStubs.StorerStub{}, nil }, }, - Marshaller: &marshallerMock.MarshalizerStub{}, - Hasher: &testscommon.HasherStub{}, - Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, - IsInSnapshottingMode: true, + Marshaller: &marshallerMock.MarshalizerStub{}, + Hasher: &testscommon.HasherStub{}, + Uint64ByteSliceConverter: &mock.Uint64ByteSliceConverterMock{}, + IsInHistoricalBalancesMode: false, } } @@ -368,7 +367,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { _, _, _ = target.ExecuteQuery(&query) assert.True(t, runWasCalled) }) - t.Run("block hash should work - when epoch is different from latest queried epoch", func(t *testing.T) { + t.Run("block hash should work - in deep history mode", func(t *testing.T) { t.Parallel() runWasCalled := false @@ -454,9 +453,9 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return providedAccountsAdapter }, } + argsNewSCQuery.IsInHistoricalBalancesMode = true target, _ := NewSCQueryService(argsNewSCQuery) - target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -475,7 +474,7 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { assert.False(t, recreateTrieWasCalled) assert.Nil(t, err) }) - t.Run("block hash should work - when epoch is same as latest queried epoch", func(t *testing.T) { + t.Run("block hash should work - in normal mode", func(t *testing.T) { t.Parallel() epoch := uint32(12) @@ -550,9 +549,9 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { return providedAccountsAdapter }, } + argsNewSCQuery.IsInHistoricalBalancesMode = false target, _ := NewSCQueryService(argsNewSCQuery) - target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: epoch} dataArgs := make([][]byte, len(args)) for i, arg := range args { @@ -571,383 +570,6 @@ func TestExecuteQuery_ShouldReceiveQueryCorrectly(t *testing.T) { assert.False(t, recreateTrieFromEpochWasCalled) assert.Nil(t, err) }) - t.Run("block nonce should work - when epoch is different from latest queried epoch", func(t *testing.T) { - t.Parallel() - - runWasCalled := false - epoch := uint32(37) - - mockVM := &mock.VMExecutionHandlerStub{ - RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { - runWasCalled = true - assert.Equal(t, int64(42), big.NewInt(0).SetBytes(input.Arguments[0]).Int64()) - assert.Equal(t, int64(43), big.NewInt(0).SetBytes(input.Arguments[1]).Int64()) - assert.Equal(t, scAddress, input.CallerAddr) - assert.Equal(t, funcName, input.Function) - - return &vmcommon.VMOutput{ - ReturnCode: vmcommon.Ok, - }, nil - }, - } - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: epoch, - } - }, - } - argsNewSCQuery.VmContainer = &mock.VMContainerMock{ - GetCalled: func(key []byte) (handler vmcommon.VMExecutionHandler, e error) { - return mockVM, nil - }, - } - argsNewSCQuery.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ - MaxGasLimitPerBlockCalled: func(_ uint32) uint64 { - return uint64(math.MaxUint64) - }, - } - providedHash := []byte("provided hash") - providedRootHash := []byte("provided root hash") - providedNonce := uint64(123) - argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} - counter := 0 - argsNewSCQuery.StorageService = &storageStubs.ChainStorerStub{ - GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { - return &storageStubs.StorerStub{ - GetCalled: func(key []byte) ([]byte, error) { - return providedHash, nil - }, - GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { - counter++ - if counter > 2 { - return nil, fmt.Errorf("no scheduled") - } - hdr := &block.Header{ - RootHash: providedRootHash, - } - buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) - return buff, nil - }, - }, nil - }, - } - argsNewSCQuery.HistoryRepository = &dblookupext.HistoryRepositoryStub{ - IsEnabledCalled: func() bool { - return true - }, - GetEpochByHashCalled: func(hash []byte) (uint32, error) { - require.Equal(t, providedHash, hash) - return epoch, nil - }, - } - - recreateTrieWasCalled := false - recreateTrieFromEpochWasCalled := false - - providedAccountsAdapter := &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - return nil - }, - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { - recreateTrieFromEpochWasCalled = true - assert.Equal(t, providedRootHash, options.GetRootHash()) - return nil - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return providedAccountsAdapter - }, - } - - target, _ := NewSCQueryService(argsNewSCQuery) - target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} - - dataArgs := make([][]byte, len(args)) - for i, arg := range args { - dataArgs[i] = append(dataArgs[i], arg.Bytes()...) - } - query := process.SCQuery{ - ScAddress: scAddress, - FuncName: funcName, - Arguments: dataArgs, - BlockNonce: core.OptionalUint64{ - Value: providedNonce, - HasValue: true, - }, - } - - _, _, _ = target.ExecuteQuery(&query) - assert.True(t, runWasCalled) - assert.True(t, recreateTrieFromEpochWasCalled) - assert.False(t, recreateTrieWasCalled) - }) - t.Run("block nonce should work - when epoch is different from latest queried epoch - in snapshotless mode", func(t *testing.T) { - t.Parallel() - - runWasCalled := false - epoch := uint32(37) - - mockVM := &mock.VMExecutionHandlerStub{ - RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { - runWasCalled = true - assert.Equal(t, int64(42), big.NewInt(0).SetBytes(input.Arguments[0]).Int64()) - assert.Equal(t, int64(43), big.NewInt(0).SetBytes(input.Arguments[1]).Int64()) - assert.Equal(t, scAddress, input.CallerAddr) - assert.Equal(t, funcName, input.Function) - - return &vmcommon.VMOutput{ - ReturnCode: vmcommon.Ok, - }, nil - }, - } - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: epoch, - } - }, - } - argsNewSCQuery.VmContainer = &mock.VMContainerMock{ - GetCalled: func(key []byte) (handler vmcommon.VMExecutionHandler, e error) { - return mockVM, nil - }, - } - argsNewSCQuery.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ - MaxGasLimitPerBlockCalled: func(_ uint32) uint64 { - return uint64(math.MaxUint64) - }, - } - providedHash := []byte("provided hash") - providedRootHash := []byte("provided root hash") - providedNonce := uint64(123) - argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} - counter := 0 - argsNewSCQuery.StorageService = &storageStubs.ChainStorerStub{ - GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { - return &storageStubs.StorerStub{ - GetCalled: func(key []byte) ([]byte, error) { - return providedHash, nil - }, - GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { - counter++ - if counter > 2 { - return nil, fmt.Errorf("no scheduled") - } - hdr := &block.Header{ - RootHash: providedRootHash, - } - buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) - return buff, nil - }, - }, nil - }, - } - argsNewSCQuery.HistoryRepository = &dblookupext.HistoryRepositoryStub{ - IsEnabledCalled: func() bool { - return true - }, - GetEpochByHashCalled: func(hash []byte) (uint32, error) { - require.Equal(t, providedHash, hash) - return epoch, nil - }, - } - - recreateTrieWasCalled := false - - providedAccountsAdapter := &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - return nil - }, - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { - assert.Fail(t, "should have not called RecreateTrieFromEpoch") - return nil - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return providedAccountsAdapter - }, - } - argsNewSCQuery.IsInSnapshottingMode = false - - target, _ := NewSCQueryService(argsNewSCQuery) - target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 5} - - dataArgs := make([][]byte, len(args)) - for i, arg := range args { - dataArgs[i] = append(dataArgs[i], arg.Bytes()...) - } - query := process.SCQuery{ - ScAddress: scAddress, - FuncName: funcName, - Arguments: dataArgs, - BlockNonce: core.OptionalUint64{ - Value: providedNonce, - HasValue: true, - }, - } - - _, _, _ = target.ExecuteQuery(&query) - assert.True(t, runWasCalled) - assert.True(t, recreateTrieWasCalled) - }) - t.Run("block nonce should work - when epoch is same as latest queried epoch", func(t *testing.T) { - t.Parallel() - - runWasCalled := false - epoch := uint32(12) - - mockVM := &mock.VMExecutionHandlerStub{ - RunSmartContractCallCalled: func(input *vmcommon.ContractCallInput) (output *vmcommon.VMOutput, e error) { - runWasCalled = true - assert.Equal(t, int64(42), big.NewInt(0).SetBytes(input.Arguments[0]).Int64()) - assert.Equal(t, int64(43), big.NewInt(0).SetBytes(input.Arguments[1]).Int64()) - assert.Equal(t, scAddress, input.CallerAddr) - assert.Equal(t, funcName, input.Function) - - return &vmcommon.VMOutput{ - ReturnCode: vmcommon.Ok, - }, nil - }, - } - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.VmContainer = &mock.VMContainerMock{ - GetCalled: func(key []byte) (handler vmcommon.VMExecutionHandler, e error) { - return mockVM, nil - }, - } - argsNewSCQuery.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ - MaxGasLimitPerBlockCalled: func(_ uint32) uint64 { - return uint64(math.MaxUint64) - }, - } - providedHash := []byte("provided hash") - providedRootHash := []byte("provided root hash") - providedNonce := uint64(123) - argsNewSCQuery.Marshaller = &marshallerMock.MarshalizerMock{} - argsNewSCQuery.StorageService = &storageStubs.ChainStorerStub{ - GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { - return &storageStubs.StorerStub{ - GetCalled: func(key []byte) ([]byte, error) { - return providedHash, nil - }, - GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { - hdr := &block.Header{ - RootHash: providedRootHash, - Epoch: epoch, - } - buff, _ := argsNewSCQuery.Marshaller.Marshal(hdr) - return buff, nil - }, - }, nil - }, - } - argsNewSCQuery.HistoryRepository = &dblookupext.HistoryRepositoryStub{ - IsEnabledCalled: func() bool { - return true - }, - GetEpochByHashCalled: func(hash []byte) (uint32, error) { - require.Equal(t, providedHash, hash) - return epoch, nil - }, - } - - recreateTrieWasCalled := false - recreateTrieFromEpochWasCalled := false - - providedAccountsAdapter := &stateMocks.AccountsStub{ - RecreateTrieCalled: func(rootHash []byte) error { - recreateTrieWasCalled = true - assert.Equal(t, providedRootHash, rootHash) - return nil - }, - RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { - recreateTrieFromEpochWasCalled = true - return nil - }, - } - argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ - GetAccountsAdapterCalled: func() state.AccountsAdapter { - return providedAccountsAdapter - }, - } - - target, _ := NewSCQueryService(argsNewSCQuery) - target.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: epoch} - - dataArgs := make([][]byte, len(args)) - for i, arg := range args { - dataArgs[i] = append(dataArgs[i], arg.Bytes()...) - } - query := process.SCQuery{ - ScAddress: scAddress, - FuncName: funcName, - Arguments: dataArgs, - BlockNonce: core.OptionalUint64{ - Value: providedNonce, - HasValue: true, - }, - } - - _, _, _ = target.ExecuteQuery(&query) - assert.True(t, runWasCalled) - assert.True(t, recreateTrieWasCalled) - assert.False(t, recreateTrieFromEpochWasCalled) - }) -} - -func TestSCQueryService_ShouldCallRecreateTrieWithoutEpoch(t *testing.T) { - t.Parallel() - - currentEpoch := uint32(0) - - argsNewSCQuery := createMockArgumentsForSCQuery() - argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ - GetCurrentBlockHeaderCalled: func() data.HeaderHandler { - return &block.Header{ - Epoch: currentEpoch, - } - }, - } - - service, err := NewSCQueryService(argsNewSCQuery) - assert.Nil(t, err) - assert.NotNil(t, service) - - currentEpoch = 0 - - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) - - currentEpoch = 37 - - service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 29} - - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) - - service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 37} - - assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(37)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) - - currentEpoch = 42 - - assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(37)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(42)) - - service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 42} - - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(37)) - assert.False(t, service.shouldCallRecreateTrieWithoutEpoch(5)) - assert.True(t, service.shouldCallRecreateTrieWithoutEpoch(42)) } func TestSCQueryService_RecreateTrie(t *testing.T) { @@ -973,13 +595,14 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { err := service.recreateTrie(testRootHash, nil) assert.ErrorIs(t, err, process.ErrNilBlockHeader) }) - t.Run("should call RecreateTrieFromEpoch, remember epoch, then call RecreateTrie (for genesis block, then blocks in other epochs)", func(t *testing.T) { + t.Run("should call RecreateTrieFromEpoch if in deep history mode", func(t *testing.T) { t.Parallel() recreateTrieWasCalled := false recreateTrieFromEpochWasCalled := false argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.IsInHistoricalBalancesMode = true argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ GetCurrentBlockHeaderCalled: func() data.HeaderHandler { return nil // after the genesis we do not have a header as current block @@ -1007,57 +630,54 @@ func TestSCQueryService_RecreateTrie(t *testing.T) { } service, _ := NewSCQueryService(argsNewSCQuery) - service.latestQueriedEpoch = core.OptionalUint32{HasValue: true, Value: 37} // For genesis block, RecreateTrieFromEpoch should be called err := service.recreateTrie(testRootHash, &block.Header{}) assert.Nil(t, err) assert.True(t, recreateTrieFromEpochWasCalled) assert.False(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) + }) + t.Run("should call RecreateTrie if not in deep history mode", func(t *testing.T) { + t.Parallel() - // For genesis block, RecreateTrie should be called - err = service.recreateTrie(testRootHash, &block.Header{}) - assert.Nil(t, err) - assert.False(t, recreateTrieFromEpochWasCalled) - assert.True(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) + recreateTrieWasCalled := false + recreateTrieFromEpochWasCalled := false - // For block in epoch 0, RecreateTrie should be called - err = service.recreateTrie(testRootHash, &block.Header{ - Epoch: 0, - }) - assert.Nil(t, err) - assert.False(t, recreateTrieFromEpochWasCalled) - assert.True(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) + argsNewSCQuery := createMockArgumentsForSCQuery() + argsNewSCQuery.IsInHistoricalBalancesMode = false + argsNewSCQuery.MainBlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return nil // after the genesis we do not have a header as current block + }, + } + argsNewSCQuery.BlockChainHook = &testscommon.BlockChainHookStub{ + GetAccountsAdapterCalled: func() state.AccountsAdapter { + return &stateMocks.AccountsStub{ + RecreateTrieCalled: func(rootHash []byte) error { + recreateTrieWasCalled = true + recreateTrieFromEpochWasCalled = false - // For block in epoch 1, RecreateTrieFromEpoch should be called - err = service.recreateTrie(testRootHash, &block.Header{ - Epoch: 1, - }) - assert.Nil(t, err) - assert.True(t, recreateTrieFromEpochWasCalled) - assert.False(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 1}, service.latestQueriedEpoch) + assert.Equal(t, testRootHash, rootHash) + return nil + }, + RecreateTrieFromEpochCalled: func(options common.RootHashHolder) error { + recreateTrieWasCalled = false + recreateTrieFromEpochWasCalled = true - // For block in epoch 1, RecreateTrie should be called - err = service.recreateTrie(testRootHash, &block.Header{ - Epoch: 1, - }) + assert.Equal(t, testRootHash, options.GetRootHash()) + return nil + }, + } + }, + } + + service, _ := NewSCQueryService(argsNewSCQuery) + + // For genesis block, RecreateTrieFromEpoch should be called + err := service.recreateTrie(testRootHash, &block.Header{}) assert.Nil(t, err) assert.False(t, recreateTrieFromEpochWasCalled) assert.True(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 1}, service.latestQueriedEpoch) - - // For block in epoch 0, RecreateTrieFromEpoch should be called - err = service.recreateTrie(testRootHash, &block.Header{ - Epoch: 0, - }) - assert.Nil(t, err) - assert.True(t, recreateTrieFromEpochWasCalled) - assert.False(t, recreateTrieWasCalled) - assert.Equal(t, core.OptionalUint32{HasValue: true, Value: 0}, service.latestQueriedEpoch) }) }