github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/statesync/module_test.go (about)

     1  package statesync
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/core/dao"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/mpt"
    10  	"github.com/nspcc-dev/neo-go/pkg/core/storage"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/stretchr/testify/require"
    13  	"go.uber.org/zap/zaptest"
    14  )
    15  
    16  func TestModule_PR2019_discussion_r689629704(t *testing.T) {
    17  	expectedStorage := storage.NewMemCachedStore(storage.NewMemoryStore())
    18  	tr := mpt.NewTrie(nil, mpt.ModeLatest, expectedStorage)
    19  	require.NoError(t, tr.Put([]byte{0x03}, []byte("leaf1")))
    20  	require.NoError(t, tr.Put([]byte{0x01, 0xab, 0x02}, []byte("leaf2")))
    21  	require.NoError(t, tr.Put([]byte{0x01, 0xab, 0x04}, []byte("leaf3")))
    22  	require.NoError(t, tr.Put([]byte{0x06, 0x01, 0xde, 0x02}, []byte("leaf2"))) // <-- the same `leaf2` and `leaf3` values are put in the storage,
    23  	require.NoError(t, tr.Put([]byte{0x06, 0x01, 0xde, 0x04}, []byte("leaf3"))) // <-- but the path should differ.
    24  	require.NoError(t, tr.Put([]byte{0x06, 0x03}, []byte("leaf4")))
    25  
    26  	sr := tr.StateRoot()
    27  	tr.Flush(0)
    28  
    29  	// Keep MPT nodes in a map in order not to repeat them. We'll use `nodes` map to ask
    30  	// state sync module to restore the nodes.
    31  	var (
    32  		nodes         = make(map[util.Uint256][]byte)
    33  		expectedItems []storage.KeyValue
    34  	)
    35  	expectedStorage.Seek(storage.SeekRange{Prefix: []byte{byte(storage.DataMPT)}}, func(k, v []byte) bool {
    36  		key := bytes.Clone(k)
    37  		value := bytes.Clone(v)
    38  		expectedItems = append(expectedItems, storage.KeyValue{
    39  			Key:   key,
    40  			Value: value,
    41  		})
    42  		hash, err := util.Uint256DecodeBytesBE(key[1:])
    43  		require.NoError(t, err)
    44  		nodeBytes := value[:len(value)-4]
    45  		nodes[hash] = nodeBytes
    46  		return true
    47  	})
    48  
    49  	actualStorage := storage.NewMemCachedStore(storage.NewMemoryStore())
    50  	// These actions are done in module.Init(), but it's not the point of the test.
    51  	// Here we want to test only MPT restoring process.
    52  	stateSync := &Module{
    53  		log:          zaptest.NewLogger(t),
    54  		syncPoint:    1000500,
    55  		syncStage:    headersSynced,
    56  		syncInterval: 100500,
    57  		dao:          dao.NewSimple(actualStorage, true),
    58  		mptpool:      NewPool(),
    59  	}
    60  	stateSync.billet = mpt.NewBillet(sr, mpt.ModeLatest,
    61  		TemporaryPrefix(stateSync.dao.Version.StoragePrefix), actualStorage)
    62  	stateSync.mptpool.Add(sr, []byte{})
    63  
    64  	// The test itself: we'll ask state sync module to restore each node exactly once.
    65  	// After that storage content (including storage items and refcounts) must
    66  	// match exactly the one got from real MPT trie. MPT pool must be empty.
    67  	// State sync module must have mptSynced state in the end.
    68  	// MPT Billet root must become a collapsed hashnode (it was checked manually).
    69  	requested := make(map[util.Uint256]struct{})
    70  	for {
    71  		unknownHashes := stateSync.GetUnknownMPTNodesBatch(1) // restore nodes one-by-one
    72  		if len(unknownHashes) == 0 {
    73  			break
    74  		}
    75  		h := unknownHashes[0]
    76  		node, ok := nodes[h]
    77  		if !ok {
    78  			if _, ok = requested[h]; ok {
    79  				t.Fatal("node was requested twice")
    80  			}
    81  			t.Fatal("unknown node was requested")
    82  		}
    83  		require.NotPanics(t, func() {
    84  			err := stateSync.AddMPTNodes([][]byte{node})
    85  			require.NoError(t, err)
    86  		}, fmt.Errorf("hash=%s, value=%s", h.StringBE(), string(node)))
    87  		requested[h] = struct{}{}
    88  		delete(nodes, h)
    89  		if len(nodes) == 0 {
    90  			break
    91  		}
    92  	}
    93  	require.Equal(t, headersSynced|mptSynced, stateSync.syncStage, "all nodes were sent exactly ones, but MPT wasn't restored")
    94  	require.Equal(t, 0, len(nodes), "not all nodes were requested by state sync module")
    95  	require.Equal(t, 0, stateSync.mptpool.Count(), "MPT was restored, but MPT pool still contains items")
    96  
    97  	// Compare resulting storage items and refcounts.
    98  	var actualItems []storage.KeyValue
    99  	expectedStorage.Seek(storage.SeekRange{Prefix: []byte{byte(storage.DataMPT)}}, func(k, v []byte) bool {
   100  		key := bytes.Clone(k)
   101  		value := bytes.Clone(v)
   102  		actualItems = append(actualItems, storage.KeyValue{
   103  			Key:   key,
   104  			Value: value,
   105  		})
   106  		return true
   107  	})
   108  	require.ElementsMatch(t, expectedItems, actualItems)
   109  }