github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/ledgermgmt/ledger_mgmt_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledgermgmt
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/hechain20/hechain/bccsp/sw"
    18  	"github.com/hechain20/hechain/common/configtx/test"
    19  	"github.com/hechain20/hechain/common/ledger/testutil"
    20  	"github.com/hechain20/hechain/common/metrics/disabled"
    21  	"github.com/hechain20/hechain/core/ledger"
    22  	"github.com/hechain20/hechain/core/ledger/cceventmgmt"
    23  	"github.com/hechain20/hechain/core/ledger/kvledger"
    24  	"github.com/hechain20/hechain/core/ledger/mock"
    25  	"github.com/hechain20/hechain/protoutil"
    26  	"github.com/hyperledger/fabric-protos-go/common"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestLedgerMgmt(t *testing.T) {
    31  	initializer, ledgerMgr, cleanup := setup(t, "ledgermgmt")
    32  	defer cleanup()
    33  
    34  	numLedgers := 10
    35  	ledgers := make([]ledger.PeerLedger, numLedgers)
    36  	for i := 0; i < numLedgers; i++ {
    37  		cid := constructTestLedgerID(i)
    38  		gb, _ := test.MakeGenesisBlock(cid)
    39  		l, err := ledgerMgr.CreateLedger(cid, gb)
    40  		require.NoError(t, err)
    41  		ledgers[i] = l
    42  	}
    43  
    44  	ids, _ := ledgerMgr.GetLedgerIDs()
    45  	require.Len(t, ids, numLedgers)
    46  	for i := 0; i < numLedgers; i++ {
    47  		require.Equal(t, constructTestLedgerID(i), ids[i])
    48  	}
    49  
    50  	ledgerID := constructTestLedgerID(2)
    51  	t.Logf("Ledger selected for test = %s", ledgerID)
    52  	_, err := ledgerMgr.OpenLedger(ledgerID)
    53  	require.Equal(t, ErrLedgerAlreadyOpened, err)
    54  
    55  	l := ledgers[2]
    56  	l.Close()
    57  	// attempt to close the same ledger twice and ensure it doesn't panic
    58  	require.NotPanics(t, l.Close)
    59  
    60  	_, err = ledgerMgr.OpenLedger(ledgerID)
    61  	require.NoError(t, err)
    62  
    63  	_, err = ledgerMgr.OpenLedger(ledgerID)
    64  	require.Equal(t, ErrLedgerAlreadyOpened, err)
    65  	// close all opened ledgers and ledger mgmt
    66  	ledgerMgr.Close()
    67  
    68  	// Recreate LedgerMgr with existing ledgers
    69  	ledgerMgr = NewLedgerMgr(initializer)
    70  	_, err = ledgerMgr.OpenLedger(ledgerID)
    71  	require.NoError(t, err)
    72  	ledgerMgr.Close()
    73  }
    74  
    75  // TestCreateLedgerFromSnapshot first creates a ledger using a genesis block and generates a snapshot.
    76  // After it, it tests creating ledger from the snapshot.
    77  func TestCreateLedgerFromSnapshot(t *testing.T) {
    78  	initializer, lgrMgr, cleanup := setup(t, "createledgerfromsnapshot")
    79  	defer cleanup()
    80  
    81  	channelID := "testcreatefromsnapshot"
    82  	snapshotDir, gb := generateSnapshot(t, lgrMgr, initializer, channelID)
    83  
    84  	t.Run("create_ledger_from_snapshot_internal", func(t *testing.T) {
    85  		_, ledgerMgr, cleanup := setup(t, "createledgerfromsnapshot_internal")
    86  		defer cleanup()
    87  
    88  		l, _, err := ledgerMgr.createFromSnapshot(snapshotDir)
    89  		require.NoError(t, err)
    90  		bcInfo, _ := l.GetBlockchainInfo()
    91  		require.Equal(t, &common.BlockchainInfo{
    92  			Height:            1,
    93  			CurrentBlockHash:  protoutil.BlockHeaderHash(gb.Header),
    94  			PreviousBlockHash: nil,
    95  			BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
    96  				LastBlockInSnapshot: 0,
    97  			},
    98  		}, bcInfo)
    99  	})
   100  
   101  	t.Run("create_ledger_from_snapshot_async", func(t *testing.T) {
   102  		_, ledgerMgr, cleanup := setup(t, "createledgerfromsnapshot_async")
   103  		defer cleanup()
   104  
   105  		callbackCounter := 0
   106  		callback := func(l ledger.PeerLedger, cid string) { callbackCounter++ }
   107  
   108  		require.NoError(t, ledgerMgr.CreateLedgerFromSnapshot(snapshotDir, callback))
   109  
   110  		ledgerCreated := func() bool {
   111  			status := ledgerMgr.JoinBySnapshotStatus()
   112  			return !status.InProgress && status.BootstrappingSnapshotDir == ""
   113  		}
   114  
   115  		require.Eventually(t, ledgerCreated, time.Minute, time.Second)
   116  		require.Equal(t, 1, callbackCounter)
   117  
   118  		ledgerids, err := ledgerMgr.GetLedgerIDs()
   119  		require.NoError(t, err)
   120  		require.Equal(t, ledgerids, []string{channelID})
   121  	})
   122  
   123  	t.Run("create_existing_ledger_returns_error", func(t *testing.T) {
   124  		// create the ledger from snapshot under the same rootdir should return error because the ledger already exists
   125  		_, _, err := lgrMgr.createFromSnapshot(snapshotDir)
   126  		require.EqualError(t, err, "error while creating ledger id: ledger [testcreatefromsnapshot] already exists with state [ACTIVE]")
   127  	})
   128  
   129  	t.Run("create_ledger_from_nonexist_or_empty_dir_returns_error", func(t *testing.T) {
   130  		testDir, err := ioutil.TempDir("", "invalidsnapshotdir")
   131  		require.NoError(t, err)
   132  		defer os.RemoveAll(testDir)
   133  
   134  		nonExistDir := filepath.Join(testDir, "nonexistdir")
   135  		require.EqualError(t, lgrMgr.CreateLedgerFromSnapshot(nonExistDir, nil),
   136  			fmt.Sprintf("error opening dir [%s]: open %s: no such file or directory", nonExistDir, nonExistDir))
   137  
   138  		require.EqualError(t, lgrMgr.CreateLedgerFromSnapshot(testDir, nil),
   139  			fmt.Sprintf("snapshot dir %s is empty", testDir))
   140  	})
   141  
   142  	t.Run("callback_func_is_not_called_if_create_ledger_from_snapshot_failed", func(t *testing.T) {
   143  		initializer, ledgerMgr, cleanup := setup(t, "callbackfuncisnotcalled")
   144  		defer cleanup()
   145  
   146  		// copy snapshotDir to a new dir and remove a metadata file so that kvledger.CreateFromSnapshot will fail
   147  		require.NoError(t, os.MkdirAll(initializer.Config.SnapshotsConfig.RootDir, 0o755))
   148  		require.NoError(t, testutil.CopyDir(snapshotDir, initializer.Config.SnapshotsConfig.RootDir, false))
   149  		newSnapshotDir := filepath.Join(initializer.Config.SnapshotsConfig.RootDir, "0")
   150  		require.NoError(t, os.Remove(filepath.Join(newSnapshotDir, "_snapshot_signable_metadata.json")))
   151  
   152  		callbackCounter := 0
   153  		callback := func(l ledger.PeerLedger, cid string) { callbackCounter++ }
   154  
   155  		require.NoError(t, ledgerMgr.CreateLedgerFromSnapshot(newSnapshotDir, callback))
   156  
   157  		// wait until CreateFromSnapshot is done
   158  		ledgerCreated := func() bool {
   159  			status := ledgerMgr.JoinBySnapshotStatus()
   160  			return !status.InProgress && status.BootstrappingSnapshotDir == ""
   161  		}
   162  		require.Eventually(t, ledgerCreated, time.Minute, time.Second)
   163  
   164  		// callback should not be called and ledger should not be generated
   165  		require.Equal(t, 0, callbackCounter)
   166  
   167  		ids, err := ledgerMgr.GetLedgerIDs()
   168  		require.NoError(t, err)
   169  		require.Equal(t, 0, len(ids))
   170  	})
   171  }
   172  
   173  func TestConcurrentCreateLedgerFromGB(t *testing.T) {
   174  	_, ledgerMgr, cleanup := setup(t, "concurrentcreateledgerfromgb")
   175  	defer cleanup()
   176  
   177  	var err error
   178  	gbs := make([]*common.Block, 0, 5)
   179  	for i := 0; i < len(gbs); i++ {
   180  		gbs[i], err = test.MakeGenesisBlock(fmt.Sprintf("l%d", i))
   181  		require.NoError(t, err)
   182  	}
   183  
   184  	// verify CreateLedger (from genesisblock) can be called concurrently
   185  	for i := 0; i < len(gbs); i++ {
   186  		gb := gbs[i]
   187  		ledgerID := fmt.Sprintf("l%d", i)
   188  		go func() {
   189  			_, err := ledgerMgr.CreateLedger(ledgerID, gb)
   190  			require.NoError(t, err)
   191  		}()
   192  	}
   193  
   194  	ledgersGenerated := func() bool {
   195  		ledgerIds, err := ledgerMgr.GetLedgerIDs()
   196  		require.NoError(t, err)
   197  		return len(ledgerIds) == len(gbs)
   198  	}
   199  	require.Eventually(t, ledgersGenerated, time.Minute, time.Second)
   200  }
   201  
   202  func TestConcurrentCreateLedgerFromSnapshot(t *testing.T) {
   203  	initializer, ledgerMgr, cleanup := setup(t, "concurrentcreateledgerfromsnapshot")
   204  	defer cleanup()
   205  
   206  	// generate 2 snapshots for 2 channels
   207  	channelID1 := "testcreatefromsnapshot1"
   208  	snapshotDir1, _ := generateSnapshot(t, ledgerMgr, initializer, channelID1)
   209  	channelID2 := "testcreatefromsnapshot2"
   210  	snapshotDir2, _ := generateSnapshot(t, ledgerMgr, initializer, channelID2)
   211  
   212  	ledgerMgr.Close()
   213  
   214  	// create a new ledger mgr to import snapshot
   215  	_, ledgerMgr2, cleanup2 := setup(t, "concurrentcreateledgerfromsnapshot2")
   216  	defer cleanup2()
   217  
   218  	// use a channel to keep the callback func waiting so that we can test concurrent CreateLedger/CreateLedgerBySnapshot calls
   219  	waitCh := make(chan struct{})
   220  	callback := func(l ledger.PeerLedger, cid string) {
   221  		<-waitCh
   222  	}
   223  	require.NoError(t, ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir1, callback))
   224  
   225  	// concurrent CreateLedger call should fail
   226  	channelID3 := "ledgerfromgb"
   227  	gb, err := test.MakeGenesisBlock(channelID3)
   228  	require.NoError(t, err)
   229  	_, err = ledgerMgr2.CreateLedger(channelID3, gb)
   230  	require.EqualError(t, err, fmt.Sprintf("a ledger is being created from a snapshot at %s. Call ledger creation again after it is done.", snapshotDir1))
   231  
   232  	// concurrent CreateLedgerBySnapshot call should fail
   233  	err = ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir2, callback)
   234  	require.EqualError(t, err, fmt.Sprintf("a ledger is being created from a snapshot at %s. Call ledger creation again after it is done.", snapshotDir1))
   235  
   236  	waitCh <- struct{}{}
   237  	ledgerCreated := func() bool {
   238  		status := ledgerMgr.JoinBySnapshotStatus()
   239  		return !status.InProgress && status.BootstrappingSnapshotDir == ""
   240  	}
   241  	require.Eventually(t, ledgerCreated, time.Minute, time.Second)
   242  
   243  	ledgerIDs, err := ledgerMgr2.GetLedgerIDs()
   244  	require.NoError(t, err)
   245  	require.Equal(t, ledgerIDs, []string{channelID1})
   246  
   247  	// CreateLedger should work after the previous CreateLedgerFromSnapshot is done
   248  	_, err = ledgerMgr2.CreateLedger(channelID3, gb)
   249  	require.NoError(t, err, "creating ledger for %s should have succeeded", channelID3)
   250  
   251  	// CreateLedgerFromSnapshot should work after the previous CreateLedgerFromSnapshot is done
   252  	callback = func(l ledger.PeerLedger, cid string) {}
   253  	require.NoError(t, ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir2, callback))
   254  
   255  	// wait until ledger is created from snapshotDir2
   256  	ledgerCreated = func() bool {
   257  		status := ledgerMgr2.JoinBySnapshotStatus()
   258  		return !status.InProgress && status.BootstrappingSnapshotDir == ""
   259  	}
   260  	require.Eventually(t, ledgerCreated, time.Minute, time.Second)
   261  
   262  	ledgerIDs, err = ledgerMgr2.GetLedgerIDs()
   263  	require.NoError(t, err)
   264  	require.ElementsMatch(t, ledgerIDs, []string{channelID1, channelID2, channelID3})
   265  }
   266  
   267  func TestChaincodeInfoProvider(t *testing.T) {
   268  	_, ledgerMgr, cleanup := setup(t, "chaincodeinfoprovider")
   269  	defer cleanup()
   270  
   271  	gb, _ := test.MakeGenesisBlock("ledger1")
   272  	_, err := ledgerMgr.CreateLedger("ledger1", gb)
   273  	require.NoError(t, err)
   274  
   275  	mockDeployedCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
   276  	mockDeployedCCInfoProvider.ChaincodeInfoStub = func(channelName, ccName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error) {
   277  		return constructTestCCInfo(ccName, ccName, ccName), nil
   278  	}
   279  
   280  	ccInfoProvider := &chaincodeInfoProviderImpl{
   281  		ledgerMgr,
   282  		mockDeployedCCInfoProvider,
   283  	}
   284  	_, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger2", constructTestCCDef("cc2", "1.0", "cc2Hash"))
   285  	t.Logf("Expected error received = %s", err)
   286  	require.Error(t, err)
   287  
   288  	ccInfo, err := ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "non-matching-version", "cc1"))
   289  	require.NoError(t, err)
   290  	require.Nil(t, ccInfo)
   291  
   292  	ccInfo, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "cc1", "non-matching-hash"))
   293  	require.NoError(t, err)
   294  	require.Nil(t, ccInfo)
   295  
   296  	ccInfo, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "cc1", "cc1"))
   297  	require.NoError(t, err)
   298  	require.Equal(t, constructTestCCInfo("cc1", "cc1", "cc1"), ccInfo)
   299  }
   300  
   301  func setup(t *testing.T, basename string) (*Initializer, *LedgerMgr, func()) {
   302  	testDir, err := ioutil.TempDir("", basename)
   303  	require.NoError(t, err)
   304  	initializer, err := constructDefaultInitializer(testDir)
   305  	require.NoError(t, err)
   306  	ledgerMgr := NewLedgerMgr(initializer)
   307  	cleanup := func() {
   308  		ledgerMgr.Close()
   309  		os.Remove(testDir)
   310  	}
   311  	return initializer, ledgerMgr, cleanup
   312  }
   313  
   314  func constructDefaultInitializer(testDir string) (*Initializer, error) {
   315  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  	return &Initializer{
   320  		Config: &ledger.Config{
   321  			RootFSPath:    testDir,
   322  			StateDBConfig: &ledger.StateDBConfig{},
   323  			PrivateDataConfig: &ledger.PrivateDataConfig{
   324  				MaxBatchSize:    5000,
   325  				BatchesInterval: 1000,
   326  				PurgeInterval:   100,
   327  			},
   328  			HistoryDBConfig: &ledger.HistoryDBConfig{
   329  				Enabled: true,
   330  			},
   331  			SnapshotsConfig: &ledger.SnapshotsConfig{
   332  				RootDir: filepath.Join(testDir, "snapshots"),
   333  			},
   334  		},
   335  
   336  		MetricsProvider:               &disabled.Provider{},
   337  		DeployedChaincodeInfoProvider: &mock.DeployedChaincodeInfoProvider{},
   338  		HashProvider:                  cryptoProvider,
   339  	}, nil
   340  }
   341  
   342  func constructTestLedgerID(i int) string {
   343  	return fmt.Sprintf("ledger_%06d", i)
   344  }
   345  
   346  func constructTestCCInfo(ccName, version, hash string) *ledger.DeployedChaincodeInfo {
   347  	return &ledger.DeployedChaincodeInfo{
   348  		Name:    ccName,
   349  		Hash:    []byte(hash),
   350  		Version: version,
   351  	}
   352  }
   353  
   354  func constructTestCCDef(ccName, version, hash string) *cceventmgmt.ChaincodeDefinition {
   355  	return &cceventmgmt.ChaincodeDefinition{
   356  		Name:    ccName,
   357  		Hash:    []byte(hash),
   358  		Version: version,
   359  	}
   360  }
   361  
   362  // generateSnapshot creates a ledger with genesis block and generates a snapshot when the ledger only has genesisblock
   363  func generateSnapshot(t *testing.T, ledgerMgr *LedgerMgr, initializer *Initializer, channelID string) (string, *common.Block) {
   364  	_, gb := testutil.NewBlockGenerator(t, channelID, false)
   365  	l, err := ledgerMgr.CreateLedger(channelID, gb)
   366  	require.NoError(t, err)
   367  
   368  	require.NoError(t, l.SubmitSnapshotRequest(0))
   369  
   370  	snapshotDir := kvledger.SnapshotDirForLedgerBlockNum(initializer.Config.SnapshotsConfig.RootDir, channelID, 0)
   371  	snapshotGenerated := func() bool {
   372  		pendingRequests, err := l.PendingSnapshotRequests()
   373  		require.NoError(t, err)
   374  		return len(pendingRequests) == 0
   375  	}
   376  	require.Eventually(t, snapshotGenerated, 30*time.Second, 100*time.Millisecond)
   377  
   378  	return snapshotDir, gb
   379  }