github.com/Finschia/finschia-sdk@v0.48.1/snapshots/helpers_test.go (about)

     1  package snapshots_test
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"compress/zlib"
     7  	"crypto/sha256"
     8  	"errors"
     9  	"io"
    10  	"os"
    11  	"testing"
    12  	"time"
    13  
    14  	protoio "github.com/gogo/protobuf/io"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	dbm "github.com/tendermint/tm-db"
    18  
    19  	"github.com/Finschia/finschia-sdk/snapshots"
    20  	snapshottypes "github.com/Finschia/finschia-sdk/snapshots/types"
    21  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    22  )
    23  
    24  func checksums(slice [][]byte) [][]byte {
    25  	hasher := sha256.New()
    26  	checksums := make([][]byte, len(slice))
    27  	for i, chunk := range slice {
    28  		hasher.Write(chunk)
    29  		checksums[i] = hasher.Sum(nil)
    30  		hasher.Reset()
    31  	}
    32  	return checksums
    33  }
    34  
    35  func hash(chunks [][]byte) []byte {
    36  	hasher := sha256.New()
    37  	for _, chunk := range chunks {
    38  		hasher.Write(chunk)
    39  	}
    40  	return hasher.Sum(nil)
    41  }
    42  
    43  func makeChunks(chunks [][]byte) <-chan io.ReadCloser {
    44  	ch := make(chan io.ReadCloser, len(chunks))
    45  	for _, chunk := range chunks {
    46  		ch <- io.NopCloser(bytes.NewReader(chunk))
    47  	}
    48  	close(ch)
    49  	return ch
    50  }
    51  
    52  func readChunks(chunks <-chan io.ReadCloser) [][]byte {
    53  	bodies := [][]byte{}
    54  	for chunk := range chunks {
    55  		body, err := io.ReadAll(chunk)
    56  		if err != nil {
    57  			panic(err)
    58  		}
    59  		bodies = append(bodies, body)
    60  	}
    61  	return bodies
    62  }
    63  
    64  // snapshotItems serialize a array of bytes as SnapshotItem_ExtensionPayload, and return the chunks.
    65  func snapshotItems(items [][]byte) [][]byte {
    66  	// copy the same parameters from the code
    67  	snapshotChunkSize := uint64(10e6)
    68  	snapshotBufferSize := int(snapshotChunkSize)
    69  
    70  	ch := make(chan io.ReadCloser)
    71  	go func() {
    72  		chunkWriter := snapshots.NewChunkWriter(ch, snapshotChunkSize)
    73  		bufWriter := bufio.NewWriterSize(chunkWriter, snapshotBufferSize)
    74  		zWriter, _ := zlib.NewWriterLevel(bufWriter, 7)
    75  		protoWriter := protoio.NewDelimitedWriter(zWriter)
    76  		for _, item := range items {
    77  			snapshottypes.WriteExtensionItem(protoWriter, item)
    78  		}
    79  		protoWriter.Close()
    80  		zWriter.Close()
    81  		bufWriter.Flush()
    82  		chunkWriter.Close()
    83  	}()
    84  
    85  	var chunks [][]byte
    86  	for chunkBody := range ch {
    87  		chunk, err := io.ReadAll(chunkBody)
    88  		if err != nil {
    89  			panic(err)
    90  		}
    91  		chunks = append(chunks, chunk)
    92  	}
    93  	return chunks
    94  }
    95  
    96  type mockSnapshotter struct {
    97  	items [][]byte
    98  }
    99  
   100  func (m *mockSnapshotter) Restore(
   101  	height uint64, format uint32, protoReader protoio.Reader,
   102  ) (snapshottypes.SnapshotItem, error) {
   103  	if format == 0 {
   104  		return snapshottypes.SnapshotItem{}, snapshottypes.ErrUnknownFormat
   105  	}
   106  	if m.items != nil {
   107  		return snapshottypes.SnapshotItem{}, errors.New("already has contents")
   108  	}
   109  
   110  	m.items = [][]byte{}
   111  	for {
   112  		item := &snapshottypes.SnapshotItem{}
   113  		err := protoReader.ReadMsg(item)
   114  		if err == io.EOF {
   115  			break
   116  		} else if err != nil {
   117  			return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message")
   118  		}
   119  		payload := item.GetExtensionPayload()
   120  		if payload == nil {
   121  			return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message")
   122  		}
   123  		m.items = append(m.items, payload.Payload)
   124  	}
   125  
   126  	return snapshottypes.SnapshotItem{}, nil
   127  }
   128  
   129  func (m *mockSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error {
   130  	for _, item := range m.items {
   131  		if err := snapshottypes.WriteExtensionItem(protoWriter, item); err != nil {
   132  			return err
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  func (m *mockSnapshotter) SnapshotFormat() uint32 {
   139  	return 1
   140  }
   141  
   142  func (m *mockSnapshotter) SupportedFormats() []uint32 {
   143  	return []uint32{1}
   144  }
   145  
   146  // setupBusyManager creates a manager with an empty store that is busy creating a snapshot at height 1.
   147  // The snapshot will complete when the returned closer is called.
   148  func setupBusyManager(t *testing.T) *snapshots.Manager {
   149  	// os.MkdirTemp() is used instead of testing.T.TempDir()
   150  	// see https://github.com/cosmos/cosmos-sdk/pull/8475 for
   151  	// this change's rationale.
   152  	tempdir, err := os.MkdirTemp("", "")
   153  	require.NoError(t, err)
   154  	t.Cleanup(func() { _ = os.RemoveAll(tempdir) })
   155  
   156  	store, err := snapshots.NewStore(dbm.NewMemDB(), tempdir)
   157  	require.NoError(t, err)
   158  	hung := newHungSnapshotter()
   159  	mgr := snapshots.NewManager(store, hung)
   160  
   161  	go func() {
   162  		_, err := mgr.Create(1)
   163  		require.NoError(t, err)
   164  	}()
   165  	time.Sleep(10 * time.Millisecond)
   166  	t.Cleanup(hung.Close)
   167  
   168  	return mgr
   169  }
   170  
   171  // hungSnapshotter can be used to test operations in progress. Call close to end the snapshot.
   172  type hungSnapshotter struct {
   173  	ch chan struct{}
   174  }
   175  
   176  func newHungSnapshotter() *hungSnapshotter {
   177  	return &hungSnapshotter{
   178  		ch: make(chan struct{}),
   179  	}
   180  }
   181  
   182  func (m *hungSnapshotter) Close() {
   183  	close(m.ch)
   184  }
   185  
   186  func (m *hungSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error {
   187  	<-m.ch
   188  	return nil
   189  }
   190  
   191  func (m *hungSnapshotter) Restore(
   192  	height uint64, format uint32, protoReader protoio.Reader,
   193  ) (snapshottypes.SnapshotItem, error) {
   194  	panic("not implemented")
   195  }