github.com/ewagmig/fabric@v2.1.1+incompatible/core/ledger/kvledger/tests/v1x_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package tests
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"testing"
    15  	"time"
    16  
    17  	protopeer "github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/common/ledger/testutil"
    19  	"github.com/hyperledger/fabric/core/ledger/kvledger"
    20  	"github.com/hyperledger/fabric/core/ledger/mock"
    21  	"github.com/hyperledger/fabric/core/ledger/util/couchdb"
    22  	"github.com/hyperledger/fabric/core/ledger/util/couchdbtest"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  // TestV11 tests that a ledgersData folder created by v1.1 can be used with future releases in a backward compatible way
    27  // The test data was generated by v1.1 code https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/v11_generate_test.go#L22
    28  func TestV11(t *testing.T) {
    29  	env := newEnv(t)
    30  	defer env.cleanup()
    31  
    32  	ledgerFSRoot := env.initializer.Config.RootFSPath
    33  	// pass false so that 'ledgersData' directory will not be created when unzipped to ledgerFSRoot
    34  	require.NoError(t, testutil.Unzip("testdata/v11/sample_ledgers/ledgersData.zip", ledgerFSRoot, false))
    35  
    36  	// verify init ledger panic and drop the corresponding db after each panic
    37  	checkInitLedgerPanicAndDropDBs(t, env, ledgerFSRoot, nil)
    38  
    39  	t.Logf("All stores are reformatted/dropped, now ledgermgmt initialization should not panic")
    40  	env.initLedgerMgmt()
    41  
    42  	h1, h2 := env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
    43  	dataHelper := &v1xSampleDataHelper{sampleDataVersion: "v1.1", t: t}
    44  
    45  	dataHelper.verify(h1)
    46  	dataHelper.verify(h2)
    47  
    48  	env.closeAllLedgersAndDrop(rebuildableStatedb + rebuildableBlockIndex + rebuildableConfigHistory + rebuildableHistoryDB)
    49  	h1, h2 = env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
    50  	dataHelper.verify(h1)
    51  	dataHelper.verify(h2)
    52  }
    53  
    54  // TestV13WithStateCouchdb tests that a ledgersData folder and couchdb data created by v1.3 can be read by latest fabric version in a backward compatible way
    55  // The test data was generated by v1.3 code https://gerrit.hyperledger.org/r/#/c/fabric/+/34078/3/core/ledger/kvledger/tests/v13_generate_test.go@60
    56  func TestV13WithStateCouchdb(t *testing.T) {
    57  	env := newEnv(t)
    58  	defer env.cleanup()
    59  
    60  	ledgerFSRoot := env.initializer.Config.RootFSPath
    61  	// pass false so that 'ledgersData' directory will not be created when unzipped to ledgerFSRoot
    62  	require.NoError(t, testutil.Unzip("testdata/v13_statecouchdb/sample_ledgers/ledgersData.zip", ledgerFSRoot, false))
    63  
    64  	// unzip couchdb data to prepare the mount dir
    65  	couchdbDataUnzipDir := filepath.Join(ledgerFSRoot, "couchdbData")
    66  	require.NoError(t, os.Mkdir(couchdbDataUnzipDir, os.ModePerm))
    67  	require.NoError(t, testutil.Unzip("testdata/v13_statecouchdb/sample_ledgers/couchdbData.zip", couchdbDataUnzipDir, false))
    68  
    69  	// prepare the local.d mount dir to overwrite the number of shards and nodes so that they match the couchdb data generated from v1.3
    70  	localdHostDir := filepath.Join(ledgerFSRoot, "local.d")
    71  	require.NoError(t, os.MkdirAll(localdHostDir, os.ModePerm))
    72  	testutil.CopyDir("testdata/v13_statecouchdb/couchdb_etc/local.d", localdHostDir, true)
    73  
    74  	// start couchdb using couchdbDataUnzipDir and localdHostDir as mount dirs
    75  	couchdbBinds := []string{
    76  		fmt.Sprintf("%s:%s", couchdbDataUnzipDir, "/opt/couchdb/data"),
    77  		fmt.Sprintf("%s:%s", localdHostDir, "/opt/couchdb/etc/local.d"),
    78  	}
    79  	couchAddress, cleanup := couchdbtest.CouchDBSetup(couchdbBinds)
    80  	defer cleanup()
    81  
    82  	// set required config data to use state couchdb
    83  	couchdbConfig := &couchdb.Config{
    84  		Address:             couchAddress,
    85  		Username:            "",
    86  		Password:            "",
    87  		MaxRetries:          3,
    88  		MaxRetriesOnStartup: 3,
    89  		RequestTimeout:      10 * time.Second,
    90  		RedoLogPath:         filepath.Join(ledgerFSRoot, "couchdbRedoLogs"),
    91  	}
    92  	env.initializer.Config.StateDBConfig.StateDatabase = "CouchDB"
    93  	env.initializer.Config.StateDBConfig.CouchDB = couchdbConfig
    94  	env.initializer.HealthCheckRegistry = &mock.HealthCheckRegistry{}
    95  	env.initializer.ChaincodeLifecycleEventProvider = &mock.ChaincodeLifecycleEventProvider{}
    96  
    97  	// verify init ledger panic and drop the corresponding db after each panic
    98  	checkInitLedgerPanicAndDropDBs(t, env, ledgerFSRoot, couchdbConfig)
    99  
   100  	t.Logf("All stores are reformatted/dropped, now ledgermgmt initialization should not panic")
   101  	env.initLedgerMgmt()
   102  
   103  	h1, h2 := env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
   104  	dataHelper := &v1xSampleDataHelper{sampleDataVersion: "v1.3", t: t}
   105  
   106  	dataHelper.verify(h1)
   107  	dataHelper.verify(h2)
   108  
   109  	// drop couchDB and other rebuildable dbs to test db rebuild
   110  	dropCouchDBs(t, couchdbConfig)
   111  	env.closeAllLedgersAndDrop(rebuildableBlockIndex + rebuildableConfigHistory + rebuildableHistoryDB)
   112  	h1, h2 = env.newTestHelperOpenLgr("ledger1", t), env.newTestHelperOpenLgr("ledger2", t)
   113  	dataHelper.verify(h1)
   114  	dataHelper.verify(h2)
   115  }
   116  
   117  func checkInitLedgerPanicAndDropDBs(t *testing.T, env *env, ledgerFSRoot string, couchdbConfig *couchdb.Config) {
   118  	t.Logf("verifying that a panic occurs because idStore has old format and then reformat the idstore to proceed")
   119  	idStorePath := kvledger.LedgerProviderPath(ledgerFSRoot)
   120  	require.PanicsWithValue(
   121  		t,
   122  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb for channel-IDs at [%s]], data format = [], expected format = [2.0]",
   123  			idStorePath),
   124  		func() { env.initLedgerMgmt() },
   125  		"A panic should occur because idstore is in format 1.x",
   126  	)
   127  	require.NoError(t, kvledger.UpgradeIDStoreFormat(ledgerFSRoot))
   128  
   129  	t.Logf("verifying that a panic occurs because blockstore index has old format and then drop the idstore to proceed")
   130  	blkIndexPath := path.Join(kvledger.BlockStorePath(ledgerFSRoot), "index")
   131  	require.PanicsWithValue(
   132  		t,
   133  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   134  			blkIndexPath),
   135  		func() { env.initLedgerMgmt() },
   136  		"A panic should occur because block store index is in format 1.x",
   137  	)
   138  	require.NoError(t, os.RemoveAll(blkIndexPath))
   139  
   140  	t.Logf("verifying that a panic occurs because historydb has old format and then drop the historydb to proceed")
   141  	historyDBPath := kvledger.HistoryDBPath(ledgerFSRoot)
   142  	require.PanicsWithValue(
   143  		t,
   144  		fmt.Sprintf("Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   145  			historyDBPath),
   146  		func() { env.initLedgerMgmt() },
   147  		"A panic should occur because history is in format 1.x",
   148  	)
   149  	require.NoError(t, os.RemoveAll(historyDBPath))
   150  
   151  	if couchdbConfig == nil {
   152  		t.Logf("verifying that a panic occurs because stateleveldb has old format and then drop the statedb to proceed")
   153  		stateLevelDBPath := kvledger.StateDBPath(ledgerFSRoot)
   154  		require.PanicsWithValue(
   155  			t,
   156  			fmt.Sprintf(
   157  				"Error in instantiating ledger provider: unexpected format. db info = [leveldb at [%s]], data format = [], expected format = [2.0]",
   158  				stateLevelDBPath,
   159  			),
   160  			func() { env.initLedgerMgmt() },
   161  			"A panic should occur because statedb is in format 1.x",
   162  		)
   163  		require.NoError(t, os.RemoveAll(stateLevelDBPath))
   164  	} else {
   165  		t.Logf("verifying that a panic occurs because statecouchdb has old format and then drop the statedb to proceed")
   166  		require.PanicsWithValue(
   167  			t,
   168  			fmt.Sprintf(
   169  				"Error in instantiating ledger provider: unexpected format. db info = [CouchDB for state database], data format = [], expected format = [2.0]"),
   170  			func() { env.initLedgerMgmt() },
   171  			"A panic should occur because statedb is in format 1.x",
   172  		)
   173  		dropCouchDBs(t, couchdbConfig)
   174  	}
   175  }
   176  
   177  // v1xSampleDataHelper provides a set of functions to verify the ledger (after upgraded to latest data format).
   178  // It verifies the ledger under the assumption that the ledger was generated by the specific generation code from v1.1 or v1.3.
   179  // For v1.1, the sample ledger data was generated by https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/v11_generate_test.go#L22
   180  // This generate function constructed two ledgers and populateed the ledgers using this code
   181  // (https://github.com/hyperledger/fabric/blob/release-1.1/core/ledger/kvledger/tests/sample_data_helper.go#L55)
   182  // For v1.3, the sample ledger data was generated by CR (https://gerrit.hyperledger.org/r/#/c/fabric/+/34078/3/core/ledger/kvledger/tests/v13_generate_test.go@60).
   183  // This generate function constructed two ledgers and populated the ledgers using this code
   184  // (https://github.com/hyperledger/fabric/blob/release-1.3/core/ledger/kvledger/tests/sample_data_helper.go#L55)
   185  type v1xSampleDataHelper struct {
   186  	sampleDataVersion string
   187  	t                 *testing.T
   188  }
   189  
   190  func (d *v1xSampleDataHelper) verify(h *testhelper) {
   191  	d.verifyState(h)
   192  	d.verifyBlockAndPvtdata(h)
   193  	d.verifyGetTransactionByID(h)
   194  	d.verifyConfigHistory(h)
   195  	d.verifyHistory(h)
   196  }
   197  
   198  func (d *v1xSampleDataHelper) verifyState(h *testhelper) {
   199  	lgrid := h.lgrid
   200  	h.verifyPubState("cc1", "key1", d.sampleVal("value13", lgrid))
   201  	h.verifyPubState("cc1", "key2", "")
   202  	h.verifyPvtState("cc1", "coll1", "key3", d.sampleVal("value14", lgrid))
   203  	h.verifyPvtState("cc1", "coll1", "key4", "")
   204  	h.verifyPvtState("cc1", "coll2", "key3", d.sampleVal("value09", lgrid))
   205  	h.verifyPvtState("cc1", "coll2", "key4", d.sampleVal("value10", lgrid))
   206  
   207  	h.verifyPubState("cc2", "key1", d.sampleVal("value03", lgrid))
   208  	h.verifyPubState("cc2", "key2", d.sampleVal("value04", lgrid))
   209  	h.verifyPvtState("cc2", "coll1", "key3", d.sampleVal("value07", lgrid))
   210  	h.verifyPvtState("cc2", "coll1", "key4", d.sampleVal("value08", lgrid))
   211  	h.verifyPvtState("cc2", "coll2", "key3", d.sampleVal("value11", lgrid))
   212  	h.verifyPvtState("cc2", "coll2", "key4", d.sampleVal("value12", lgrid))
   213  }
   214  
   215  func (d *v1xSampleDataHelper) verifyHistory(h *testhelper) {
   216  	lgrid := h.lgrid
   217  	expectedHistoryCC1Key1 := []string{
   218  		d.sampleVal("value13", lgrid),
   219  		d.sampleVal("value01", lgrid),
   220  	}
   221  	h.verifyHistory("cc1", "key1", expectedHistoryCC1Key1)
   222  }
   223  
   224  func (d *v1xSampleDataHelper) verifyConfigHistory(h *testhelper) {
   225  	lgrid := h.lgrid
   226  	h.verifyMostRecentCollectionConfigBelow(10, "cc1",
   227  		&expectedCollConfInfo{5, d.sampleCollConf2(lgrid, "cc1")})
   228  
   229  	h.verifyMostRecentCollectionConfigBelow(5, "cc1",
   230  		&expectedCollConfInfo{3, d.sampleCollConf1(lgrid, "cc1")})
   231  
   232  	h.verifyMostRecentCollectionConfigBelow(10, "cc2",
   233  		&expectedCollConfInfo{5, d.sampleCollConf2(lgrid, "cc2")})
   234  
   235  	h.verifyMostRecentCollectionConfigBelow(5, "cc2",
   236  		&expectedCollConfInfo{3, d.sampleCollConf1(lgrid, "cc2")})
   237  }
   238  
   239  func (d *v1xSampleDataHelper) verifyConfigHistoryDoesNotExist(h *testhelper) {
   240  	h.verifyMostRecentCollectionConfigBelow(10, "cc1", nil)
   241  	h.verifyMostRecentCollectionConfigBelow(10, "cc2", nil)
   242  }
   243  
   244  func (d *v1xSampleDataHelper) verifyBlockAndPvtdata(h *testhelper) {
   245  	lgrid := h.lgrid
   246  	h.verifyBlockAndPvtData(2, nil, func(r *retrievedBlockAndPvtdata) {
   247  		r.hasNumTx(2)
   248  		r.hasNoPvtdata()
   249  	})
   250  
   251  	h.verifyBlockAndPvtData(4, nil, func(r *retrievedBlockAndPvtdata) {
   252  		r.hasNumTx(2)
   253  		r.pvtdataShouldContain(0, "cc1", "coll1", "key3", d.sampleVal("value05", lgrid))
   254  		r.pvtdataShouldContain(1, "cc2", "coll1", "key3", d.sampleVal("value07", lgrid))
   255  	})
   256  }
   257  
   258  func (d *v1xSampleDataHelper) verifyGetTransactionByID(h *testhelper) {
   259  	h.verifyTxValidationCode("txid7", protopeer.TxValidationCode_VALID)
   260  	h.verifyTxValidationCode("txid8", protopeer.TxValidationCode_MVCC_READ_CONFLICT)
   261  }
   262  
   263  func (d *v1xSampleDataHelper) sampleVal(val, ledgerid string) string {
   264  	return fmt.Sprintf("%s:%s", val, ledgerid)
   265  }
   266  
   267  func (d *v1xSampleDataHelper) sampleCollConf1(ledgerid, ccName string) []*collConf {
   268  	switch d.sampleDataVersion {
   269  	case "v1.1":
   270  		return []*collConf{
   271  			{name: "coll1", members: []string{"org1", "org2"}},
   272  			{name: ledgerid, members: []string{"org1", "org2"}},
   273  			{name: ccName, members: []string{"org1", "org2"}},
   274  		}
   275  	case "v1.3":
   276  		return []*collConf{
   277  			{name: "coll1"},
   278  			{name: ledgerid},
   279  			{name: ccName},
   280  		}
   281  	default:
   282  		// should not happen
   283  		require.Failf(d.t, "sample data version %s is wrong", d.sampleDataVersion)
   284  		return nil
   285  	}
   286  }
   287  
   288  func (d *v1xSampleDataHelper) sampleCollConf2(ledgerid string, ccName string) []*collConf {
   289  	switch d.sampleDataVersion {
   290  	case "v1.1":
   291  		return []*collConf{
   292  			{name: "coll1", members: []string{"org1", "org2"}},
   293  			{name: "coll2", members: []string{"org1", "org2"}},
   294  			{name: ledgerid, members: []string{"org1", "org2"}},
   295  			{name: ccName, members: []string{"org1", "org2"}},
   296  		}
   297  	case "v1.3":
   298  		return []*collConf{
   299  			{name: "coll1"},
   300  			{name: "coll2"},
   301  			{name: ledgerid},
   302  			{name: ccName},
   303  		}
   304  	default:
   305  		// should not happen
   306  		require.Failf(d.t, "sample data version %s is wrong", d.sampleDataVersion)
   307  		return nil
   308  	}
   309  }