From 339bec1632a66dd51008c38a49df176af7853917 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 11:47:13 +0300 Subject: [PATCH 1/9] core: adjust TestDumpAndRestore test `with state root` test should consider StateRootInHeader set to true. --- pkg/core/blockchain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 39710482f..0930fed2a 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -1578,7 +1578,7 @@ func TestDumpAndRestore(t *testing.T) { }) t.Run("with state root", func(t *testing.T) { testDumpAndRestore(t, func(c *config.Config) { - c.ProtocolConfiguration.StateRootInHeader = false + c.ProtocolConfiguration.StateRootInHeader = true }, nil) }) t.Run("remove untraceable", func(t *testing.T) { From 21515e1835d115c7e5ea99d68a352a58d531c40e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 12:14:17 +0300 Subject: [PATCH 2/9] core: refactor Level store creation for tests We don't need to create the whole DB configuration structure. --- pkg/core/helper_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 23ecede29..2b31e3391 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -69,13 +69,10 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c func newLevelDBForTesting(t testing.TB) storage.Store { ldbDir := t.TempDir() - dbConfig := storage.DBConfiguration{ - Type: "leveldb", - LevelDBOptions: storage.LevelDBOptions{ - DataDirectoryPath: ldbDir, - }, + dbOptions := storage.LevelDBOptions{ + DataDirectoryPath: ldbDir, } - newLevelStore, err := storage.NewLevelDBStore(dbConfig.LevelDBOptions) + newLevelStore, err := storage.NewLevelDBStore(dbOptions) require.Nil(t, err, "NewLevelDBStore error") return newLevelStore } From ad0c53c0670ac8aed150c48b487bff7b1af2052d Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 12:54:14 +0300 Subject: [PATCH 3/9] core: unify the format of error messages for (*Blockchain).init() --- pkg/core/blockchain.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1d51823f8..0546f1eb8 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -332,22 +332,22 @@ func (bc *Blockchain) init() error { return bc.storeBlock(genesisBlock, nil) } if ver.Value != version { - return fmt.Errorf("storage version mismatch betweeen %s and %s", version, ver.Value) + return fmt.Errorf("storage version mismatch (expected=%s, actual=%s)", version, ver.Value) } if ver.StateRootInHeader != bc.config.StateRootInHeader { return fmt.Errorf("StateRootInHeader setting mismatch (config=%t, db=%t)", ver.StateRootInHeader, bc.config.StateRootInHeader) } if ver.P2PSigExtensions != bc.config.P2PSigExtensions { - return fmt.Errorf("P2PSigExtensions setting mismatch (old=%t, new=%t", + return fmt.Errorf("P2PSigExtensions setting mismatch (old=%t, new=%t)", ver.P2PSigExtensions, bc.config.P2PSigExtensions) } if ver.P2PStateExchangeExtensions != bc.config.P2PStateExchangeExtensions { - return fmt.Errorf("P2PStateExchangeExtensions setting mismatch (old=%t, new=%t", + return fmt.Errorf("P2PStateExchangeExtensions setting mismatch (old=%t, new=%t)", ver.P2PStateExchangeExtensions, bc.config.P2PStateExchangeExtensions) } if ver.KeepOnlyLatestState != bc.config.KeepOnlyLatestState { - return fmt.Errorf("KeepOnlyLatestState setting mismatch: old=%v, new=%v", + return fmt.Errorf("KeepOnlyLatestState setting mismatch (old=%v, new=%v)", ver.KeepOnlyLatestState, bc.config.KeepOnlyLatestState) } bc.dao.Version = ver From 8d6aa1782de8e50bcb07e0d165114da9ba0022b4 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 13:08:02 +0300 Subject: [PATCH 4/9] core: fix StateRootInHeader error message for (*Blockchain).init() --- pkg/core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 0546f1eb8..88d4d7c3a 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -336,7 +336,7 @@ func (bc *Blockchain) init() error { } if ver.StateRootInHeader != bc.config.StateRootInHeader { return fmt.Errorf("StateRootInHeader setting mismatch (config=%t, db=%t)", - ver.StateRootInHeader, bc.config.StateRootInHeader) + bc.config.StateRootInHeader, ver.StateRootInHeader) } if ver.P2PSigExtensions != bc.config.P2PSigExtensions { return fmt.Errorf("P2PSigExtensions setting mismatch (old=%t, new=%t)", From 9adcefc2ef7e7de9ea9ef8e1eaa26dc93457eb7c Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 13:44:14 +0300 Subject: [PATCH 5/9] core: gracefully wrap Seek error if failed to retrieve header hases --- pkg/core/dao/dao.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index cae2622d6..04eb31cf4 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "errors" + "fmt" iocore "io" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -513,18 +514,20 @@ func (dao *Simple) GetStateSyncCurrentBlockHeight() (uint32, error) { func (dao *Simple) GetHeaderHashes() ([]util.Uint256, error) { var hashes = make([]util.Uint256, 0) + var seekErr error dao.Store.Seek(storage.SeekRange{ Prefix: dao.mkKeyPrefix(storage.IXHeaderHashList), }, func(k, v []byte) bool { newHashes, err := read2000Uint256Hashes(v) if err != nil { - panic(err) + seekErr = fmt.Errorf("failed to read batch of 2000 header hashes: %w", err) + return false } hashes = append(hashes, newHashes...) return true }) - return hashes, nil + return hashes, seekErr } // GetTransaction returns Transaction and its height by the given hash From a57c3f484280b0d43bb38781cf0d33a2cc486b62 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 16:25:48 +0300 Subject: [PATCH 6/9] core: fix InitWithIncompleteStateJump test --- pkg/core/blockchain_test.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 0930fed2a..7bc8a9560 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -1834,6 +1834,7 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) { c.ProtocolConfiguration.P2PStateExchangeExtensions = true c.ProtocolConfiguration.StateSyncInterval = stateSyncInterval c.ProtocolConfiguration.MaxTraceableBlocks = maxTraceable + c.ProtocolConfiguration.KeepOnlyLatestState = true } bcSpout := newTestChainWithCustomCfg(t, spountCfg) initBasicChain(t, bcSpout) @@ -1865,14 +1866,15 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) { _, err := batch.Persist() require.NoError(t, err) - checkNewBlockchainErr := func(t *testing.T, cfg func(c *config.Config), store storage.Store, shouldFail bool) { + checkNewBlockchainErr := func(t *testing.T, cfg func(c *config.Config), store storage.Store, errText string) { unitTestNetCfg, err := config.Load("../../config", testchain.Network()) require.NoError(t, err) cfg(&unitTestNetCfg) log := zaptest.NewLogger(t) _, err = NewBlockchain(store, unitTestNetCfg.ProtocolConfiguration, log) - if shouldFail { + if len(errText) != 0 { require.Error(t, err) + require.True(t, strings.Contains(err.Error(), errText)) } else { require.NoError(t, err) } @@ -1888,22 +1890,22 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) { checkNewBlockchainErr(t, func(c *config.Config) { boltCfg(c) c.ProtocolConfiguration.RemoveUntraceableBlocks = false - }, bcSpout.dao.Store, true) + }, bcSpout.dao.Store, "state jump was not completed, but P2PStateExchangeExtensions are disabled or archival node capability is on") }) t.Run("invalid state jump stage format", func(t *testing.T) { bcSpout.dao.Store.Put(bPrefix, []byte{0x01, 0x02}) - checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, true) + checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state jump stage format") }) t.Run("missing state sync point", func(t *testing.T) { bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)}) - checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, true) + checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "failed to get state sync point from the storage") }) t.Run("invalid state sync point", func(t *testing.T) { bcSpout.dao.Store.Put(bPrefix, []byte{byte(stateJumpStarted)}) point := make([]byte, 4) binary.LittleEndian.PutUint32(point, uint32(len(bcSpout.headerHashes))) bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point) - checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, true) + checkNewBlockchainErr(t, boltCfg, bcSpout.dao.Store, "invalid state sync point") }) for _, stage := range []stateJumpStage{stateJumpStarted, newStorageItemsAdded, genesisStateRemoved, 0x03} { t.Run(fmt.Sprintf("state jump stage %d", stage), func(t *testing.T) { @@ -1911,8 +1913,11 @@ func TestBlockchain_InitWithIncompleteStateJump(t *testing.T) { point := make([]byte, 4) binary.LittleEndian.PutUint32(point, uint32(stateSyncPoint)) bcSpout.dao.Store.Put([]byte{byte(storage.SYSStateSyncPoint)}, point) - shouldFail := stage == 0x03 // unknown stage - checkNewBlockchainErr(t, spountCfg, bcSpout.dao.Store, shouldFail) + var errText string + if stage == 0x03 { + errText = "unknown state jump stage" + } + checkNewBlockchainErr(t, spountCfg, bcSpout.dao.Store, errText) }) } } From 032d9749c2c92e305d96c8ab726cbf493c916234 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 18:39:56 +0300 Subject: [PATCH 7/9] core: improve blockchain error messages --- pkg/core/blockchain.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 88d4d7c3a..36b299511 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -367,7 +367,7 @@ func (bc *Blockchain) init() error { currHeaderHeight, currHeaderHash, err := bc.dao.GetCurrentHeaderHeight() if err != nil { - return err + return fmt.Errorf("failed to retrieve current header info: %w", err) } if bc.storedHeaderCount == 0 && currHeaderHeight == 0 { bc.headerHashes = append(bc.headerHashes, currHeaderHash) @@ -425,7 +425,7 @@ func (bc *Blockchain) init() error { bHeight, err := bc.dao.GetCurrentBlockHeight() if err != nil { - return err + return fmt.Errorf("failed to retrieve current block height: %w", err) } bc.blockHeight = bHeight bc.persistedHeight = bHeight From 8e3f2417f4c7ed11aa60b57d88a658310c88ccf5 Mon Sep 17 00:00:00 2001 From: AnnaShaleva Date: Mon, 28 Feb 2022 13:25:08 +0300 Subject: [PATCH 8/9] core: add check for native activations history It should be consistent with storage states. --- pkg/core/blockchain.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 36b299511..e86b1fe0b 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -448,13 +448,16 @@ func (bc *Blockchain) init() error { // contract state from DAO via high-level bc API. for _, c := range bc.contracts.Contracts { md := c.Metadata() + storedCS := bc.GetContractState(md.Hash) history := md.UpdateHistory if len(history) == 0 || history[0] > bHeight { + if storedCS != nil { + return fmt.Errorf("native contract %s is already stored, but marked as inactive for height %d in config", md.Name, bHeight) + } continue } - storedCS := bc.GetContractState(md.Hash) if storedCS == nil { - return fmt.Errorf("native contract %s is not stored", md.Name) + return fmt.Errorf("native contract %s is not stored, but should be active at height %d according to config", md.Name, bHeight) } storedCSBytes, err := stackitem.SerializeConvertible(storedCS) if err != nil { From 49c995ec0600c825f9bd8ed2f3f07391507144d6 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 25 Feb 2022 18:43:05 +0300 Subject: [PATCH 9/9] core: add tests for blockchain initialisation Close #1577. --- pkg/core/blockchain_test.go | 226 ++++++++++++++++++++++++++++++++++++ pkg/core/helper_test.go | 44 ++++--- 2 files changed, 257 insertions(+), 13 deletions(-) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 7bc8a9560..fd4bfb7d0 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -17,10 +17,12 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/chaindump" + "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -2003,3 +2005,227 @@ func setSigner(tx *transaction.Transaction, h util.Uint160) { Scopes: transaction.Global, }} } + +func TestBlockchain_StartFromExistingDB(t *testing.T) { + ps, path := newLevelDBForTestingWithPath(t, "") + customConfig := func(c *config.Config) { + c.ProtocolConfiguration.StateRootInHeader = true // Need for P2PStateExchangeExtensions check. + } + bc := initTestChain(t, ps, customConfig) + go bc.Run() + initBasicChain(t, bc) + require.True(t, bc.BlockHeight() > 5, "ensure that basic chain is correctly initialised") + + // Information for further tests. + h := bc.BlockHeight() + cryptoLibHash, err := bc.GetNativeContractScriptHash(nativenames.CryptoLib) + require.NoError(t, err) + cryptoLibState := bc.GetContractState(cryptoLibHash) + require.NotNil(t, cryptoLibState) + var ( + managementID = -1 + managementContractPrefix = 8 + ) + + bc.Close() // Ensure persist is done and persistent store is properly closed. + + newPS := func(t *testing.T) storage.Store { + ps, _ = newLevelDBForTestingWithPath(t, path) + t.Cleanup(func() { require.NoError(t, ps.Close()) }) + return ps + } + t.Run("mismatch storage version", func(t *testing.T) { + ps = newPS(t) + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + d := dao.NewSimple(cache, bc.config.StateRootInHeader, bc.config.P2PStateExchangeExtensions) + d.PutVersion(dao.Version{ + Value: "0.0.0", + }) + _, err := d.Persist() // Persist to `cache` wrapper. + require.NoError(t, err) + _, err = initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "storage version mismatch")) + }) + t.Run("mismatch StateRootInHeader", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, func(c *config.Config) { + customConfig(c) + c.ProtocolConfiguration.StateRootInHeader = false + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "StateRootInHeader setting mismatch")) + }) + t.Run("mismatch P2PSigExtensions", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, func(c *config.Config) { + customConfig(c) + c.ProtocolConfiguration.P2PSigExtensions = false + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "P2PSigExtensions setting mismatch")) + }) + t.Run("mismatch P2PStateExchangeExtensions", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, func(c *config.Config) { + customConfig(c) + c.ProtocolConfiguration.StateRootInHeader = true + c.ProtocolConfiguration.P2PStateExchangeExtensions = true + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "P2PStateExchangeExtensions setting mismatch")) + }) + t.Run("mismatch KeepOnlyLatestState", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, func(c *config.Config) { + customConfig(c) + c.ProtocolConfiguration.KeepOnlyLatestState = true + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "KeepOnlyLatestState setting mismatch")) + }) + t.Run("corrupted headers", func(t *testing.T) { + ps = newPS(t) + + // Corrupt headers hashes batch. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + key := make([]byte, 5) + key[0] = byte(storage.IXHeaderHashList) + binary.BigEndian.PutUint32(key[1:], 1) + cache.Put(key, []byte{1, 2, 3}) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "failed to read batch of 2000")) + }) + t.Run("corrupted current header height", func(t *testing.T) { + ps = newPS(t) + + // Remove current header. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + cache.Delete([]byte{byte(storage.SYSCurrentHeader)}) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "failed to retrieve current header")) + }) + t.Run("missing last batch of 2000 headers and missing last header", func(t *testing.T) { + ps = newPS(t) + + // Remove latest headers hashes batch and current header. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + cache.Delete([]byte{byte(storage.IXHeaderHashList)}) + currHeaderInfo, err := cache.Get([]byte{byte(storage.SYSCurrentHeader)}) + require.NoError(t, err) + currHeaderHash, err := util.Uint256DecodeBytesLE(currHeaderInfo[:32]) + require.NoError(t, err) + cache.Delete(append([]byte{byte(storage.DataExecutable)}, currHeaderHash.BytesBE()...)) + + _, err = initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "could not get header")) + }) + t.Run("missing last block", func(t *testing.T) { + ps = newPS(t) + + // Remove current block from storage. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + cache.Delete([]byte{byte(storage.SYSCurrentBlock)}) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "failed to retrieve current block height")) + }) + t.Run("missing last stateroot", func(t *testing.T) { + ps = newPS(t) + + // Remove latest stateroot from storage. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + key := make([]byte, 5) + key[0] = byte(storage.DataMPTAux) + binary.BigEndian.PutUint32(key, h) + cache.Delete(key) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "can't init MPT at height")) + }) + t.Run("failed native Management initialisation", func(t *testing.T) { + ps = newPS(t) + + // Corrupt serialised CryptoLib state. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + key := make([]byte, 1+4+1+20) + key[0] = byte(storage.STStorage) + binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) + key[5] = byte(managementContractPrefix) + copy(key[6:], cryptoLibHash.BytesBE()) + cache.Put(key, []byte{1, 2, 3}) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "can't init cache for Management native contract")) + }) + t.Run("invalid native contract deactivation", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, func(c *config.Config) { + customConfig(c) + c.ProtocolConfiguration.NativeUpdateHistories = map[string][]uint32{ + nativenames.Policy: {0}, + nativenames.Neo: {0}, + nativenames.Gas: {0}, + nativenames.Designation: {0}, + nativenames.StdLib: {0}, + nativenames.Management: {0}, + nativenames.Oracle: {0}, + nativenames.Ledger: {0}, + nativenames.Notary: {0}, + nativenames.CryptoLib: {h + 10}, + } + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s is already stored, but marked as inactive for height %d in config", nativenames.CryptoLib, h))) + }) + t.Run("invalid native contract activation", func(t *testing.T) { + ps = newPS(t) + + // Remove CryptoLib from storage. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + key := make([]byte, 1+4+1+20) + key[0] = byte(storage.STStorage) + binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) + key[5] = byte(managementContractPrefix) + copy(key[6:], cryptoLibHash.BytesBE()) + cache.Delete(key) + + _, err := initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s is not stored, but should be active at height %d according to config", nativenames.CryptoLib, h))) + }) + t.Run("stored and autogenerated native contract's states mismatch", func(t *testing.T) { + ps = newPS(t) + + // Change stored CryptoLib state. + cache := storage.NewMemCachedStore(ps) // Extra wrapper to avoid good DB corruption. + key := make([]byte, 1+4+1+20) + key[0] = byte(storage.STStorage) + binary.LittleEndian.PutUint32(key[1:], uint32(managementID)) + key[5] = byte(managementContractPrefix) + copy(key[6:], cryptoLibHash.BytesBE()) + cs := *cryptoLibState + cs.ID = -123 + csBytes, err := stackitem.SerializeConvertible(&cs) + require.NoError(t, err) + cache.Put(key, csBytes) + + _, err = initTestChainNoCheck(t, cache, customConfig) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch (stored contract state differs from autogenerated one)", nativenames.CryptoLib))) + }) + + t.Run("good", func(t *testing.T) { + ps = newPS(t) + _, err := initTestChainNoCheck(t, ps, customConfig) + require.NoError(t, err) + }) +} diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 2b31e3391..927932bf3 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -68,24 +68,44 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c } func newLevelDBForTesting(t testing.TB) storage.Store { - ldbDir := t.TempDir() - dbOptions := storage.LevelDBOptions{ - DataDirectoryPath: ldbDir, - } - newLevelStore, err := storage.NewLevelDBStore(dbOptions) - require.Nil(t, err, "NewLevelDBStore error") + newLevelStore, _ := newLevelDBForTestingWithPath(t, "") return newLevelStore } +func newLevelDBForTestingWithPath(t testing.TB, dbPath string) (storage.Store, string) { + if dbPath == "" { + dbPath = t.TempDir() + } + dbOptions := storage.LevelDBOptions{ + DataDirectoryPath: dbPath, + } + newLevelStore, err := storage.NewLevelDBStore(dbOptions) + require.Nil(t, err, "NewLevelDBStore error") + return newLevelStore, dbPath +} + func newBoltStoreForTesting(t testing.TB) storage.Store { - d := t.TempDir() - testFileName := filepath.Join(d, "test_bolt_db") - boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: testFileName}) - require.NoError(t, err) + boltDBStore, _ := newBoltStoreForTestingWithPath(t, "") return boltDBStore } +func newBoltStoreForTestingWithPath(t testing.TB, dbPath string) (storage.Store, string) { + if dbPath == "" { + d := t.TempDir() + dbPath = filepath.Join(d, "test_bolt_db") + } + boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath}) + require.NoError(t, err) + return boltDBStore, dbPath +} + func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain { + chain, err := initTestChainNoCheck(t, st, f) + require.NoError(t, err) + return chain +} + +func initTestChainNoCheck(t testing.TB, st storage.Store, f func(*config.Config)) (*Blockchain, error) { unitTestNetCfg, err := config.Load("../../config", testchain.Network()) require.NoError(t, err) if f != nil { @@ -98,9 +118,7 @@ func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Bloc if _, ok := t.(*testing.B); ok { log = zap.NewNop() } - chain, err := NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, log) - require.NoError(t, err) - return chain + return NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, log) } func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block {